Submit
Browse
Anonymous
Login
RSS
SuperCollider Code
Fork Code: Command-line SC utility for MIDI clock out
name
code content
var window; var deviceMenu, devices, device, midiout; var tempoCtl, tempoCtlView, tempoWatcher; var clock, beatView, beatRoutine; var beatColor = Color.white; var pendingColor = Color.gray(0.5); var background = Color.gray(0.3); var vBack = Color.gray(0.25); var startB; var routine; var d = 1/24; // don't collide with main SC process (dammit) Archive.archiveDir = PathName.tmp; MIDIClient.init; // synchronous devices = MIDIClient.destinations.collect { |ep| ep.device + " : " ++ ep.name }; if(thisProcess.argv.size >= 1) { device = devices.detectIndex { |str| str.containsi(thisProcess.argv[0]) }; }; clock = LinkClock.new.latency_(s.latency); TempoClock.default = clock; if(thisProcess.argv.size >= 2) { clock.tempo = thisProcess.argv[1].asFloat / 60.0; }; midiout = MIDIOut(0); window = Window("MIDI Clock Out", Rect(800, 200, 500, 250)).front; window.background_(Color.gray(0.2)); window.layout = VLayout( HLayout( nil, deviceMenu = PopUpMenu(), startB = Button(), // stopB = Button(), nil ), View().fixedHeight_(60) .layout_(HLayout( nil, StaticText().string_("Tempo").stringColor_(Color.white), tempoCtlView = View().minWidth_(400), nil )), beatView = UserView().fixedHeight_(100) ); deviceMenu .items_(devices) .background_(vBack) .stringColor_(Color.white) .action_({ |view| if(device.notNil) { midiout.disconnect(device); }; device = view.value; midiout.connect(device); }); if(device.notNil) { deviceMenu.value_(device); midiout.connect(device); }; startB.states_([["stopped", Color.white, Color.gray(0.5)], ["RUNNING", Color.black, Color(0.7, 1, 0.7)]]) .action_({ |view| if(view.value > 0) { if(routine.notNil) { routine.stop }; routine = Routine { midiout.start; (thisThread.clock.nextBar - thisThread.beats).wait; loop { 23.do { |i| midiout.midiClock; d.wait; }; midiout.midiClock; (thisThread.clock.beats.ceil - thisThread.beats).wait; }; }.play(clock, quant: [-1, -0.5]); } { midiout.stop; routine.stop; routine = nil; }; }); tempoCtl = EZSlider(tempoCtlView, Rect(10, 0, 350, 40), "", [40, 240], initVal: clock.tempo * 60, labelWidth: 0, numberWidth: 45); tempoCtl.action_({ |view| clock.tempo = view.value / 60; }); tempoWatcher = SimpleController(clock) .put(\tempo, { defer { tempoCtl.value = clock.tempo * 60 } }); tempoCtl.numberView.stringColor_(Color.white).normalColor_(Color.white).background_(vBack); tempoCtl.sliderView.background_(vBack); // silly but I need to be sure MIDIClient init has finished { { tempoCtl.value = clock.tempo * 60}.defer(1) }.defer(1); beatView .background_(background) .drawFunc = { |view| var b = view.bounds.moveTo(0, 0).insetBy(10, 10); var bpb = clock.beatsPerBar; var beat = clock.beatInBar.round; var div = b.width / bpb; var dimen = Rect(div * 0.2, b.height * 0.2 + b.top, div * 0.6, b.height * 0.6); bpb.do { |i| Pen.color_( if(i <= beat) { beatColor } { pendingColor } ) .fillOval( dimen.moveBy(i * div, 0) ) }; }; beatRoutine = Routine { loop { { beatView.refresh }.defer(s.latency); 1.0.wait; } }.play(clock, quant: 1); window.onClose = { beatRoutine.stop; routine.stop; clock.stop; tempoWatcher.remove; if(thisProcess.platform.ideName != "scqt") { 0.exit }; };
code description
When sending MIDI clock out at a fast tempo, there may be fewer than 10 ms between clock ticks. If you're sending the clock messages from the same sclang that is sequencing musical events, any unexpectedly long-running sclang activity could cause clock messages to be delayed, interfering with timing. One solution is to run the MIDI clock in a separate sclang instance, and sync to the main sclang instance using LinkClock. Command-line: sclang path/to/thisUtility.scd deviceMatch tempo Both parameters, after the code file path, are optional. You can change them in a GUI later. 'deviceMatch' is a partial string match for the MIDI output device. 'tempo' is a float, for BPM. When you click the button next to the device menu, it will wait until just before the next bar line, and then send a MIDI clock start message, and start running ticks on the downbeat. This has been battle-tested in a pub live jam, driving a Digitakt, for an hour without glitches. Note that the MIDI '.connect' stuff is for Linux. You might need to tweak this for Windows or Mac.
use markdown for formating
category tags
comma separated, i.g. "wild, siren" (do not enter default SC class names, please)
ancestor(s)
comma separated identificators, i.g. "1-C,1-1,1-4M,1-x"
Private?
the code will be accessible by direct url and not visible in public activity
signup to submit public code without captcha
comment of change