From 57050ba42967df9fa159dbb257f58c27216c8db3 Mon Sep 17 00:00:00 2001 From: David Runge Date: Wed, 30 Dec 2020 01:14:51 +0100 Subject: Add classes for Error and Device classes/ZZZError.sc: Add a simple Error class. classes/ZZZDevice.sc: Add a generic class to make the device-specific ZZZES3 class obsolete. This class utilizes all new and improved SynthDefs of ZZZ. --- classes/ZZZDevice.sc | 334 +++++++++++++++++++++++++++++++++++++++++++++++++++ classes/ZZZError.sc | 3 + 2 files changed, 337 insertions(+) create mode 100644 classes/ZZZDevice.sc create mode 100644 classes/ZZZError.sc diff --git a/classes/ZZZDevice.sc b/classes/ZZZDevice.sc new file mode 100644 index 0000000..b10a6b0 --- /dev/null +++ b/classes/ZZZDevice.sc @@ -0,0 +1,334 @@ +ZZZDevice : ZZZ{ + + classvar < clockTypes = #[\zeroFive, \minusFiveFive, \korgZeroFive, \korgMinusFiveFive]; + classvar < playTypes = #[\beat, \bar]; + var < outs, + < tempoBusses, + < clocks, + < deviceGroup, + < gateBusses, + < gates, + < tempos, + clocksTempoMap; + + *new{ + arg channels, server; + if(channels.isNil, { + ZZZError("ZZZDevice: Initialized without setting channels parameter!").throw; + }); + if(server.isNil, { + ZZZError("ZZZDevice: Initialized without setting server parameter!").throw; + }); + ^super.newCopyArgs(channels, server).init; + } + + init{ + ("Initializing ZZZDevice...").postln; + clocks = Dictionary(); + gates = Dictionary(); + gateBusses = Dictionary(); + outs = Dictionary(); + tempos = Dictionary(); + tempoBusses = Dictionary(); + clocksTempoMap = Dictionary(); + (1..super.channels.size).do({|item, i| + outs.add(item -> super.channels[i]); + }); + // add Group for all Synths used by ZZZDevice instance + super.server.doWhenBooted({deviceGroup = Group.new()}); + } + + outputOnHardware{ |output| + if(output.isNil || output.isInteger.not,{ + ZZZError("ZZZDevice: The provided output number is not valid (needs to be >= 1).").throw; + }); + if(output < 1 || output > outs.size,{ + ZZZError("ZZZDevice: The provided output number is not valid (needs to be >= 1, <"++outs.size++").").throw; + }); + ^outs.at(output); + } + + addTempo{|slot, tempo| + if(slot.isNil || slot.isInteger.not, { + ZZZError("ZZZDevice: The provided slot number is not valid (needs to be >= 1).").throw; + }); + if(slot < 1, { + ZZZError("ZZZDevice: The provided slot number is not valid (needs to be >= 1).").throw; + }); + if(tempo.isNil || tempo.isFloat.not, { + ZZZError("ZZZDevice: The provided tempo is not valid (needs to be >= 0.0).").throw; + }); + if(tempo < 0.0, { + ZZZError("ZZZDevice: The provided tempo is not valid (needs to be >= 0.0).").throw; + }); + + if(tempos.at(slot).isNil, { + ("Create new TempoBusClock in slot "++ slot++" with tempo "++tempo).postln; + tempoBusses.add(slot -> Bus.control()); + tempoBusses.at(slot).setSynchronous(tempo); + tempos.add(slot -> TempoBusClock.new(control: tempoBusses.at(slot))); + },{ + ZZZError("ZZZDevice: Cannot add tempo "++ tempo ++" in slot "++ slot ++". There is a TempoBusClock already.").throw; + }); + } + + removeTempo{|slot, playType = \beat, quant = 1| + var removeFunc; + if(slot.isNil || slot.isInteger.not, { + ZZZError("ZZZDevice: The provided slot number is not valid (needs to be >= 1).").throw; + }); + if(slot < 1, { + ZZZError("ZZZDevice: The provided slot number is not valid (needs to be >= 1).").throw; + }); + + clocksTempoMap.keysValuesDo({|key, value| + if(value == slot, { + this.removeClock(output: key, playType: playType, quant: quant); + }); + }); + removeFunc = { + tempos.at(slot).clear; + tempos.at(slot).stop; + tempos.removeAt(slot); + tempoBusses.at(slot).free; + tempoBusses.removeAt(slot); + }; + if(playType == \beat, { + tempos.at(slot).play(removeFunc, quant: quant); + }); + if(playType == \bar, { + tempos.at(slot).playNextBar(removeFunc); + }); + } + + setTempo{|slot, tempo| + if(slot.isNil || slot.isInteger.not || slot < 1, { + ZZZError("ZZZDevice: The provided slot number is not valid (needs to be >= 1).").throw; + }); + if(tempo.isNil || tempo.isFloat.not || tempo < 0.0, { + ZZZError("ZZZDevice: The provided tempo is not valid (needs to be >= 0.0).").throw; + }); + if(tempos.at(slot).isNil, { + ZZZError("ZZZDevice: Cannot set tempo "++ tempo ++" at slot "++ slot ++". There is no TempoBusClock.").throw; + }); + + tempoBusses.at(slot).setSynchronous(tempo); + } + + addClock{ |output, slot, type = \zeroFive, playType = \beat, quant = 1, replace = false| + var clockFunc, synthName, synthArgs; + if(slot.isNil || slot.isInteger.not || slot < 1, { + ZZZError("ZZZDevice: The provided slot number is not valid (needs to be >= 1).").throw; + }); + if(slot < 1, { + ZZZError("ZZZDevice: The provided slot number is not valid (needs to be >= 1).").throw; + }); + if(tempos.at(slot).isNil, { + ZZZError("ZZZDevice: There is no tempo at slot "++slot).throw; + }); + if(output.isNil || output.isInteger.not || output < 1, { + ZZZError("ZZZDevice: The provided output number is not valid (needs to be >= 1).").throw; + }); + if(output < 1, { + ZZZError("ZZZDevice: The provided output number is not valid (needs to be >= 1).").throw; + }); + if(clockTypes.includes(type).not, { + ZZZError("ZZZDevice: Unrecognized clock type "++ type ++". Only the following are understood: "++ clockTypes).throw; + }); + if(playTypes.includes(playType).not, { + ZZZError("ZZZDevice: Unrecognized playType "++ type ++". Only the following are understood: "++ playTypes).throw; + }); + if(clocks.at(output).notNil && replace.not, { + ZZZError("Cannot add clock on output "++ output ++". There is a "++clocks.at(output).defName++" playing and replacement was not requested.").throw; + }); + if(clocks.at(output).isNil && replace, { + ZZZError("Cannot replace clock on output "++ output ++". There is no clock playing.").throw; + }); + // add \ZZZClock Synth to add clocking for various standards + // the standard (derived from MIDI SYNC) uses 24 pulses per quarter note + if(type == \zeroFive, { + synthName = \ZZZClock; + synthArgs = [\out, this.outputOnHardware(output: output), \bus, tempoBusses.at(slot), \mul, 0.5]; + }); + // some devices react to the range of minus five to plus five + if(type == \minusFiveFive, { + synthName = \ZZZClock; + synthArgs = [\out, this.outputOnHardware(output: output), \bus, tempoBusses.at(slot), \add, -0.5]; + }); + // Korg uses 48 pulses per quarter note + if(type == \korgZeroFive, { + synthName = \ZZZClockKorg; + synthArgs = [\out, this.outputOnHardware(output: output), \bus, tempoBusses.at(slot), \mul, 0.5]; + }); + // some devices react to the range of minus five to plus five + if(type == \korgMinusFiveFive, { + synthName = \ZZZClockKorg; + synthArgs = [\out, this.outputOnHardware(output: output), \bus, tempoBusses.at(slot), \add, -0.5]; + }); + + clockFunc = { + var synth; + if(replace, { + synth = Synth.replace(clocks.at(output), synthName, synthArgs, sameID: true).onFree({ + clocks.removeAt(output); + clocksTempoMap.removeAt(output); + }); + },{ + synth = Synth(synthName, synthArgs, target: this.deviceGroup).onFree({ + clocks.removeAt(output); + clocksTempoMap.removeAt(output); + }); + }); + clocks.add(output -> synth); + clocksTempoMap.add(output -> slot); + }; + // add Synth on next beat + if(playType == \beat, { + tempos.at(slot).play(clockFunc, quant: quant); + }); + // add Synth on next bar + if(playType == \bar, { + tempos.at(slot).playNextBar(clockFunc); + }); + } + + removeClock{ |output, playType = \beat, quant = 1| + var removeFunc; + if(output.isNil || output.isInteger.not || output < 1, { + ZZZError("ZZZDevice: The provided output number is not valid (needs to be >= 1).").throw; + }); + if(clocks.at(output).isNil, { + ZZZError("ZZZDevice: There is no clock at output "++output++" to remove.").throw; + }); + + if(playType == \beat, { + tempos.at(clocksTempoMap.at(output)).play({clocks.at(output).free}, quant: quant); + }); + if(playType == \bar, { + tempos.at(clocksTempoMap.at(output)).playNextBar({clocks.at(output).free}); + }); + } + + setClockTempo{ |output, slot, playType = \beat, quant = 1, type = \zeroFive| + this.addClock(output: output, slot: slot, playType: playType, quant: quant, replace: true, type: type); + } + + addGate{ |output, input = 0.9| + if(output.isNil || output.isInteger.not, { + ZZZError("ZZZDevice: The provided output number is not valid (needs to be >= 1).").throw; + }); + if(output < 1, { + ZZZError("ZZZDevice: The provided output number is not valid (needs to be >= 1).").throw; + }); + if(input.isNil || input.isFloat.not, { + ZZZError("ZZZDevice: The provided value for input is not valid (needs to be 0.0 < input <= 1.0).").throw; + }); + if(input < 0.0 || input > 1.0, { + ZZZError("ZZZDevice: The provided value for input is not valid (needs to be 0.0 < input <= 1.0).").throw; + }); + if(gates.at(output).notNil, { + ZZZError("ZZZDevice: Cannot add gate on output "++ output ++". There is a "++gates.at(output).defName++" already.").throw; + }); + + gateBusses.add(output -> Bus.control()); + gateBusses.at(output).setSynchronous(input); + gates.add( + output -> Synth(\ZZZGate, [\out, this.outputOnHardware(output: output), \bus, gateBusses.at(output)]).onFree({ + gateBusses.removeAt(output); + gates.removeAt(output); + }) + ); + } + + removeGate{ |output| + if(output.isNil || output.isInteger.not, { + ZZZError("ZZZDevice: The provided output number is not valid (needs to be >= 1).").throw; + }); + if(output < 1, { + ZZZError("ZZZDevice: The provided output number is not valid (needs to be >= 1).").throw; + }); + if(gates.at(output).isNil, { + ZZZError("ZZZDevice: There is no gate at the provided output "++output).throw; + }); + + gates.at(output).free; + } + + postAll{ + this.postClocks; + this.postGates; + this.postOuts; + this.postTempos; + } + + postClocks{ + ("Clocks").postln; + ("------").postln; + ("output -> tempo slot (value)").postln; + clocks.keysValuesDo({|key, value| + if(value.isRunning, { + if(super.server.hasShmInterface,{ + (key.asString++" -> "++clocksTempoMap.at(key)++" ("++tempoBusses.at(clocksTempoMap.at(key)).getSynchronous++")").postln; + },{ + tempoBusses.at(clocksTempoMap.at(key)).get({ |values| + (key.asString++" -> "++clocksTempoMap.at(key)++" ("++values++")").postln; + }); + }); + },{ + (key.asString++" -> "++clocksTempoMap.at(key)++" ("++Nil++")").postln; + }); + }); + ("------").postln; + } + + postGates{ + ("Gates").postln; + ("-----").postln; + ("output -> value").postln; + gates.keysValuesDo({|key, value| + if(value.isRunning, { + if(super.server.hasShmInterface,{ + (key.asString++" -> "++gateBusses.at(key).getSynchronous).postln; + },{ + gateBusses.at(key).get({ |values| + (key.asString++" -> "++values).postln; + }); + }); + },{ + (key.asString++" -> "++Nil).postln; + }); + }); + ("-----").postln; + } + + postOuts{ + ("Outputs").postln; + ("-------").postln; + ("hardware channel -> server output bus channel").postln; + outs.keysValuesDo({|key, value| + (key.asString++" -> "++value.asString).postln; + }); + ("-------").postln; + } + + postTempos{ + ("Tempos").postln; + ("------").postln; + ("slot -> speed").postln; + tempos.keysValuesDo({|key, value| + if(value.isRunning, { + if(super.server.hasShmInterface,{ + (key.asString++" -> "++tempoBusses.at(key).getSynchronous).postln; + },{ + tempoBusses.at(key).get({ |values| + (key.asString++" -> "++values).postln; + }); + }); + },{ + (key.asString++" -> "++Nil).postln; + }); + }); + ("------").postln; + } + +} diff --git a/classes/ZZZError.sc b/classes/ZZZError.sc new file mode 100644 index 0000000..670619f --- /dev/null +++ b/classes/ZZZError.sc @@ -0,0 +1,3 @@ +ZZZError : Error{ + +} -- cgit v1.2.3-54-g00ecf