^^ crow strategies: Just Friends

added the sprintf bits to the scripting part of the FAQ!

example code snippet
<pre><code>
----------begin_max5_patcher----------
2443.3oc4as0iiaaE94Y9UHHf7lWCdQjTL.8grca2VzcSCxNEKBRVLP1l1Qy
HKYnKykFj7au7hjrrsrtXyYV214AOTzjhmqemCOj92t9J2YIOIxbc9Vme14p
q9squ5JcWpNtp74qbWG7z7nfL8vbmmrdsHN2ch46xEOkq6W7Tv5MQBmjkNKS
RWGjmKV3rVjkErRTM3vE5glL6t2.YUcFWrNLNRjqe8vsclTjW0Knr2MA4y+0
v3U2lJlmaHZLFNkQl3fA7ofINPFV8ODXJv4KpI86Wes5iICj61ifq4tbQTzs
ySSdz4WbkD1lh7eF9koOjDkm47mb7lxP+haabIoUtD0JWB6fKQzoRlD4SzLo
OV+DRwjkSx7dxedivLCW2Sh+Ol1UwoGQQhsjhzyWwTTfhC8XmkVbYThbcaiX
gt0TPZvZQtH8VQbvrHQSpyX+p5gNbVC0EqoYGtl0H5O6Q2MwwcVP7J6pCM1s
N+820hfgaGkHjosSg.scJ47zhwhGkj1ArgxKrENXL3I3N3.ei9w3o4MFk0jS
0o6HL5lTwFQ7BmZrmV3Z5H3ZX+bsgeg.7.Xaaxp4NybV1B6QbsoCHDqUpHd+
nmUJTIRhBI3jX0h0yDosvT3gfAcdLbo9rjge4gbNhZMaSZXb9RmrmUz81Pme
S31XmeyTzxVich.cF6bYRbdV3+VS+PIqU187jnjTCOAlxo.luGFhYHhGehrG
eDyi.39.LGAILUWPB0mtsaoTqgbpcOH0ZGKUcZZ86RCChb6WWvMFeX5X8szi
yMJLd+D0zKlp+c0LYIEoyqdUkfDNaWsEhr7v3f7vj3FiQkohCrdPqCWrIQp6
xp3AiOiI6JB.T+zVlHcgPK3AsZ+LTZUEgtWhkXFyYsPCQnv1YL0bH7rVXkcc
uqL0Bb3tZz1WGMwbT0NlZvMMVvd.Svc+iGPeLjFXHjF3njFYKwLox9zFjF9r
IsJhgvaRZpmZwsNXylGDoYkueMUIgVuyfg4OQ+XXr4QM7iap3gvpwqQ8bCRk
XN4R.mhTCjzSTOWyTSjlrwEgkVsR4gbIyxeNZenjsnYEKBS9TdPdQ1seTDWX
.1jxgkAEQ46J6lsZYXTTMh6U0eiLDPIXVId7jxt2AcFw8fPeEDrFglpaofpI
MLIKmCrZRdRfb.RMTOMnstkOBSvpVf8lFZ6ZAfbyJnf48LsjcA2AuWJKiWYB
DiXfpfJRimzjMIoUVAxoxqGeQdxpzfEgpLtqP+LpXi31DmW9sZwtVlTJSWKE
ngUyoooZsx3CEyC5RADLet7EuiPEWEoCqCnoaUF8qYbscliGiHyTXhVLg84l
VDHft2jDQh06udLeJkomgO.56aZIWZDZ24Na0tQlAdPlIhLm3wXlog3lWEgV
OulgZkde+SYFwNeJHNy4Sh0gyRhj4KTMzLI8MWoi1k.QR9PKGfTFxXk3C.Pu
coOUNK54caXrxgTTa4nxTfZDIUldHcVE6yf86LTanbD+gAZitqGgesefOCP7
8MsNf.22ifVYdPU+wLsLuoKWGhOFjm2kCQyjBk.wM.Dze0xfJndXET7YRP2j
JsEMytK5p15ZrVU6wPMyZrEm+g9VGgqHyqS2evjFezg+dW11xVjc7CuqPFzc
Y37FgcgGCP3GKlEdua+twcPnek7awLpGyv9JMjdZd6JMFSTLo6K6B0q8y27W
GgSqe213DfTto0iTBFZ1AGDAQcEfCp2AnNxhdmeZC6pLM5I.mGEBAlzL3Hpm
V5S3PIBamF7PJVpd0TGyix4ks.7C8uaXM+2ddQZxJQ7MZKuuzbSkpTW2yYWx
U53YR1mPMA8UA.zrG+Bv.ePoh7+FYpEL8dwKSlZ6YWMrv16aW8cwKBhDNeLI
Nou7kL0LotVIv5RjfObE1FOEziwZWuUswpUzB5BRENe3fM7twZ7JqHTasNd1
zCbR6oX8wblI2MLWtwnxVLBtSE66EwhGBNm3esquNqvksrmO33XySFTXeQZY
5PG15jfRrIhQos5aKxykKPGVrMXInQk3WKM.ksfaqsfcHq2UWSyKHh58Agw+
w.oJUVUUg5Ork8IsrGCk9QiJD.rz2Z6GuHzD5hhn9GYQgKpNVjgYasG0zUtZ
mFz6QA6ZOBYuqhMkWePrXb9gswGsIqnGLXaR1xMpmF9z77znyf5sJAoNmsNA
OrnA0qJzy2mrPjcZo7bJQKOM4jUY3QAg.dgMr99FGya6DSanHGFz7Efz9gv4
48Kq9pqO+gTQlH+hKaiOMOYiX3oar83H3X0e8WSrWUg7MAyFNqLPTu1rb5EE
wpLkb2Ny5MA5V1X2QiT9JlIzMIqT65vZoy1gR4nkZzxrz+ZcOmX3qn38yAOH
TWju+v5R3wmUHXJwRL2hfz6eSb3peM+M5MtOpCcXfmTTaNLbBpb25DooDnD4
8PHg1qlfWMT.q9XIpOfBe8qwBkqruJqa85KX8RUZy5KDl9vIdRYsn9GXUG20
jwCBYZoCsVNA00Ar65fMPiz8qvo5VX471cNP3WjhgcQTZ8S7LeG7ztnps9cZ
shHE.Ju8113UgFyNlHG0paVjPl8g4FI71jjFmD47HQP5PK3OnWavsdfGzvdE
mcXT3+G.dd2lv36GUrUpBZyuFi.WddG8DVc.yZriu0Z8z+zNQT3Csx2dVqlK
eayV8mE0.nzi4eLYmOsvYVLbZ5ULAjKJj46MUW3ypae3W6z9iTIE2+lAa08n
NWTRMZWkJrqp7A3dkWVNNG6yJaw7s592MWs92FUHdCbrmvLjUdGD1xg7l7kM
IvzjGiGME1qj2hT3e94fwSf.DmXtyGLFFiLWNJDTBxYeB78oBwIPgZoWcImf
5eLC1m39QwhQSZrZvOXc9D.DlfdAbP9IQTTxicShGdY5Hb.vuq.z9Hh4NFw7
g9ZDc4bH066w9L.5+FY.cIveubuWi2DotZBjs+fbZF2yJD3lhzMcWpsCf18.
HtoNE.JWaCi7ITj+.u0vr5akmm9ZtnwMTdlVUvmJlKBen6S+oMah5T51Rbfl
am2NDmI4.kYwfKAG0iRwk+Psfrx6hpzZki6LR7qiUTlHdQ1kF5bdvl9PLZre
0ODjm37AUlRtVa8yxEKKhhxGW4rOwpCA8IDno.hTLCatqyTowK0tBUCS8BwS
VLGTjbSYl6PGt1HCi3XtuUkGhrKqqc+ipcb77vII0O5xiUCm2Ell+ryeYUu2
LRfR6py2GWpTkHOk0EePAEFXUTaeCtPNGP8Kgp8JO6SJ.fgm7Mnz7KPSedIJ
4Q1lR8j9G660+90+GzFfLZ.
-----------end_max5_patcher-----------
</code></pre>

