^^ crow: druid scripts

Here’s a script to quickly set up some simple utilities using a clock and data input. Think Pam’s New Workout - different clock divisions for each output, and different modes for each output (S&H, quantizers, LFOs, sequencer, etc).

  • input[1] is the clock input.
  • input[2] is the data input.
  • mode[i]= <mode> sets the mode for each output. Options include S&H, slewed S&H, quantized S&H, random S&H, clock-synced LFOs, trigger pulses etc. See notes in the script for the correct strings for each mode.
  • output[i].level acts as an attenuverter/scalar for each output (pre-quantizer for quantized modes).
  • div[i] = <n> sets the clock division of the clock input for each output.
  • scale determines a scale in semitones for the quantized mode
  • sequence stores a semitone sequence which any output can be switched to.

You can change modes, levels, sequence step values, scale notes, etc in real-time using Druid/Max.

Nothing super special but I find it is nice to have a few of these quickly accessible at any time.

Let me know if you have an issues!

NB: it requires the latest version of druid installed and the most recent crow firmware installed.

Code

modes.lua (4.2 KB)

28 Likes

Thanks for this!

I’m finding that my crow will lock up if I clock this script too fast. Maybe this is expected since crow isn’t intended to be audio-rate, but I’m trying to figure out how to “clamp” the max clock value to prevent this from happening. I’m not having much luck so far. Any suggestions?

update: works like a charm after updating firmware/druid and uploading latest version of the script. thanks @voidstar

Hmm, okay, I’ll see if I can recreate!

Might be useful to set all of the outputs to ‘none’ for their mode (output[i].shape = ‘none’) except for output[1]. Then test each of the different modes on output[1] and see if there is an individual mode which is breaking down when the input clock gets above a certain rate.

It’s def not crow itself locking up, it’s something in my script :wink:

I’ll take a look tonight or tomorrow!

2 Likes

I wrote a simple octave control/adder today. It doesn’t do much, and probably isn’t worth sharing, but… :grinning:

input 1 = pitch cv
input 2 = offset cv
----------
output 1 = new and improved/ crow'd pitch cv
Summary
-- input 1 - pitch cv
-- input 2 - offset cv

local offset = 0.0

input[1].stream = function(volts) 
  output[1].volts = input[1].volts + offset
end

input[2].stream = function(volts)
  offset = math.floor(volts)
end

function init()
  input[1].mode('stream', 0.01)
  input[2].mode('stream', 0.01)
end
5 Likes

Nice!

Here’s a breakout of the quantizer function I wrote the other day - realized it might be useful to post it as an isolated code snippet. It expects a voltage and a table of semitone notes.

Summary
local scaleExample = {0,2,4,5,7,9,11,12} -- Major Scale

function quantize(volts,scale) 
	local oct = math.floor(volts)
	local interval = volts - oct
	local semitones = interval / (n2v(1))
	local degree = 1
	while degree < #scale and semitones > scale[degree+1]  do
		degree = degree + 1
	end
	local above = scale[degree+1] - semitones
	local below = semitones - scale[degree]
	if below > above then 
		degree = degree +1
	end
	note = scale[degree]
	note = note + 12*oct
	return note
end
5 Likes

Cool script! I would love to understand why input 2 doesn’t have a stream function set and instead relies on metro updates to set the offset?

Haha, tbh… it’s just the way I did it. I’m sure you could also use the input[2].stream function. :smiley:

Edit: using the stream function is clearly the way to go :sweat_smile::sweat_smile: script updated

Ha. Cool. Just curious. :+1:

1 Like

Just a quick note to say I added a bunch of example standalone crow scripts into the druid/examples folder available here.

There’s clock dividers, shift registers, a sequential switch and a bunch more!

26 Likes

Awesome, 20 characters of thank you!

Amazing, the code is so clean! I was gonna wait a bit to order both crow+JF at the same time but I might get a crow first, I already see a lot of potential uses even without JF.

boids

a flocking simulator, cheeky :smiley:

3 Likes

Ornithopters

This scripts allows you to set-up “Turing Machine” style feedback shift registers (FSRs).

