Add .sc as a SuperCollider file extension

This commit is contained in:
Paul Chaignon
2014-10-22 10:16:30 -04:00
parent 2a66b754c2
commit b6a9993c97
5 changed files with 696 additions and 0 deletions

View File

@@ -2299,6 +2299,7 @@ SuperCollider:
lexer: Text only
extensions:
- .scd
- .sc
Swift:
type: programming

View File

@@ -0,0 +1,19 @@
WarpPreset {
*new {|path|
if(path.notNil) {
^Object.readArchive(path);
};
^super.new.init();
}
init {
}
save {
Dialog.savePanel({|path|
this.writeArchive(path);
});
}
}

View File

@@ -0,0 +1,286 @@
WarpTate {
classvar <numSections = 8;
// classvar <sectionDur = 3 * 60;
classvar <sectionDur = 60;
var <sensorKeys;
var <clock;
var <tempo;
var <>tempoChannel;
var <>tempoControl;
var <out;
var <tracks;
var <>sections;
var <availableControls;
var <controls;
var <sensorVals;
var <sensorPrevs;
var <sensorMins;
var <sensorMaxs;
var <>sensorMinAdj;
var <>sensorMaxAdj;
var <doAdjusts;
var <playRout;
*new {
^super.new.init;
}
init {
tempo = 120;
tempoChannel = 15;
tempoControl = 3;
clock = TempoClock.default
.tempo_(2)
.permanent_(true);
MIDIClient.init;
out = MIDIOut.newByName("IAC Driver", "Bus 1");
out.latency = 0;
tracks = IdentityDictionary[];
sections = Array.newClear(WarpTate.numSections);
// sections is a List of IdentityDictionary to be mapped to
// WarpTrack settings var
// e.g. List[IdentityDictionary[
// '303_1' -> IdentityDictionary['notes' -> List[42]],
// '808_1' -> IdentityDictionary['notes' -> List[24]]
// ]
// ]
sensorKeys = ['303a', '303b', '808a', '808b'];
// channel 16 reserved for tempo changes
availableControls = 15.collect {|channel|
(0..120).reject({|item, i|
[ 0, 1, 2, 4, 5, 6, 7, 8, 10, 11, 12, 13, 64, 65, 66, 67, 68,
69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 84, 91, 92, 93, 94,
95, 96, 97, 98, 99, 100, 102 ].includes(item)
});
};
controls = (0..127).collect { IdentityDictionary[] };
this.addOSCdefs();
CmdPeriod.add({this.stop});
}
tempo_ {|argTempo|
if(argTempo >= 50 && argTempo <= 177) {
tempo = argTempo;
clock.tempo = tempo / 60;
out.control(tempoChannel, tempoControl, tempo - 50);
} {
"Tempo out of Logic's range :(".postln;
};
}
addTrack {|trackKey, channel, type|
tracks[trackKey] = WarpTrack(this, trackKey, channel, type);
^tracks[trackKey];
}
loadTrack {|preset, checkAvailable|
var track = WarpTrack.load(this, preset, checkAvailable),
trackKey = track.settings['key'];
tracks[trackKey] = track;
^tracks[trackKey];
}
readTrack {|path|
var track = WarpTrack.read(this, path),
trackKey = track.settings['key'];
tracks[trackKey] = track;
^tracks[trackKey];
}
removeTrack {|trackKey|
tracks[trackKey].allOff();
tracks[trackKey] = nil;
}
removeAllTracks {
tracks = IdentityDictionary[];
}
on {|trackKey, note|
tracks[trackKey].on(note);
}
off {|trackKey, note|
tracks[trackKey].off(note);
}
hit {|trackKey, note=60, vel=127, dur=1|
tracks[trackKey].hit(note, vel, dur);
}
noteOn {|midiChannel, note, vel|
out.noteOn(midiChannel, note, vel);
}
noteOff {|midiChannel, note, vel|
out.noteOff(midiChannel, note, vel);
}
control {|midiChannel, num, val|
out.control(midiChannel, num, val);
}
isControlAvailable {|channel, controlNum|
^controls[channel].keys.includes(controlNum.asSymbol).not;
}
setControl {|channel, num, key|
controls[channel][num.asSymbol] = key;
availableControls[channel].remove(num);
}
assign {|trackKey, paramKey, controlNum, learn=false|
var channel = tracks[trackKey].settings['midiChannel'];
if(controlNum.notNil && this.isControlAvailable(channel, controlNum)) {
tracks[trackKey].assign(paramKey, controlNum, learn);
availableControls[channel].removeAt(0);
} {
if(availableControls[channel].size > 0) {
tracks[trackKey].assign(paramKey, availableControls[channel][0], learn);
availableControls[channel].removeAt(0);
} {
"no controls left!".postln;
};
};
"Don't forget to turn off MIDI learn".postln
}
setParam {|trackKey, paramKey, val|
var track, param;
if((track = tracks[trackKey]).notNil) {
if(track.params[paramKey].notNil) {
track.setParam(paramKey, val);
} {
"paramKey doesn't exist".postln;
};
} {
"track key doesn't exist".postln;
};
}
// sec takes IdentityDictionarys of WarpTrack settings
addSection {|index, tempo=120, presets|
// Add section with tempo
sections[index] = IdentityDictionary[
'tempo' -> tempo,
'tracks' -> presets
];
presets.do {|preset, i|
// create track if there ain't one with this key
if(tracks.includesKey(preset['key']).not) {
this.loadTrack(preset, true);
};
// store the preset
// sections[index]['tracks'][preset['key']] = preset;
};
^sections;
}
play {
if(sections.any {|item, i| item.notNil; }) {
playRout = Routine {
inf.do {|i|
sections.do {|section|
if(section.notNil) {
if(i !== 0) {
tracks.do {|track|
track.allOff();
};
this.removeAllTracks();
};
this.tempo = section['tempo'];
section['tracks'].do {|track, i|
var newTrack = this.loadTrack(track, false);
newTrack.play();
};
sectionDur.wait;
};
};
}
};
clock.playNextBar(playRout);
} {
"no sections added!".postln;
};
}
stop {
playRout.stop;
tracks.do {|track|
track.allOff();
out.allNotesOff(track.settings['midiChannel']);
}
}
addOSCdefs {
sensorVals = 0!sensorKeys.size;
sensorPrevs = 0!sensorKeys.size;
sensorMins = 9999!sensorKeys.size;
sensorMaxs = 0!sensorKeys.size;
sensorMinAdj = sensorMinAdj ?? { 0.005 };
sensorMaxAdj = sensorMaxAdj ?? { 0.01 };
doAdjusts = false!sensorKeys.size;
sensorKeys.do {|sensorKey, i|
OSCdef(("sensor_" ++ sensorKey).asSymbol, {|msg, time, addr, recvPort|
var val = msg[1];
sensorPrevs[i] = sensorVals[i];
sensorVals[i] = val;
if(doAdjusts[i]) {
sensorMins[i] = min(val, sensorMins[i]);
sensorMaxs[i] = max(val, sensorMaxs[i]);
if(val < sensorMaxs[i]) {
sensorMaxs[i] = sensorMaxs[i] - sensorMaxAdj;
};
if(val > sensorMins[i]) {
sensorMins[i] = sensorMins[i] + sensorMinAdj;
};
} {
val = val.clip(sensorMins[i], sensorMaxs[i]);
};
tracks.do {|track, j|
if(track.settings['sensorFuncs'].includesKey(sensorKey)) {
track.sensor(
sensorKey,
val.linlin(
sensorMins[i],
sensorMaxs[i],
127,
0
)
);
};
}
}, ("/prox/" ++ sensorKey).asSymbol);
}
}
}

