^^ crow strategies: Just Friends

since the endless possibilities of using crow to play/program Just Friends over i2c seems to be a likely first use for many who will be receiving their crows this week, I wanted to kickstart convo around best practices + how to get the most out of this pairing.

though I’ll share some of my approaches to get things started, they do not represent the best approaches. I want to learn from you all, as I’m still wrapping my head around things. so, this will hopefully serve beginners and experienced artists alike :slight_smile:

destination: all the notes at the same time

while building the ^^jf_synth device for crow’s Max/Max for Live toolkit, I ran into a funny thing – Just Friends is a 6 voice polyphonic synth, but in transient/sound it sometimes loses track of pitch data when it receives simultaneous messages over i2c. this happened with Teletype as well, but it becomes very obvious when you plug a MIDI keyboard into a Max patch and make 6-note chord shapes.

I found that if I spaced out the message delivery between crow and Just Friends by ~10 milliseconds, I got perceivable polyphony with no dropped notes or weird pitches.

approach

working around this behavior seems to be as good a starting point as any for this thread, so here’s two ways I approached scripting to successfully get (close enough for jazz/ambient/garage rock) simultaneous notes playing out of Just Friends:

Max

if you crack open the [crow] object’s help patcher, you’ll find an i2c tab with this patch:

on the right, you’ll find a [zl.queue] collecting every note event from the [kslider] (keyboard keys) + [midiin] combo as a list. when [zl.queue] receives a bang message in it’s left inlet, it pushes the first message it received through its outlet to crow and removes it from the list – First In, First Out (FIFO).

@Rodrigo came up with a better solution!

this gives Just Friends a bit of breathing room between note messages, to parse them out and allocate accordingly.

norns

I took a similar approach with norns, while updating less concepts. less concepts only sends 2 notes at a time, but often simultaneously.

in my init(), I establish a 10ms clock called jf_note_spacer to replicate the [metro 10] object from Max:

jf_note_spacer = metro.init()
jf_note_spacer.time = 0.01
jf_note_spacer.count = 1

I chose to have it fire only once, whenever it’s activated. I then set up a function for it to trigger:
jf_note_spacer.event = jf_spacer

I also made sure that this metro wouldn’t run at startup by initializing it in a stop state:
jf_note_spacer:stop()