It implements 5 independent shift registers which can create evolving rhythms, sequences and even velocity-sensitive envelope patterns. They can also be used as looping CV recorders or even quadrature LFOs (if you are interested in that just let me know - I should have it added in by tomorrow though)! Each of the 5 shift registers has its own unique type of data storage and voltage output associated with it.

Set the four outputs to different modes to create 4 patterns which each evolve separately but in sync, set all four outputs to read from different locations within the same register for traditional shift register CV delays, or anything in between!

By default, Output 1 generates a gate pattern, Output 2 generates an analog voltage pattern, Output 3 generates a quantized analog voltage pattern, and Output 4 generates envelopes with a pattern of different amplitudes.

The Inputs

info
name description
Input 1 The clock source.
Input 2 Used as the probability CV control: at 0V, all shift registers are frozen into loops. Moving towards +5V increases the probability that the last value will be replaced with a new value upon looping back around. Moving towards -5V increases the amount that a value will mutate/deviate away from its current value (i.e. it sets a max drunk walk distance).

The Registers

info
name description
rhythm digital register: stores 0s and 1s
weightedBitSum digital-to-analog register: it stores 0s and 1s, however outputs assigned to this register will generate an analog voltage by taking a weighted sum of several bits from the register. Weights are determined by the weights table. Think the Turing Machine Volts expander!
quantizedWeightedBitSum another digital-to-analog register, but outputs assigned to this register will be quantized to a V/oct scale set by the scales table
analog stores analog values between its min and max values.
quantizedAnalog stores analog values between its min and max, but quantizes them into the scale set by the scales table.
velocityEnvelope stores analog values, but they are treated as amplitudes for an envelope generated at the corresponding output. The attack is instantaneous, but the decay is controlled by the outputs slew time (e.g. output[1].slew = 0.3).

Each register has a length (minimum of 1, maximum of 256, default 16) which determines how many values are stored in it. The last value is looped back to the beginning if Input 2 is set to 0V, creating a fixed pattern with no mutation occurring. You can change the length of a register (non-destructively) at any time using the length key.

rhythm.length = 8
velocityEnvelope.length = 56

The 3 analog registers also have minimum and maximum values they can generate. These are set by their min and max keys:

analog.min = -5
analog.max = 3.4
velocityEnvelope.max = 7.5

The Outputs

info

Each output can be configured to a different mode of voltage generation using the modes table… Depending on the style chosen, the output will automatically be assigned to one of the five shift registers. The location from within that register is set using the locations table.

For instance, running the following will set crow’s 3rd and 4th outputs to read from different locations within the quantized analog voltage register:

modes[3] = 'quantizedAnalog'
modes[4]='quantizedAnalog'
locations[3] = 1
locations[4] = 2

Setting an output’s mode to 'none' will result in the output being freed to be used in another manner:

modes[1] = 'none'
mode register info
'trig' rhythm setting an output to this mode results in a 10ms trigger pulse generated whenever the readlocation of the output in the rhythm register is a 1. the trigger’s level is determined by the output’s level key - e.g. output[1].level = 5 results in 5V trigs if modes[1] = 'trig'
'gate' rhythm setting an output to this mode results in a gate which remains high whenever the read location in the rhythm register is a 1. the gate’s level is determined by the output’s level key - e.g. output[1].level = 5 results in 5V gates if modes[1] = 'gate'
'envelope' rhythm setting an output to this mode results in an envelope being generated out the corresponding output whenever the read location in the rhythm register is a 1. the envelope’s level is determined by the output’s level key - e.g. output[1].level = 5 results in 5V gates if modes[1] = 'envelope'. the output’s slew time determines the decay time on the envelope: output[1].slew = 0.5 would set a 500ms decay time.
'weightedBitSum' weightedBitSum setting an output to this mode results in an analog voltage being generated as a weighted bit sum of several bits from the weightedBitSum register. the weights are set in the weights table. the output slew time works as expected.
'quantizedWeightedBitSum' quantizedWeightedBitSum setting an output to this mode results in an analog voltage being generated as a weighted bit sum of several bits from the quantizedWeightedBitSum register. the weights are set in the weights table. the output slew time works as expected. the voltages are quantized into the scale set by the scale table.
'analog' analog setting an output to this mode results in an analog voltage being generated. the output slew time works as expected.
'quantizedAnalog' quantizedAnalog setting an output to this mode results in an analog voltage being generated. the output slew time works as expected. the voltages are quantized into the scale set by the scale table.
'velocityEnvelope' velocityEnvelope setting an output to this mode results in an envelope being generated. the value of the read location in the velocityEnvelope register is used to set the amplitude of the envelope 0 corresponds to 0V amplitude, 10 corresponds to 10V. changing the velocityEnvleope.min and velocityEnvleope.max keys will then let you set the range of allowed envelope amplitudes. decay time is controlled by the output’s slew time.