also testing this now and getting unexpected results. will keep you updated as I learn more

Okay thanks Dan!
Glad I’m not just being dense.

closing the loop!

for the Max folks, here’s a Geode patch that demonstrates the functionality: geode_ex.maxpat (30.8 KB)

how to navigate the patch:

  • throw JF into transient and shape
  • connect patcher to crow
  • enable pullups, if needed
  • enable geode
  • [bang] the [49] number object
  • set channel [1], division [4], repeats [3] (wait till it all dies down before moving on)
  • [bang] that section and you should see 4 pulses from channel 1 (repeats assigns how many pulses after initial pulse)
  • under that: division [3], repeats [2] (wait till it all dies down before moving on)
  • [bang] that section and you should see 3 pulses from channel 6 (repeats assigns how many pulses after initial pulse)
5 Likes

Thanks again for your help Dan!

In the Max Geode patch is there a reason why we need to divide the repeats and division by 1638?

Yeah! It has to do with synthesis mode and Teletype though…

Are you familiar with Teletype?

Yeah i’m familiar with TT but i never tried Geode on it. Guess I’m wondering how it applies to Crow here as I couldn’t find anything related in the docs

The fundamental reason is the underlying I2C code which runs Just Type was written long before the crow idea had begun germinating. It was originally written only with Teletype in mind; Teletype does not have floating point numbers in its scripting language, just integers.

