[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[offlist] Re: [sc-users] enttec dmx pro
On Wednesday 17 June 2009 16:31:18 you wrote:
> helo,
>
> has anyone ever used the enttec dmx pro interface from within sc? i am
> currently using wtf2osc and it works with some dmx clients but not with
> others. somehow the signal is strange. the "reset" period is too long.
I have these classes, which I made quite a while ago but never actually tested
with the device.
Let me know if that works for you :)
sincerely,
Marije
> thanks!
> wilm
>
> _______________________________________________
> sc-users mailing list
>
> info (subscription, etc.):
> http://www.beast.bham.ac.uk/research/sc_mailing_lists.shtml archive:
> https://listarc.bham.ac.uk/marchives/sc-users/
> search: https://listarc.bham.ac.uk/lists/sc-users/search/
(
// create a new instance of DMX
d = DMX.new;
// create a DMX device
e = EntTecDMXUSBPro.new( 0 );
// set the device for DMX
d.device = e;
// create an empty cue:
c = DMXCue.new;
// add the cue to DMX:
d.cues.add( c );
c.put( 5, 0.5 );
c.put( 20, 1 );
// set the current cue:
d.currentCue = d.cues.at( 0 );
a = DMXSubCue.new;
a.put( 3, 0.1 );
a.put( 15, 1 );
)
v = DMXGui( d );
a.data
d.currentCue.data
d.map
d.map.findKeyForValue( 3 )
d.map.at(\table)
d.map.put( \table, 2);
d.fade( DMXCue.new, 10.0 );
c = DMXCue.new
c.data
d.fade( c, 10.0 );
b = d.currentCue.deepCopy;
b.merge( a );
b.data
b.data * 0.8 + d.currentCue.data * 0.2
c.data
a.data
a.data.indices
GUI.scrollPane
Tdef( \dmxfade ).envir
Tdef( \dmxfade ).envir.fadeval
Tdef( \dmxfade ).set( \speed, 2 );
// class to create a GUI for DMX
DMXGui{
classvar <>xposScreen=0, <>yposScreen=20;
classvar <counter = 0;
var <>dmx;
var <sliderview, <cueview;
var <showconsole;
var <>w, <watcher;
*new { |dmx, w|
^super.new.w_(w).dmx_(dmx).init;
}
init {
var f, xsize, ysize;
var ncview, rmview, clview;
counter = counter + 1;
xsize = 300;
ysize = 105+130+10;
w = w ?? {
w = GUI.window.new("DMX Control", Rect(xposScreen, yposScreen, xsize + 10, ysize)).front;
//w.view.background_(Color.black);
w.view.decorator = FlowLayout(Rect(4, 4, w.bounds.width, w.bounds.height), 2@2, 2@2);
w;
};
// unclutter the windows on the screen:
yposScreen = yposScreen + 160;
if ( yposScreen > 700,
{ yposScreen = 20; xposScreen = xposScreen + xsize + 10;
if ( xposScreen > 900,
{ xposScreen = 0; });
});
showconsole = GUI.button.new( w, Rect( 0, 0, xsize-5, 20 ) ).states_( [ ["Show console"] ] ).action_( { DMXConsole.new( dmx ) } );
// GUI.staticText.new(w, Rect(0, 0, xsize - 2, 20)).string_("WiiMote" + wiimote.id + wiimote.address )
// .align_(0);
//.background_(labelColor);
cueview = GUI.compositeView.new( w, Rect( 5, 30, 205, 130 ));
// rm = WiiRemoteGUI.new( rmview, wiimote, 30 );
// sliderview = GUI.compositeView.new( w, Rect( 5, 160, 205, 105 ));
// nc = WiiNunchukGUI.new( ncview, wiimote, 160 );
watcher = SkipJack.new( { this.updateVals }, 0.1, { w.isClosed }, (\dmx_gui_ ++ counter));
watcher.start;
}
updateVals {
{
// rm.updateVals;
// nc.updateVals;
// cl.updateVals;
}.defer;
}
hide{
if ( GUI.scheme.id == \swing,
{
w.visible_( false );
},{
w.close;
});
watcher.stop;
}
show{
if ( GUI.scheme.id == \swing,
{
w.visible_( true );
});
watcher.start;
}
}
DMXConsole{
classvar <>xposScreen=0, <>yposScreen=20;
classvar <counter = 0;
var <>dmx;
var <sliderviews;
var <showconsole;
var <tabpane;
var <sliders;
var <>w, <watcher;
*new { |dmx, w|
^super.new.w_(w).dmx_(dmx).init;
}
init {
var f, xsize, ysize;
counter = counter + 1;
xsize = 16*37 + 15;
ysize = 210;
w = w ?? {
w = GUI.window.new("DMX Console", Rect(xposScreen, yposScreen, xsize + 10, ysize)).front;
w.view.decorator = FlowLayout(Rect(4, 4, w.bounds.width, w.bounds.height), 2@2, 2@2);
w;
};
// unclutter the windows on the screen:
yposScreen = yposScreen + 160;
if ( yposScreen > 700,
{ yposScreen = 20; xposScreen = xposScreen + xsize + 10;
if ( xposScreen > 900,
{ xposScreen = 0; });
});
//sliderview = GUI.scrollPane.new( w, w.view.bounds )
tabpane = JSCTabbedPane.new( w, Rect( w.view.bounds.left, w.view.bounds.top, w.view.bounds.width-15, w.view.bounds.height-15) );
//.verticalScrollBarShown_( \never ).horizontalScrollBarShown_( \auto );
sliders = Array.new;
sliderviews = 16.collect{ |it2|
var gv;
gv = GUI.compositeView.new( tabpane, Rect( 0, 0, 16*35 + 5, 165) );
sliders = sliders ++ 16.collect{ |it|
DMXSlider.new( dmx, gv, (37*it)+2 ).channel_( it + (16*it2) ).value_(0);
};
tabpane.setTitleAt( it2, (""++(it2*16)++"-"++((it2+1)*16-1)) );
gv;
};
/* sliders = 16.collect{ |it|
DMXSlider.new( dmx, sliderview, (37*it)+5 ).channel_( it ).value_(0);
};*/
w.refresh;
w.front;
watcher = SkipJack.new( { this.updateVals }, 0.1, { w.isClosed }, (\dmx_console_ ++ counter));
watcher.start;
}
updateVals {
{
// update only the currently visible slider values
sliders.copyRange( tabpane.value * 16, (tabpane.value+1)*16-1 ).do{ |it,i|
it.value = dmx.currentCue.data.at( it.chan.value );
}
}.defer;
}
hide{
if ( GUI.scheme.id == \swing,
{
w.visible_( false );
},{
w.close;
});
watcher.stop;
}
show{
if ( GUI.scheme.id == \swing,
{
w.visible_( true );
});
watcher.start;
}
}
DMXSlider{
var <slider, <val, <chan, <nam;
var <view;
var <>dmx;
*new{ |dmx,parent,xoff|
^super.new.dmx_(dmx).init( parent, xoff );
}
init{ |parent,xoffset|
var coff;
coff = xoffset; // currently as compositeView is not relative
view = GUI.compositeView.new( parent, Rect( xoffset, 0, 35, 150 ));
slider = GUI.slider.new( view, Rect( coff, 0, 35, 80 ) ).action_({ |slid|
val.valueAction_( slid.value );
});
val = GUI.numberBox.new( view, Rect( coff, 82, 35, 20 ) ).action_({ |nb|
var curVal;
curVal = nb.value.clip(0,1);
dmx.currentCue.data.put( chan.value, curVal );
if ( dmx.autoSet, { dmx.setCue } );
val.value = curVal;
slider.value = curVal;
//slider.value = nb.value;
}).step_(1/256);
chan = GUI.numberBox.new( view, Rect( coff, 104, 35, 20 ) );
nam = GUI.textField.new( view, Rect( coff, 126, 35, 20 ) ).action_({ |tf|
dmx.map.put( tf.value.asSymbol, chan.value.asInteger );
});
}
channel_{ |cval|
chan.value = cval;
nam.value = dmx.map.findKeyForValue( cval );
}
value_{ |cval|
val.value = cval;
slider.value = cval;
}
}
// This contains a set of higher level classes
// Classes for a specific light, or a group of lights, or a complete set
DMXSet{
var <>name;
var <groups;
*new{ |name|
^super.new.init.name_( name );
}
init{
groups = ();
}
addGroup{ |group|
groups.put( group.name, group );
}
removeGroup{ |group|
groups.removeAt( group.name );
}
}
DMXGroup{
var <>name;
var <lights;
*new{ |name|
^super.new.init.name_( name );
}
init{
lights = ();
}
addLight{ |light|
lights.put( light.name, light );
}
removeLight{ |light|
lights.removeAt( light.name );
}
}
DMXLight{
var <>name;
var <properties;
var <channels;
*new{ |name|
^super.new.init.name_( name );
}
init{
properties = ();
channels = ();
}
setStrobe{ |chans,vals|
vals = vals ? [0,0,0];
properties.put( \duration, vals[0] );
properties.put( \intensity, vals[1] );
properties.put( \rate, vals[2] );
channels.put( \duration, chans[0] );
channels.put( \intensity, chans[1] );
channels.put( \rate, chans[2] );
DMX.map.put( name++\_++\duration, chans[0] );
DMX.map.put( name++\_++\intensity, chans[1] );
DMX.map.put( name++\_++\rate, chans[2] );
}
setGob{ |chans,vals|
/* set to useful stuff here
vals = vals ? [0,0,0];
properties.put( \duration, vals[0] );
properties.put( \intensity, vals[1] );
properties.put( \rate, vals[2] );
channels.put( \duration, chans[0] );
channels.put( \intensity, chans[1] );
channels.put( \rate, chans[2] );
DMX.map.put( name++\_++\duration, chans[0] );
DMX.map.put( name++\_++\intensity, chans[1] );
DMX.map.put( name++\_++\rate, chans[2] );
*/
}
setLight{ |chans,vals|
vals = vals ? [0,0];
properties.put( \intensity, vals[0] );
properties.put( \color, vals[1] );
channels.put( \intensity, chans[0] );
channels.put( \color, chans[1] );
DMX.map.put( name++\_++\intensity, chans[0] );
DMX.map.put( name++\_++\color, chans[1] );
}
set{ |key,val|
properties.put( key, val );
}
}
// with DMXColor you can store colormaps corresponding to the needed DMX value to get the specified color
DMXColor{
classvar <>maps;
classvar <>currentMap;
*initClass{
maps = ();
currentMap = ();
}
*get{ |color|
^currentMap.at( color );
}
}
// DMX is a masterclass which contains the DMXCues, and the DMXDevice
// for now, it is assumed that there is always 1 dmx device
DMX{
classvar <>device;
classvar <>channeloffset=0;
classvar <>maxchannels=512;
classvar <>autoSet = false;
// map is an IdentityDictionary, which allows you to use names for channels
classvar <map;
// cues are all the cues that are defined for this thing
classvar <cues;
// the current light cue, containing the settings for this moment
classvar <>currentCue;
classvar <fadeval;
/* *new{
^super.new.init;
}*/
*initClass{
map = ();
cues = Array.new;
}
*setCurrentMap{ arg name;
map = DMXMap.at( name );
}
*setCue{
device.sendDMX( currentCue );
}
*blackout{ |time,curve|
if ( time.isNil, {
currentCue = DMXCue.new;
this.setCue;
},{
this.fade( DMXCue.new, time, curve );
});
}
*fade{ arg to, time=1.0, curve=\linear, timestep=0.025;
var spec, startCue, endCue, nsteps, ddmx, curdmx;
spec = [0,1,curve].asSpec;
startCue = currentCue;
if ( to.isKindOf( DMXSubCue ), {
endCue = currentCue.deepCopy.merge( to );
}, {
endCue = to;
});
endCue.data.postln;
nsteps = round(time/timestep);
// can't do more than 256 steps, due to 8bit resolution
if ( nsteps > 256, { nsteps = 256; timestep = time/nsteps; } );
ddmx = 1/nsteps;
// Tdef( \dmxfade ).envir = ();
Tdef( \dmxfade, {
// envir = ();
// envir.put( \timestep, timestep );
// envir.put( \nsteps, nsteps );
Tdef(\dmxfade).set( \speed, 1 );
nsteps.do{ |i|
currentCue = DMXCue.new;
// spec.map( 1-(ddmx*(i+1)) ).postln;
currentCue.data = (startCue.data * spec.map( 1-(ddmx*(i+1)) ) ) + (endCue.data * spec.map( ddmx*(i+1) ) );
fadeval = ddmx*(i+1); // could be displayed
Tdef(\dmxfade).set( \fadeval, fadeval );
// currentCue.data.postln;
this.setCue;
( timestep / Tdef(\dmxfade).envir.speed ).wait;
};
});
Tdef( \dmxfade ).play;
}
}
DMXMap{
classvar maps;
*initClass{
maps = ();
}
*addMap{ arg name;
maps.put( name, () );
}
*at{ arg name;
^maps.at( name );
}
*putItem{ arg name, itemName, value;
maps.at( name ).put( itemName, value );
^maps.at( name );
}
}
// DMXCue is a complete set of DMX messages, which make up one scene.
DMXCue{
var <>name;
var <>id;
var <>channeloffset=0;
var <>maxchannels=512;
var <>data;
classvar spec;
*initClass{
ControlSpec.initClass;
spec = [0, 256, \linear, 1].asSpec;
}
*new{ arg offset, maxch;
^super.new.init( offset, maxch );
}
init{ arg offset, maxch;
channeloffset = offset ? channeloffset;
maxchannels = maxch ? maxchannels;
data = Array.fill( this.size, 0 );
}
size{
^(maxchannels-channeloffset);
}
// returns the cue as an Int8Array, for sending it to the device
asInt8{
^spec.map( data ).as( Int8Array );
}
merge{ |subcue|
subcue.data.do{ |it,i|
data.put( i, it );
}
}
put{ |id, val|
data.put( id, val );
}
at{ |id|
^data.at( id );
}
}
DMXSubCue{
var <data;
*new{
^super.new.init;
}
init{
data = Order.new;
}
put{ |id, val|
data.put( id, val );
}
at{ |id|
^data.at( id );
}
}
// for now, we assume that the DMXDevice shows up as a SerialPort in the computer; this is the case for the EntTec DMX USB Pro, which is a subclass of DMXDevice
// DMXDevice just encapsulates the common properties of DMXDevices
DMXDevice : SerialPort{
sendDMX{ arg cue;
var datablob;
var cuesize = cue.size + 1;
// Int8Array[0] is the DMX start code
datablob = this.createSendHeader(cuesize) ++ Int8Array[0] ++ cue.asInt8 ++ this.createFooter;
// add in when testing for real:
//this.putAll( datablob );
}
createSendHeader{ arg data_size=512;
// subclass responsibility
^Int8Array[];
}
createFooter{
// subclass responsibility
^Int8Array[];
}
}
EntTecDMXUSBPro : DMXDevice {
*new {
| port,
baudrate(57600),
databits(8),
stopbit(true),
parity(nil),
crtscts(false),
xonxoff(false)
exclusive(false) |
^super.new( port, baudrate, databits, stopbit, parity, crtscts, xonxoff, exclusive ).init;
}
init{
DMX.device = this;
}
createSendHeader{ arg data_size = 512;
// header consists of: 0x7E, label (in this case 6), datasize low byte, datasize high byte;
// ser.write(chr(data_size & 0xFF))
// ser.write(chr((data_size >> 8) & 0xFF))
^Int8Array[ 0x7E, 6, data_size.bitAnd( 0xFF ), (data_size >> 8).bitAnd( 0xFF ) ];
}
createFooter{
^Int8Array[ 0xE7 ];
}
}