Advanced

info

You can reconfigure how outputs assigned to each mode generate their voltages by redefining the functions stored in the action table. for instance, to change how outputs set to the 'analog' mode generate voltage, you could redefine actions.analog:

actions.analog = function(outputIndex) 
    return lfo(output[outputIndex].rate,analog.register[locations[outputIndex]])
end

You can also create new shift register tables at run time using the newRegister(name,length,digitalOrAnalog,minVal,maxVal) helper function to generate a table. Store the table in a variable with a name of your choice. E.g.:

myRegister = newRegister('voidstarReg',16,'digital',0,1)

Whenever you want to “process them”, pass them to processShiftRegisters(register,cv).

You can then read the values stored from <varName>.register[location].

Code

ornithopters.lua (5.2 KB)

NB: it requires the latest version of druid installed and the most recent crow firmware installed.

29 Likes

this is incredible

honestly dont know if you are ok with the idea (or if it defeats the purpose of this thread…) but can it have it’s own thread in library?

seems pretty advanced and also has pending updates etc

if you do, mabye titling as ^^ornithopters will make druid scripts easy to find among the other apps

tagging would also work

5 Likes

FWIW, I think that all scripts (crow, teletype, max, etc) should be in the library, and tagged appropriately. As a user, this makes the job of finding things and finding things that need updating much much easier. Maybe keep this thread for testing and discussion of crow druid scripts? Just an idea. Thanks!

4 Likes

I’m getting ready to propose an Index idea, which this would be very appropriate for…

2 Likes

Excellent. I’d also like a simple way to find out what scriipts/applications/etc have been updated so I know to download them. Sorting by latest in the library kind of gets you there, but most of the time the thread is updated due to a discussion and not an actual update. I was going to propose 2 threads for each, 1 being in the library and only updated by the creator when the applicable script or program is updated, and another for discussion, open to all. Maybe what you are proposing would cover this functionality somehow? Thanks @eblomquist

I like the current format of using git to store the script and a library thread to demo and discuss it. Personally, I’m not in favor of 2 threads for each. Another way you could stay up to date with scripts in this scenario is following users on github, which would allow you to avoid the discussion in a library thread if you really wanted to.

4 Likes

That is a helpful observation.

As a still relative newb, my proposal will be in part an invitation to others to join in a discussion of possible paths towards an index that would be deeply respectful of existing resources…

My primary observation is that there is a massive amount of great information already here but it can be difficult to navigate…

An index could aggregate links to topics, which could inspire more experienced members to hopefully review the accessibility of the specific topical resources…

3 Likes

Made a little sequencer that’s intended to be live-coded via druid. It creates a random melody and then provides the user with a few helper functions you can invoke from druid to change the sequence. Functions and inputs/outputs are sort of documented in the code.

I have other ideas for it but mostly did it as a proof-of-concept. Then again if anyone has suggestions I’m all ears!

Example usage within druid

– set step 4 slew to 1
slew(4, 1)
– transpose whole sequence up an octave
oct()
– transpose steps 5-6 down 1 octave
oct(-1, 5, 6)
– set step 6 envelope to ASL function
eg(6, adsr())
– duplicate sequence
cp()
– reverse steps 9-16
rev(9, 16)
– transpose steps 5-9 up 7 semitones
tp(7, 5, 9)
– burn it all, randomize notes and start over
rndm()

It’s also fun without the live coding if you send a trigger or lfo into input 2 to change up the random melody

livecodesequencer.lua (2.6 KB)

14 Likes