I wrote the description below on a crowded rush hour train, please excuse any typos or not rigorously formatting code snippets!

Teletype explanation

In synthesis mode, you would do the following to play a note:

JF.NOTE pitch vel

How should Pitch and Velocity (ie peak-to-peak voltage) be scaled? What velocity input should correspond to a 5V peak-to-peak waveform? What about 2.5vpp? What about 2.342vpp? The natural choices would be “use the number exactly as is" - but since Teletype is limited to integers, you could only set a 1 or 2 or 3 or 4 or 5vpp waveform but not 2.342vpp or 2.5vpp.

Since Teletype can’t handle floating points, our options would be very limited.

Solution: choose a larger number to represent 10vpp loudness, for instance 1000. Then, typing in 1000 for velocity would result in 10vpp, 900 would be 9vpp, 250 would be 2.50vpp and so on… but we still could not get 2.342vpp. How can we maximize our available precision?

Solution: Make an extremely large number equal to 10vpp. Let’s call this our “super precise large number scaling system.” Now, every voltage like 2.342vpp corresponds to some integer. For instance, to figure out what 1vpp is, just take our super large number and divide it by 10. That’s annoying to do every time though, so… hello Teletype V operator

V 1 conveniently converts the number 1 into the correct number in our “super precise large integer scaling system” for 1vpp. VV 132 creates the correct number for 1.32vpp.

The velocity argument of JF.NOTE expects to be given an integer from our special scale, but instead of doing so directly, we do it by picking an easier voltage number and converting it with the V operator.

Similarly, the pitch input expects numbers to be specified in the super precise scaling system and then interprets them as volt-per-octave voltages. So suppose I want a 1-octave increase above the default of C3 - I would do V 1. What if I want 7 semitones? Then I can use the N operator to accurately convert 5 into first the correct volt per octave voltage, then into the correct “super large integer scaling system” number.

At the same time, I also have the ability to pick some very precise microtonal pitches beyond just semitones though if I set the argument directly instead of using a “lookup table” to (the N or V operators) first.

So why does this matter?

The same I2C command JF.NOTE on teletype is used to control geode mode.
In geode on teletype, you do not need to use the lookup tables; since the arguments only make sense as whole numbers and there is no need for floating points, you set the number of repeats and divisions directly.

So JF.NOTE 1 1 means either make one envelope using the first click division length in geode, or it means play an incredibly quiet (silent really) note that is essentially no different than C3, the default 0 note in synthesis mode.

Alternatively, JF.NOTE V 1 V 1 means play thousands of repeats of an extremely long envelope in geode V 1 is really just a super large integer, or it means play a note one octave above C3 at 1vpp in synthesis mode.

Crucially, the same command is used to talk to both modes; the range switch on JF is what determines the difference in meaning, not the sending device.