View File

@@ -0,0 +1,263 @@
WarpTrack {
classvar <defaults;
var <parent;
var <settings;
*initClass {
defaults['303_1'] = defaults['303'].copy;
defaults['303_1']['paramControls'].putPairs([
'Bus 1', 46,
'Bus 2', 45,
'Bus 3', 48,
'echovol', 47
]);
defaults['303_1']['params'].putPairs([
'Bus 1', 0,
'Bus 2', 0,
'Bus 3', 0,
'echovol', 0
]);
defaults['303_2'] = defaults['303_1'].copy;
defaults['303_2']['paramControls'].putPairs([
]);
defaults['303_2']['params'].putPairs([
]);
defaults['808_1'] = defaults['808'].copy;
defaults['808_1']['paramControls'].putPairs([
'bitcrusher', 88,
'Send 1', 89,
'Send 2', 90,
'Send 3', 101
]);
defaults['808_1']['params'].putPairs([
'bitcrusher', 0,
'Send 1', 0,
'Send 2', 0,
'Send 3', 0
]);
defaults['808_2'] = defaults['808_1'].copy;
defaults['808_2']['paramControls'].putPairs([]);
defaults['808_2']['params'].putPairs([]);
}
*new {|argParent, argKey, argMidiChannel, argType|
^super.new.init(argParent, argKey, argMidiChannel, argType);
}
*read {|argParent, path|
^super.new.init(argParent).readPreset(path);
}
*load {|argParent, preset, checkAvailable|
^super.new.init(argParent).loadPreset(preset, checkAvailable);
}
init {|argParent, argKey, argMidiChannel, argType|
parent = argParent;
settings = IdentityDictionary[
'key' -> argKey,
'midiChannel' -> argMidiChannel,
'notes' -> Set[],
'params' -> IdentityDictionary[],
'paramControls' -> IdentityDictionary[],
'patternTrack' -> true,
'sensorFuncs' -> IdentityDictionary[]
];
if(argType.notNil) {
settings['type'] = argType;
if(WarpTrack.defaults.keys.includes(argType)) {
['paramControls', 'params'].do {|key, i|
settings[key] = WarpTrack.defaults[argType][key];
}
};
this.initParams();
};
}
on {|note|
var clock = parent.clock;
var sub = 1 / (parent.clock.tempo * 16); // one sub division
clock.schedAbs(clock.nextBar - sub, {
if(settings['patternTrack']) {
this.allOff();
} {
this.off(note);
};
});
parent.clock.playNextBar({
settings['notes'].add(note);
parent.noteOn(settings['midiChannel'], note, 127);
});
}
off {|note|
// settings['notes'].remove(note);
parent.noteOff(settings['midiChannel'], note, 0);
}
hit {|note=60, vel=127, dur=1, quant=0|
{
parent.noteOn(settings['midiChannel'], note, vel);
dur.wait;
parent.noteOff(settings['midiChannel'], note, vel);
}.fork(parent.clock, quant:quant);
}
allOff {
settings['notes'].do {|note, i|
this.off(note);
};
}
assign {|paramKey, num, learn=false, init=true, checkAvailable=true|
if(num.notNil) {
this.assignAll(IdentityDictionary[paramKey -> num], learn, init, checkAvailable);
} {
parent.assign(settings['key'], paramKey, nil, learn);
};
}
assignAll {|paramControls, learn=false, init=true, checkAvailable=true|
var action = {
var channel = settings['midiChannel'];
paramControls.keysValuesDo { |paramKey, num|
if(checkAvailable.not ||
(checkAvailable && parent.isControlAvailable(channel, num))) {
settings['paramControls'][paramKey] = num;
if(init) {
settings['params'][paramKey] = 0;
};
parent.setControl(channel, num, paramKey);
if(learn) {
parent.control(channel, num, 127);
0.05.wait;
parent.control(channel, num, 0);
};
paramKey ++ " assigned to controlNum " ++ num;
} {
("this controlNum " ++ num ++ " is already assigned!").postln;
};
};
};
if(learn) {
action.fork;
} {
action.();
};
}
initParams {
settings['params'].keysValuesDo { |key, value|
this.setParam(key, value);
};
settings['paramControls'].keysValuesDo { |key, value|
parent.setControl(settings['midiChannel'], value, key);
};
}
setParam {|paramKey, val, quant|
var func = {
parent.control(
settings['midiChannel'],
settings['paramControls'][paramKey],
val
);
settings['params'][paramKey] = val;
};
if(quant.notNil) {
{
func.();
}.fork(parent.clock, quant:quant);
} {
func.();
};
}
readPreset {|path, checkAvailable=true|
this.loadPreset(Object.readArchive(path), checkAvailable);
}
loadPreset {|preset, checkAvailable=true|
// copy all settings except notes and paramControls
preset.keys.reject({|settingKey, i|
['notes', 'paramControls'].includes(settingKey);
}).do {|presetKey, i|
settings[presetKey] = preset[presetKey];
};
// copy notes if it's a patternTrack
if(preset['patternTrack']) {
settings['notes'] = preset['notes'];
};
// assign all without learn or init
this.assignAll(
preset['paramControls'],
false,
false,
checkAvailable
);
this.initParams();
// if(settings['notes'].size > 0) {
// this.on(settings['notes'].asArray[0]);
// };
}
sensor {|sensorKey, val|
settings['sensorFuncs'][sensorKey].do {|func, i|
func.(this, val);
}
}
addFunc {|sensorKey, funcKey, func|
if(parent.sensorKeys.includes(sensorKey)) {
if(settings['sensorFuncs'].includesKey(sensorKey).not) {
settings['sensorFuncs'][sensorKey] = IdentityDictionary[];
};
settings['sensorFuncs'][sensorKey][funcKey] = func;
} {
"parent doesn't have that sensor key".postln;
};
}
removeFunc {|sensorKey, funcKey|
settings['sensorFuncs'][sensorKey].removeAt(funcKey);
if(settings['sensorFuncs'][sensorKey].isEmpty) {
settings['sensorFuncs'].removeAt(sensorKey);
};
}
availableControls {
^parent.availableControls[settings['midiChannel']].copy;
}
save {
Dialog.savePanel({|path|
settings.writeArchive(path);
});
}
play {
if(settings['patternTrack'] && settings['notes'].notEmpty) {
this.on(settings['notes'].choose);
};
}
}