so what is jf_spacer()? it’s just a single crow command to handle the second note:
crow.ii.jf.play_note(((BLAH-BLAH-NOTE-MATHS)/12,5)

and it all comes together in my note handling function:

if i == 1 then
     crow.ii.jf.play_note((BLAH-BLAH-NOTE-MATHS)/12,5)
elseif i == 2 then
     jf_note_spacer:start()
end

so:

  • if the note is for the first voice in the pair, that can be played straightaway
  • if the note is for the second voice in the pair, start that 10ms timer
  • at the end of the 10ms timer, crow will send JF the data for that second voice

but it all sounds like simultaneous events!!

what next?

  • lol, can someone plz come up with a better way to manage polyphonic messaging to JF in norns?
  • as folks start to dive into Just Friends + crow, I’d love to learn from your approaches! as you get coding, what do you find works best to juice the most out of this pair?
  • ask questions! share answers! be excellent to each other.
30 Likes

Dan - you are my hero. This is what I’ve been hoping for in terms of making this fabbo new tool accessible. Well done.

5 Likes

Hmm… I wonder if it would be possible to wrap this kind of logic in a reusable library so this pattern doesn’t have to be copied for every script that wants JF poly functionality.

Maybe each “play_note” command could be pushed to a table that acts kind of like an “event queue” where each function invocation is delayed by 10ms. I’m not sure if this would work well, I’m sort of thinking out loud…

1 Like

This, over and over. You’re amazing Dan!!

1 Like

this is exactly what I was hoping would come out of the convo! yeah, a standard function that just enqueues and bangs out any message sent to JF from norns through crow – that’d be awesome! I’m sure some folks have ideas around this…just gotta wait out those deliveries :sweat_smile:

(@kali_yuga + @bobbcorr, just doin my job and tryin to help :hugs:)

2 Likes

I assume yes, but: will JF be addressible from Druid? I am currently Norns-less and a lot of my initial exploration of Crow will be through Druid.

yes! all the WIP syntax bits should work across all platforms – druid, norns, max

2 Likes

This is so awesome. Would crow be able to convert cv/gate in from Marbles/Rene etc to ii messages for JF? Would it be able to do that only with Druid, without norns attached?

1 Like

I don’t have druid experience but this sounds totally do-able! I feel like you’d just need a script that sorta does:

  • tell crow what type of polling to do on its inputs – stream? threshold? rising + falling?
  • tell crow how you want to report on those polls: trigger on the other input or a metro or when voltage is less than x?
  • tell crow to connect to JF
  • tell crow to scale the incoming voltage to a batch of notes
  • tell crow to send those notes to JF

then save it onto crow in standalone.

please share what you’re able to do – I’m so curious about integrating something like marbles into the equation!

6 Likes

Is there a sound demo of Just Friends used in 6 voices polyphonic mode somewhere please ? (re-reading, I understand this is a work in progress, maybe in a near future :- )

1 Like

@Dan_Derks & @germinal wow. I never even thought of something like this.

Dan, thanks for taking the time in (all) of the crow threads to give great specific information in regards to initial and future integration with crow & other devices. Excited to place my order soon for it & a just friends!

1 Like

The new crow videos shows all the voices being used, the first one is sequenced by Awake on Norns and near the end with the arpeggios played by the standalone script.

1 Like

I know I posted this once before but couldn’t find it, sorry for spamming, but this is all Just Friends controlled via Teletype, sequenced by Kria.

2 Likes

Thanks @Jonny and @kbit ! :wink:

Although I have no crow to test with, you can improve your zl queue approach by using something like this:

Max Patcher

----------begin_max5_patcher----------
2023.3oc2Z87bqhiD9ryeETbc83RBPBXp4xt6g2o8zbXpsRMkKrQ1QumLhED
uj7lZ9ee0O.r.C1XhsyTSNPHcift+zW2sjR+GOsvcC+MRoqyO67ryhE+wSKV
nEoDrn9uW3dH4ssrjR8i4lQdku4qtKMpDj2DZw4EzLQizrpC7JAiHzCATKMO
Qr8EZ190EjsBymL.tBrzAFDp9kum5pm7pyue7EQyZdOvZgzT8WTZE+TfqRze
9zSpKKmnGbfTVlrmbhKTKuboifuzYaA+0g8mF6vHR7dNw3Ltts18ntpef1gg
WvU8N0U8miqN1jkyFogSJV8C1p+WEohb6czXe80vqdJE5Z+pIE0dWs6svcGk
Q9NonjxyrdCKbSxysDuvZHJL4qb8KJZYqHZlQDrUTA46zlwCZklTHcPgz6pJ
ztu6a3.2iuFdJoHqhpeSFgxYmZSROOjkH4U4IaMCVMc0n9HjEh0bCn9WAg.0
uPwVnlbddOiu8ajTaiykmSxnY4EjRRlHQzy1cSI6RpXh063YhR5OzVfl4Mf9
c0V3fJU9f17+mEzDVqCruflxyTFQmYBk3lO2yNPjlwircF8SjkjOvfkT.IrL
hxRoSVUtIoPMQsgQrBUjraNm0UU63XjchZ04zrrdnnfmOtxB59WNyX2vkJOb
t2sVS45pLi10RNgXcYx26h1hDFqNXs6q+sjL5gDAQPMSAdfVkjrDoi9RoLSE
i0weMZ99.ZRkb7sjWoohWzeHaxf7wo4MjH21Y4TpLmnnqLQx9xtRJEua.cKQ
UapigWKHGxYRun6Czo1ic.qcZsNx6kdaK+vARaYmgylKSl670c1OxfUmFKaF
LVmw1ORcMRGf5Ar3xikRqeZMjaiXcJ75DE2A+9C5tnXcxGOc5aunY6u9OJ+k
w44NhWJ3U6ewQWLyoJSPYNRJm3c6gvnYjsboR6zFWMB46qSOGfBWAAQvnf35
e7jY4.lD49y.vhAOJDqpjTCTRtRofWPbjYebnYxAI8UmlHmQwN+OJ1A7WECB
gXLv7CLRgcHS0uYfcQQOJrKkvRduEgb17tztOTd2XY93fUg.LL.0vxjqoJd1
brH7iBm1xyjqFSnYXueXCmot6aDRtiUd48DgZAd2bZlmd009xHz9XGDDOeRV
viB7n6bRZvIG4CJAKZlSImmQJjo5RxjpUzN48Do7WoRDvYup75XPYvrgx.cI
.nrFfG.B7vMXIVsKFMXhmS4gHuaIX1YaN8JG5rwgMd4P6.zS1lyljr8tKs1t
yEpZ56IWq..A8BaxqIQISfr8VflLHEF7f.oevbr2F30hSSCiLaqIPtImf949
Q3KiQdifQv+VQjpAIr2Jb+EW7AHR3nGEHwMY6GGmfmkHMQ7wOTkNpapcSH37
fmvGD7XV8.DLS3QSitDDUmwdPHJX1gY3GUX1+vANSzQc9iWDb.lfHenL9xGD
50.NHov3UnYhNA3GVRHlylOPV5IwfpqlYNAO77y5DfdPnhZoONdmi3bCJe4Y
BdBCWEAjEvZJwCB+PwUAOpR7+hLsyp6ejELBIKu6AP9MQV9pcwLa7A9vJuSO
zcqHWAAZGimHlDKpAk7i0KmFF2vhT02Cm+hftkfj9qc2JfiZ28ZyJZ7O69G.
ibZIcB1a1L04O2OZVJ4MqCp+lfVFHX9m2lIsBBFMU.YriOxaL.45vgiGRuZC
jib5rZGQoeX.pjWUrsgRTu9SIouiOkRJEzr1+mEOezKb557a4Ly+nlmc.qBj
ZWoNrHcTjBllzT4UauvSLigs2P34sWvRqKvdSqGno4bYt0ZP1CEpV.h5vwh.
XbHnICpzoggwin6dg.QSDAv2PDnwKiiFX2OvvKoScfO8WxaMndpp6DtI2x9m
.tgiZbRL.g8ZXGQnN.POc2KDXh..7r..b..3tXtvoFpKWi+ksWjrjNFd2M5f
fIlOc.53mQ9zfoFUbo7+fkVLi6n8hlJo3zIhOM6cR7gKZtimzgWjZ5FCvmuO
.9n9.793C3qg2b8ISNyhG.lSu.ij047QQMGrQDTsaC+QzcuV7P3MZ4SOnzSg
SsFv.qJ5SwdmZ5eb3eIr23IWiEcCR+WKroMrbU8ZS5ZSeEsNQHJnapDlc1X2
WYWU+urmw2jvp6tk1tiZf1i4oiFWcq6o8ttatxzIe8Q05946zUrXiQiAjlmY
vNFbpeG+I7cfm7cLtWulwS8I50Dd8Z.uSa9twa7t9Mcmdl1z.R8fzi8sVUJk
+q5tGa8+gjUYloZ5wst.wl86nLVKuylizbRG0rx197yli5EG.U+Os.q7g9gd
X8cxaPH6XVyXfsAhnfX0Y.IuC6EFfz2E44i70j8dCy632RUhP+E.wQf.yclp
F1ELbSx1W2sbgVs7UdAOmWz11fq7iae9JAeeQRJs9PD.c3wKq4QERs866K2C
R.k1LFadW6jw2JYT4D3uolcO2DQ+JhctzP59f1h4DA+WrJRSimNrsjrcq7Cz
YhVtqSbPnBxwsydwJQv6fAVveM6psvi1ERx+Bh0zi6jE9ueO45MPfWLJPyeC
C888zfYjGDB8u8F3WJHjYXgZzyW8i5N4NswJy7Vab+WBiwe87VmcW79rtOdQ
w.PTaF5Ar9HODTCpgQvHyJIQRDGcScfpCaHEegyRuZvUQEA5oeTrGDosTnc5
qahAVXR2nrvym1+5rskOcZteb.F6GWyhCCM7YIdGOe9rod5HcR9S+4S+ey5P
NyC
-----------end_max5_patcher-----------

In your version you are staggering everything received by 10ms, which also means that any message sent can be up to 10ms late (if you just missed the metro bang). Also having a constantly running metro 10 in your patch can maybe jam up the scheduler, particularly if you have lots of other metros at play.

The patch above will send any message received, instantly. IF it receives a message within 10ms of the last message, it will then add it to zl queue and send it after 10ms. This gives you instant response time and note staggering when you’re dealing with polyphony.

Also sprintf is quite programmer-y and oldschool. Maybe something like combine would be better, especially if you put the. message formatting after the zl queue.

8 Likes

dang, this is gold, thank you so much rod!!!

implemented into ^^jf_synth and the help patcher!

agreed, but am having a hard time wrapping my head around it. will explore. heck, folks are gettin’ crows today, maybe someone else will <33333

I started whipping something up, but didn’t know how critical the %.2f thing was in terms of what crow is expecting. If it matters matters you can just use round 0.01 before the combine object to get the same results. The , is weirder/harder, in which case sprintf may be a good way to go.

Hello All,
I got my Crow plugged in tonight and connected to a JF via ii. Exciting!

I’m trying to wrap my head around how I can script the various Just Type modes to JF using Crow instead of Teletype (which I don’t have). I assume everything that can be done via Teletype can be done via Crow… Is that correct?

In comparing the messages sent to Crow from the Max and from the Just Type documentation there appears to be a formatting difference that I’m trying to understand.

For instance, in the Just Type documentation what’s written as:
“JF.NOTE pitch velocity” becomes:
“ii.jf.play_note(NOTE, VELOCITY.)” in Max.
So it looks like we need to prepend our messages with “ii.jf.play”?

I’m specifically interested in playing around with the Geode mode, so would the formatting be: ii.jf.play_note(NOTE, VELOCITY, DIVISION, REPEATS.)"?

Are these difference documented anywhere?

Thanks so much!
B

PS: Also, would these formatting differences be the same in Druid? Or is the change Max specific?

1 Like

In Max, you use the exact same command as you would in druid - the only difference is you wrap it in quotes to make sure they are a single symbol, and prepend that symbol with “tell_crow”.

In druid:
output[1].volts = 3

In max:
tell_crow “output[1].volts = 3”

The quotes are crucial here because they make it so that the Lua code is treated a single chunk.

Typically, the I use the sprintf object with the symout argument to format my Lua chunks and turn them into a single symbol, followed by a prepend object to tack on the tell_crow.

Mobile right now but can post a max code snippet as a demo tomorrow! The help file should have a lot of this already though. The max for live devices would also be good references.

In synthesis, (range switch set to sound range), the function is:
ii.jf.play_note(pitch,vel)

In geode, (range switch set to shape range), the function is:
ii.jf.play_note(repeats,division)

geode does not make sound, just cv slopes.

The two functions are equivalent to teletype with the small exception of scaling for pitch and velocity in synthesis - remember on teletype you need to use a lookup to set the velocity (N 3 instead of 3 or V 5 instead of 5), whereas on crow I believe you just pass midi note and voltage level though you should confirm that for yourself since I’m not at my case!

7 Likes

This is sooo helpful. Thank you!!
And thanks for the clarification on the Geode mode. I realize that I was a bit confused on that.

EDIT: One of the things that’s still confusing me is the added “play” formatting in Max that doesn’t appear in the Just Type documentation. “JF.NOTE pitch velocity” becomes “ii.jf.play_note(pitch,vel)”.

It would be fine to follow that but it gets confusing for some of the other commands, like “JF.TICK”. What should this become in Max? I’ve tried “ii.jf.tick(BPM)”, “ii.jf.play_tick(BPM)”, “ii.jf.tick_BPM” and “ii.jf.tick BPM”. None of them seem to work as expected (though as I’m new to this mode it is possible I’m mistaken on that).

Thanks again!
Ben