# ^^ crow strategies: Just Friends

Oke so small storytime here
I’m using a programmable midi extender for yarns where i have programmed in a “chord” mode origanaly for Rings. (midi device: https://forum.mutable-instruments.net/t/needles-1u-midi-controller-yarns-companion/12656) It’s basicly a chrod mode which strumms through the notes you hit. So something like: C smalldelaywithnothing E smalldelaywithnothing F smalldelay and then shut of because it’s the last note.

I loved this so much that i bought a Crow for it to make the same thing work with just friends (with added vibrato support from yarns ) But now im looking to implement something on the geode side. Is there any idea to make geode like a playable thing?

For anyone who is interested in my code:

Code
``````-- JF with transpose and first combined

function delay(time, action)
local dmetro = metro.init(devent, time, 1)
local count = 0
dmetro:start()
function devent()
count = count + 1
if count == 2 then
action()
metro.free(dmetro.id)
end
end
end

length = {}
rhythm = {}
notes  = {}
step   = {}
bits   = {}
scale  = { {0,2,4,7,9}, {0,2,4,5,7,9,11} }
decay  = 0.4
attack = 2

function set_d(t) decay = (t-1)/8 + 0.05 end
function set_a(t) attack = (t-1)/64 + 0.003 end

function play(out,ix)
if rhythm[ix][ step[ix] ] & 8 == 8 then
if ix == 1 then set_d(t[ix]) else set_a(t[ix]) end
t[ix] = 0
output[out+1]()
end
if rhythm[ix+2][ step[ix+2] ] & 8 == 8 then
n1 = notes[ix][ step[ix+2] ]
n2 = notes[ix][ step[ix] ]
abs = math.abs(input[2].volts)/5
note = n1 + abs*(n2-n1)
note = math.floor(note * (abs*3 + 0.1))
s = scale[input[2].volts > -0.04 and 1 or 2]
nn = s[ note%(#s) + 1 ]
oct = math.floor(note/12)
output[out].volts = nn/12 + oct
end
step[ix] = (step[ix] % length[ix]) + 1
step[ix+2] = (step[ix+2] % length[ix+2]) + 1
end

sd = 0
function lcg(seed)
local s = seed or sd
sd = (1103515245*s + 12345) % (1<<31)
return sd
end

function get_d() return decay end
function get_a() return attack end

t = {0,0}
function env(count)
for i=1,2 do t[i] = t[i] + 1 end
end

function trans(count)
transpose = input[2].volts - lastNote
if (transpose > 0.008 or transpose < -0.008) then
ii.jf.transpose(transpose - 2)
else
ii.jf.transpose(-2)
end
end

input[1].change = function(s)
getters()

if speed > 0 and tsc == 1.0 then
tmetro:start()
ii.jf.mode(1)
lastNote = input[2].volts
noteCount = noteCount + 1
delay( 0.003, function() ii.jf.play_note(input[2].volts,5) lastNote = input[2].volts end )
elseif speed > 0 and noteCount > 5 then
lastNote = input[2].volts
delay( 0.003, function() ii.jf.play_note(input[2].volts,5) lastNote = input[2].volts end )
else
noteCount = 0
ii.jf.mode(0)
tmetro:stop()
end

play(1,1)
play(3,2)
end

function getters()
ii.jf.get('speed')
ii.jf.get('tsc')

ii.jf.event = function(e, value)
if e.name == 'speed' then
speed = value
elseif e.name == 'tsc' then
tsc = value
end
end
end

function init()
getters()
lastNote = 0
noteCount = 0
input[1].mode('change', 1, 0.1, 'rising')
lcg(unique_id())
lcg()
lcg()
lcg()

for i=1,4 do
length[i] = lcg()%19 + 6
rhythm[i] = {}
for n=1,32 do
rhythm[i][n] = lcg()
end
step[i] = 1
end

-- notes
for i=1,4 do
notes[i] = {0}
for n=1,31 do
notes[i][n+1] = notes[i][n] + (lcg() % 7) -3
end
end

-- out params
output[1].slew   = 0
output[1].volts  = 0
output[2].action = ar(get_a,get_d)
output[3].slew   = 0.01
output[3].volts  = 0
output[4].action = ar(get_a,get_d)

-- start sequence!
dec = metro.init{ event = env, time = 0.1 }
dec:start()

tmetro = metro.init{ event = trans, time = 0.03 }
end
``````

Note the outputs acts as the First script so when you aren’t playing Just friends synth mode you can use the First script.

Is there a reason why i cant use the delay function when im using a metro function on the fm knob? i want to read out the fm knob position on just friends but once i’m playing the just friends in synth mode and turn the fm knob it’s getting really wonky. So not fm wonky but it sometimes hits 2 notes are all notes.

Yesterday I tried for the first time ^^JFsynth on Max for Live, but my JF hangs after a couple of notes sent from Ableton…
it could be the ii cable is too long (35cm)?
Or it’s something else?
Today I’ll try with Teletype&JF, let’s see what’ll happens…

Hey y’all, I’m having an issue:

After updating Crow and Just Friends to their newest firmwares, Crow doesn’t seem to talk to JF at all. the jf.get functions all return ‘nil’ and things like jf.mode don’t appear to do anything.

Any thoughts?

I’m currently scripting through maiden, but the results appear to be the same when using druid.

edit: Literally have no idea what I did, but when I moved the two modules to my skiff by themselves, it started working. God damn it.

edit: Unfortunately it stopped working again almost immediately. Weird stuff.

Had the same, and outlined how I fixed it in a post earlier, which I can’t seem to find now (deleted?), anyway, I followed these calibration/factory reset procedures at the bottom of this page: JF: Latest Version – WHIMSICAL RAPS

also, good call re: calibration, forgot that was a solution for that specific trouble. @zbs i also emailed you so lmk how it all goes!

2 Likes

Thank very much, and @dan_derks too! After that calibration it’s at least temporarily working. I’ll let you know if it poops out again. Thank you <3

edit: ya it broke again almost immediately Very weird stuff.

edit: @dan_derks , i did try reseating the i2c cable and i tried a different one to no apparent avail.

edit: Crow isn’t reading faders, disting EX or just friends in any circumstance I’m trying. There has to be something stupid I’m missing here?

1 Like

Just beginning with Crow / Just Friends. I have both modules updated and connected via i2c. They work as expected when my laptop is connected. For instance uploading and running a script using JF a polyphonic synth. I disconnect laptop and they continue to work. But if I power down the entire modular, they are disconnected when I turn back on later. I thought the whole point of Crow is that it automatically runs the uploaded script. Do I have to connect my laptop every time to run this patch or am I missing something in the work flow?

Here’s the script —

— just friends poly sequencer

function init()

metro[1].event = n

metro[1].time = 0.9

metro[1]:start()

metro[2].event = n2

metro[2].time = 0.7

metro[2]:start()

output[2].action = ar(0.03,2.7)

end

lydian = {0,2,4,6,7,9,11}

penta = {0,2,4,7,9}

penta2 = {0,2,4,6,7,9,11}

scale_ix = 1

scale_ix2 = 1

scale = lydian

level = 3

function n()

for n=1,2 do

``````scale_ix = (scale_ix + 3) % #penta +1

local note = penta[scale_ix]/12

ii.jf.play_voice( n, note, level )
``````

end

end

function n2()

for n=1,3 do

``````scale_ix2 = (scale_ix2 + 2) % #penta2 +1

local note2 = penta2[scale_ix2]/12

ii.jf.play_voice( n+1, note2, level )
``````

end

end

you need to add `ii.jf.mode(1)` to your `init` function. this tells just friends to enter synth mode which is required for the `ii.jf.play_note` calls to work.

then in druid, make sure you upload your script with `u your_script_name.lua`.

nb: when you’re working on the script we suggest using `r your_script_name.lua` as the “run” command is faster than “upload”, and doesn’t write to the flash (hence reduces wear on the memory). then just run `u` (no name needed) at the end of a session to freeze it to the module.

7 Likes

hey all. im just starting to dip my toes into i2c/JF/m4l. writing midi in ableton and sending to JF as a polysynth is a dream

that said i think every session i’ve had i’ve had a crash. seems to happen when i’m making microedits or adding to a midi sequence most. JF stops emitting sound despite still showing up as connected. the only way i’ve gotten it to
start back up is to power cycle my rack and delete/re-create command and JFsynth. obviously this isn’t sustainable… has anyone else experienced this and found a solve? no issues so far with crow/norns/JF i2c… just with the m4l stuff. thanks for any help!

works perfectly for me with crow + m4l. what version of ableton are you on?

1 Like
1. haven’t updated to 11.1 yet but will do so this week

hmm, have been reports of crashes with m4l devices and ableton 11 but not heared of troubles with the crow pack…

I’m trying to query the JF voices to see which one is free.
According to the Just Type help page, I should be able to do something like the following to see if, when the strum() function is called by the clock, the channel (in this case 1) is free or not. Whatever I do, it keeps repeating that it’s true in the rate of the clock. Maybe I’m not getting something- how does this work?

``````function strum()
while true do

clock.sync(1/2)

-- check if voice 1 is free
channel = 1
if crow.ii.jf.get('trigger', channel) == True then
print("true!")
else
print("false!")
end

note = math.random(#notes[1])
crow.ii.jf.pitch(math.random(6),(notes[noteSet][note]/12-1)+tune)
crow.ii.jf.trigger(math.random(6),1)

end
end

crow.ii.jf.event = function( e, value )
if e.name == 'trigger' then
-- handle trigger response
-- e.arg: first argument, ie channel
-- e.device: index of device
print(e.arg)
end
end
``````

The problem (or at least one of) is that `crow.ii.jf.get(...)` doesn’t return anything! It triggers an asynchronous event that is received in the `crow.ii.jf.event()` handler (which you have correctly configured). Where you are printing `print(e.arg)` in that handler, try adding the value so you can see what it’s actually reporting: `print(e.arg, value)`.

Secondly, I think that in lua truthy is `true`, not `True` though I could be wrong and it has an upper-case alias (regardless, changing that won’t fix this case).

I think that when you `get` the `'trigger'`, it will return `1` or `0` (rather than `true` and `false`) though I’d confirm that by looking at the print in the `event` handler.

Curious why you’re trying to inspect the voice allocator (though it’s cool that you can! i didn’t conceive of that as a use-case when getting `trigger` states). I always assumed people would use the in-built `play_note` allocator, or make their own custom allocator and use `play_voice` instead.

1 Like

Thank you, it seems your assumptions are right, on true being lowercase and trigger not returning true/false…
though `value` always returns 1 for me, even if I make sure it is playing or not by commenting out the triggering of the voice. Any idea why? This is my code:

``````function strum()
while true do
clock.sync(1/8)

-- check if voice 1 is free
channel = 1
crow.ii.jf.get('trigger', channel)

note = math.random(#notes[1])
crow.ii.jf.pitch(math.random(6),(notes[noteSet][note]/12-1)+tune)
--crow.ii.jf.trigger(math.random(6),1)

end
end

crow.ii.jf.event = function( e, value )
if e.name == 'trigger' then
-- handle trigger response
-- e.arg: first argument, ie channel
-- e.device: index of device
print(e.arg, value)
end
end
``````

As for why I’m trying to inspect the voice allocator- after making my Tidbit script I had this (still vague) idea of doing a slow version. Vague idea incoming:
It would have slow moving notes that would never suddenly change pitch while they are playing (so no triggering notes that are playing, hence the need for this). It would however have gradual/glissandi-like pitch changes during its AR envelope (is this possible at all?). It would be called HIKE and would look like six mountains on Norns display, and some animation would indicate the pitch and the AR envelope form. Of course this would be influenced by twiddling TIME. Anyway the idea is not complete yet, but I have a hunch this could be interesting.

EDIT: simpler said- this would allow you to play notes only when there is one voice free- making the TIME knob also responsible for the rate new notes are let in- and making sure there are not sudden pitch changes while envelopes are going.

1 Like

sounds really interesting!

i think the issue is the way `get 'trigger'` is implemented might mean it’s always 1 when in synthesis mode. the reason being, `trigger` returns whether the channel is in motion – i don’t think this variable tracks whether the volume has hit zero, so when the oscillator goes silent it is still oscillating (just being muted), like in an analogue oscillator.

for your idea of the frequency of notes being played being relatively to the TIME knob, you could just poll the value of time with `crow.ii.jf.get 'time'` (receiving the value in the `jf.event` handler), and control a norns `clock` which triggers the notes. i’d use `jf.play_voice` and build a simple round-robin allocator – it’s just a wrapping counter from 1 to 6.

regarding the gliss idea, there is no in-built support for this (JF is already at the limit of processing power in synthesis mode). the way i’d suggest to try it is with something like the `fnl` function i made for a maps stream (ep3.lua · GitHub) – it lets you continuously sweep values over i2c with a given framerate. you’ll probably need to experiment with the speed, and interleaving the oscillators to find something stable though (as the technique can easily saturate the i2c bus).

4 Likes

Thank you so much! This is great and it’s wonderful to hear a bit about how things are implemented! A lot to unpack and to experiment with here… I’ll try some stuff out Thanks!

I’ve been using a crow script running Just Friends as a synthesizer. After I change to a different script in Crow, something unrelated to JF, JF continues to run in the same mode. I’m not sure how to get it back to it’s normal operation. Is there an ii.jf___ command to quit?

ii.jf.mode(0) should switch it. 1 is synth mode, 0 is standard

2 Likes