View File

@@ -0,0 +1,127 @@
WarpUtil {
var <>parent;
var <curSensor;
var <win;
var <texts;
var <sensorSlider;
var <sliders;
var <updateRout;
*new {|argParent|
^super.new.init(argParent);
}
init {|argParent|
parent = argParent;
texts = IdentityDictionary[];
this.makeView();
this.startUpdate();
}
calibrate {
{
parent.doAdjusts.size.do {|i|
parent.sensorMaxs[i] = 0;
parent.sensorMins[i] = 9999;
parent.doAdjusts[i] = true;
};
sliders.do {|slider, i|
if(parent.sensorKeys[i] !== curSensor) {
slider.valueAction_(0);
0.1.wait;
slider.valueAction_(1);
};
}
}.fork(AppClock);
}
stopCalibrate {
parent.doAdjusts.size.do {|i|
parent.doAdjusts[i] = false;
}
}
makeView {
var width = 370;
if(win.notNil) {
win.close;
};
win = Window("Sensor Inspector", Rect(256, 10, width, 424)).front;
win.view.addFlowLayout;
['sensorVals', 'sensorPrevs', 'sensorMins', 'sensorMaxs'].do {|label, i|
StaticText(win, (width * 0.5)@20)
.string_(label.asString);
win.view.decorator.nextLine;
texts[label] = StaticText(win, (width / 2)@40);
if(curSensor.notNil) {
texts[label].string_(
"\t" ++ parent.perform(label).at(
parent.sensorKeys.indexOf(curSensor)
);
);
};
win.view.decorator.nextLine;
};
sensorSlider = EZSlider(win, 280@20, label: curSensor.asString);
sliders = parent.sensorKeys.collect { |sensorKey, i|
EZSlider(win, 280@20, label: sensorKey.asString)
.action_({|ez|
NetAddr.localAddr.sendMsg(
"/prox/" ++ sensorKey,
ez.value * 100
);
});
};
}
startUpdate {
updateRout = Routine {
inf.do {|i|
var index;
if(curSensor.notNil) {
index = parent.sensorKeys.indexOf(curSensor);
['sensorVals', 'sensorPrevs', 'sensorMins',
'sensorMaxs'].do {|label, i|
texts[label]
.string_("\t" ++ parent.perform(label)[index]);
};
sensorSlider.value = parent.sensorVals[index].linlin(
parent.sensorMins[index],
parent.sensorMaxs[index],
0,
1
);
};
0.05.wait;
}
};
updateRout.play(AppClock);
}
stopUpdate {
updateRout.stop();
}
curSensor_ {|argCurSensor|
curSensor = argCurSensor;
this.makeView();
}
}