Fast forward to crow, which can handle floating point numbers after all. The play_note functions was written with the pitch priority in mind however JF is still expecting to receive an integer in our “super large integer scaling system” which it will then internally convert to the right pitch.

So on crow, play_note expects a floating point v/oct voltage; it then converts this to our “super large integer scale” and sends it to jf.

This poses an issue when we try to use geode - if you type in “1” to play_note, it converts that into a super large integer before sending it to JF.

As such, when we actually want to send a 1 to JF, eg in geode mode, we can’t give play_note a 1 as an argument - we need to give it a much smaller decimal number which will then be immediately made bigger again by play_note, landing on the integer 1.

9 Likes

Keen to see what vcv -> crow -> just friends could be. Orca’s heart driving a few jf voices :slight_smile:

4 Likes

Appreciate the detailed response - really clarifies how to get geode working!

1 Like

Just spent some time with my new Just Friends/Crow/Norns (awake). Wow. Just Friends seems so powerful to me, unexpectedly so:

6 Likes

Has anyone tried to do any alternate tunings (microtonal etc) stuff with crow+jf?

I, of course, completely understand the need to use MIDI, but it is also a slight shame. In modular, using purely CV, we are not as much burdened by musical history and choices made decades ago (which we of course have to deal with every time working with MIDI.)

So, to clarify what I’m refering to, the problem is MIDI pitch bend values (which you need to get non-traditional pitches) are not note specific which can become a problem when using polyphony.

Thanks!

Do you specifically mean with the ^^jf_synth device not receiving MIDI pitch bend? There is nothing in the crow<>JF I2C connection that prevents microtonal sequencing of JF via crow.

JF receives a command from crow which tells it what interval to play relative to C3 and how loud to play it (amplitude in volts):

ii.jf.play_note(<v/oct voltage>, <amplitude voltage>)

So for instance, to play one octave above C3 at a 5Vpp amplitude, you would send:

ii.jf.play_note(1,5)

Two octaves above, at 3.4Vpp amplitude:

ii.jf.play_note(2,3.4)

Since one semitone corresponds to 1/12th of a volt in the V/oct standard, to play a perfect fifth above C3, you would send the following, since a perfect 5th is 7 semitones.

ii.jf.play_note(7/12,5).

One cent is one hundredth of a semitone, so 1/1200 volts. So, to send the note that is 37 semitones below a major third, you could send a message like this - 4/12 corresponds to a major third, and then we subtract 37 semitones from that voltage.

ii.jf.play_note(4/12 - 37/1200,5)

To incorporate pitch bend into the ^^jf_synth M4L device, you would need to come up with a scaling system to map pitch bend values (0-127) to voltage. You would then add this voltage to the voltage determined by the MIDI note. This combined voltage would then be put into the Lua command which gets sent back to the command center device and then out to crow itself and then on to JF.

8 Likes

I’m about to try out this 10ms delay trick in @dan_derks original post here but I’m curious if anything ever came out of the talk about generalizing this approach. Also curious if this can be fixed deeper down like the iic layer? Thanks everyone for your work on this.

This will be fixed in the next Crow firmware release. I’ve got proper polyphony working already, but it needs more testing.

14 Likes

That’s great to hear! I’ll hold off and wait for that. Thank you for the update.

Very nice, just started exploring this patch last night. What setting did you have your mode switch in?

Has anyone played with the ii.jf.retune command at all? I’m getting odd behaviour.

For example, if I send

ii.jf.retune(2, 2, 1)
ii.jf.retune(6, 6, 1)

I would expect 2n and 6n to be the same as their default settings. But instead with the Intone knob at noon, they play the same pitch (Identity) but as I rotate clockwise they go DOWN (I would expect them to go up) with the same pitch and then quickly become inaudible. And if turn Intone counter they immediately turn off past noon and there’s no output at all.

If I send ii.jf.retune(0, 0, 0) everything resets to the defaults correctly. Any ideas?

I have a theory, just because I ran into some troubles with setting arguments in Geode. not at my JF at the moment, but what happens if you send:

ii.jf.retune(2, 2/1638, 1/1638)
ii.jf.retune(6, 6/1638, 1/1638)

?