bsp2/libanalogrytm

Trig condition encoding found!

alisomay opened this issue ยท 29 comments

They're doing an interesting thing with the encoding the trig conditions.
I've just found out how they do it, it is a little complex. Maybe you know it already but it is not documented in libanalogrytm.

Here it goes:

Let's take the first trig as the example. (1 is used to denote the bits.)

notes[0] -> 1_000_0000 -> bit7 we thought that it is a flag but it is not, now let's call it AGR_6
micro_timings[0] -> 11_00_0000 -> lets call bit7 and bit6 as AGR_5 and AGR_4
retrig_lengths[0] -> 1_000_0000 -> lets call bit7 AGR_3
retrig_rates[0] -> 111_0_0000 -> lets call bit7, bit6 and bit5 as AGR_2, AGR_1, AGR_0

If we put them all together the AGR (Aggregate) bits make up a 7 bit value,

  • AGR_6...AGR_0 -> has a 7 bit range.

For these 7 bits not the whole possible range is used but from 0b000_0000 to 0b100_0000 (0..=64) total 65 values.

When I turn the knob over the trig condition there are:

  • Exactly 65 values
  • Each value counts in order from 0 to 64.
  • When the trig condition is unset the value reads 0x7F (127) (0b011_1111). We can understand if it is unset from here.

I hope it is useful for you also โœจ. I don't know how properly document this mess but if you ever feel like it go for it ๐Ÿ˜„ .

Helper operations to assemble this value:

unsigned char trig_condition = 0;
trig_condition |= (( micro_timings[0] & 0xC0 ) >> 2);
trig_condition |= (( retrig_lengths[0] & 0x80 ) >> 4);
trig_condition |= (( retrig_rates[0] & 0xE0 ) >> 5);

I don't know if it is valid C code but I think it conveys the info.

Also when using micro timings a more relevant way to interpret is:

signed char mt = (micro_timing_value & 3F) << 2

This way we have:

  • Range "-92..=92"
  • Mid point is 0
  • For every increment or decrement it is +4 or -4

Very nice! I had some ugly code to get the trig condition based on microtiming / retrig values but this looks nice if true. I just started porting over my app to support OS 1.70. It would be nice if the library contained some functions to get / set these encoded values, much like the new function to set the trig flags.

I've already implemented those in my library. It is working pretty well for me, I've tested it multiple times and it checks out.
Please let me know how it goes for you also. โœจ

We can also make a PR to add these utility functions to libanalogrytm but I don't know if it falls in the scope of this library which has a pretty fundamental scope.

bsp2 commented

Sorry for the late reply, I've been occupied with other things.

The latest revision (pattern.h, pattern.c) adds note / velocity / note length / microtiming / retrig / trig condition setter + getter utility functions. The *_trig_condition functions are based on on alisomay's findings. Haven't had much time for testing, please let me know if there are any issues.

Amazing ๐Ÿ™ Thanks.

@mekohler Please test them when you have time so the issue can be closed ๐Ÿ™

Thanks @bsp2, I won't get to test these until next week as I am still changing the underlying Model to support the new changes, but I trust @alisomay :]

Ok I have implemented my own in the upper layer (they work fine) bu I'll test libanalogrytm versions as soon as I find time and close the issue then.

bsp2 commented

minor update to pattern.h, pattern.c:

  • rename utility fxns to ar_pattern_track_set_step_*/ ar_pattern_track_get_step_*
  • fix a bug in ar_pattern_track_get_step_note (mask out condition bit)
  • add some comments

..and I've tested at least the read functions.

Since you're on it, you can add these also if you wish to โœจ

#define AR_TRIG_CONDITION_1_PERCENT  (0)
#define AR_TRIG_CONDITION_3_PERCENT  (1)
#define AR_TRIG_CONDITION_4_PERCENT  (2)
#define AR_TRIG_CONDITION_6_PERCENT  (3)
#define AR_TRIG_CONDITION_9_PERCENT  (4)
#define AR_TRIG_CONDITION_13_PERCENT (5)
#define AR_TRIG_CONDITION_19_PERCENT (6)
#define AR_TRIG_CONDITION_25_PERCENT (7)
#define AR_TRIG_CONDITION_33_PERCENT (8)
#define AR_TRIG_CONDITION_41_PERCENT (9)
#define AR_TRIG_CONDITION_50_PERCENT (10)
#define AR_TRIG_CONDITION_59_PERCENT (11)
#define AR_TRIG_CONDITION_67_PERCENT (12)
#define AR_TRIG_CONDITION_75_PERCENT (13)
#define AR_TRIG_CONDITION_81_PERCENT (14)
#define AR_TRIG_CONDITION_87_PERCENT (15)
#define AR_TRIG_CONDITION_91_PERCENT (16)
#define AR_TRIG_CONDITION_94_PERCENT (17)
#define AR_TRIG_CONDITION_96_PERCENT (18)
#define AR_TRIG_CONDITION_98_PERCENT (19)
#define AR_TRIG_CONDITION_99_PERCENT (20)
#define AR_TRIG_CONDITION_100_PERCENT (21)
#define AR_TRIG_CONDITION_FILL (22)
#define AR_TRIG_CONDITION_FILL_NOT (23)
#define AR_TRIG_CONDITION_PRE (24)
#define AR_TRIG_CONDITION_PRE_NOT (25)
#define AR_TRIG_CONDITION_NEI (26)
#define AR_TRIG_CONDITION_NEI_NOT (27)
#define AR_TRIG_CONDITION_1ST (28)
#define AR_TRIG_CONDITION_1ST_NOT (29)
#define AR_TRIG_CONDITION_1B2 (30)
#define AR_TRIG_CONDITION_2B2 (31)
#define AR_TRIG_CONDITION_1B3 (32)
#define AR_TRIG_CONDITION_2B3 (33)
#define AR_TRIG_CONDITION_3B3 (34)
#define AR_TRIG_CONDITION_1B4 (35)
#define AR_TRIG_CONDITION_2B4 (36)
#define AR_TRIG_CONDITION_3B4 (37)
#define AR_TRIG_CONDITION_4B4 (38)
#define AR_TRIG_CONDITION_1B5 (39)
#define AR_TRIG_CONDITION_2B5 (40)
#define AR_TRIG_CONDITION_3B5 (41)
#define AR_TRIG_CONDITION_4B5 (42)
#define AR_TRIG_CONDITION_5B5 (43)
#define AR_TRIG_CONDITION_1B6 (44)
#define AR_TRIG_CONDITION_2B6 (45)
#define AR_TRIG_CONDITION_3B6 (46)
#define AR_TRIG_CONDITION_4B6 (47)
#define AR_TRIG_CONDITION_5B6 (48)
#define AR_TRIG_CONDITION_6B6 (49)
#define AR_TRIG_CONDITION_1B7 (50)
#define AR_TRIG_CONDITION_2B7 (51)
#define AR_TRIG_CONDITION_3B7 (52)
#define AR_TRIG_CONDITION_4B7 (53)
#define AR_TRIG_CONDITION_5B7 (54)
#define AR_TRIG_CONDITION_6B7 (55)
#define AR_TRIG_CONDITION_7B7 (56)
#define AR_TRIG_CONDITION_1B8 (57)
#define AR_TRIG_CONDITION_2B8 (58)
#define AR_TRIG_CONDITION_3B8 (59)
#define AR_TRIG_CONDITION_4B8 (60)
#define AR_TRIG_CONDITION_5B8 (61)
#define AR_TRIG_CONDITION_6B8 (62)
#define AR_TRIG_CONDITION_7B8 (63)
#define AR_TRIG_CONDITION_8B8 (64)
#define AR_TRIG_CONDITION_NONE (0x7F)
bsp2 commented

very nice, I already wanted to ask if someone could spare the time to write these down. thanks !

EDIT: new pattern.h

By the way these are irrelevant to the scope of this issue but I've seen that they're missing in kit.h.
Since I was working on this part today, I just did them quickly.

The values are right but I don't know if the namings are directly in style for the repo.
Change them as u wish โœจ

#define AR_FX_LFO_DEST_NONE (37)

#define AR_FX_LFO_DEST_DELAY_TIME (0)
#define AR_FX_LFO_DEST_DELAY_PINGPONG (1)
#define AR_FX_LFO_DEST_DELAY_STEREOWIDTH (2)
#define AR_FX_LFO_DEST_DELAY_FEEDBACK (3)
#define AR_FX_LFO_DEST_DELAY_HPFILTER (4)
#define AR_FX_LFO_DEST_DELAY_LPFILTER (5)
#define AR_FX_LFO_DEST_DELAY_REVERBSEND (6)
#define AR_FX_LFO_DEST_DELAY_MIXVOLUME (7)
#define AR_FX_LFO_DEST_DELAY_OVERDRIVE (8)

#define AR_FX_LFO_DEST_REVERB_PREDELAY (10)
#define AR_FX_LFO_DEST_REVERB_DECAY (11)
#define AR_FX_LFO_DEST_REVERB_SHELVINGFREQ (12)
#define AR_FX_LFO_DEST_REVERB_SHELVINGGAIN (13)
#define AR_FX_LFO_DEST_REVERB_HPFILTER (14)
#define AR_FX_LFO_DEST_REVERB_LPFILTER (15)
#define AR_FX_LFO_DEST_REVERB_MIXVOLUME (16)

#define AR_FX_LFO_DEST_DISTORTION_DISTORTIONAMOUNT (18)
#define AR_FX_LFO_DEST_DISTORTION_SYMMETRY (19)

#define AR_FX_LFO_DEST_COMPRESSOR_THRESHOLD (21)
#define AR_FX_LFO_DEST_COMPRESSOR_ATTACK_MS (22)
#define AR_FX_LFO_DEST_COMPRESSOR_RELEASE_S (23)
#define AR_FX_LFO_DEST_COMPRESSOR_RATIO (24)
#define AR_FX_LFO_DEST_COMPRESSOR_SIDECHAINEQ (25)
#define AR_FX_LFO_DEST_COMPRESSOR_MAKEUPGAIN (26)
#define AR_FX_LFO_DEST_COMPRESSOR_DRYWETMIX (27)
#define AR_FX_LFO_DEST_COMPRESSOR_VOLUME (28)
bsp2 commented

thanks again -- merged kit.h

(also tested the ar_pattern_track_set_step_trig_condition() function in the meantime...and fixed it. ahem :-))

bsp2 commented

p.s.: I recorded a video of my updated kit / sound editor the other day. Just showcases some hihat sounds (mainly using "hh lab"): FW1.70 hihat test

(and well, the editor already looks a bit different now, i.e. I fixed the icon+font sizes)
(it does have a rather nice sound preset manager, much easier to use than menu-diving for sounds on the AR itself)

Super nice!
Also it transmits the kits in musically instant time. Looks like a seamless editing experience ๐Ÿ‘

bsp2 commented

the sound transfer is fast enough for sequenced sound changes (you can sequence the editor).

then again, performance controllers and scenes are usually enough.
the only drawback is that the performance controllers are slewed, i.e. there is no way to immediately change the sound (like with p-locks). The drum sequencer (briefly shown in the video) has an option to send the events ahead of time, though.

bsp2 commented

(off topic) speaking of unlisted YT videos, here's another one that shows my sampler + modular synth in 8bit lo-fi mode
(originally recorded this for someone who still uses some Amiga software I wrote as a teenager)
aon style fm test 2023 11 23 18 06 10

this "modular synth" is based on a (very simple) script language and auto-generates UIs, samples, or, alternatively, "C" code, which is compiled to native code voice plugin libraries (.dylib / .so / .dll) which are instantly reloaded (just takes a few dozen milliseconds for simple machines). The plugin API can be found in my STFX GitHub repo.

While the plugins can have an arbitrary number of parameters (and modulation inputs), the UI displays just the first 8.
I.e. this encourages "Elektron" style "machines" (instead of parameter overkill). It's also possible to (easily) create macro parameters that modulate "n" internal parameters.

These "machines" (sample generators or realtime voice plugins) can also be embedded in the (unreleased) standalone player, e.g. for games and demos.

Well, just a little show-and-tell, I'll probably delete this post later on.

Please don't delete it.
It is very inspiring that you've created software in this level of sophistication and performance for mostly your own creative purposes. It is great that you're sharing this.

There might be a small generation gap between us :).

Atari ST was the first computer I've encountered and I started to make abstract art ( ๐Ÿ˜… ) with Cubase 1.0 on black and white screen also tracked with Voyetra Sequencer on DOS but back then I was 6. I was also playing some AMIGA games at my friends house back then.

What a time it was.. Since you were writing audio software back then, hats off my friend ๐Ÿ‘

Also the jam in the video is cool. I'm listening it again now โœจ

bsp2 commented

I was a teenager in the mid nineties so you can do the math ;-)

Voyetra is something I have never used myself but coincidentally it served as an inspiration for the "freeform" node in my sequencer (i.e. basic feature set, everything on one screen, focus on composition / song writing). You can micro-edit a selection in a "tracker" view via a single key press, though.

And heh, forgot to mention the little demo song ~3 minutes in. Could be the soundtrack to some cave-exploring jump'n'run game shrug :D

EDIT: and just for perspective, I started this SW in my twenties and 1.5 million lines of code later I'm now in my fourties . good grief! (the good news is that it's all open source, even if it's not on GitHub, yet. This is going to be a Blender-size repo)

Almost ready to test, but I think:

  1. The Retrig Parameters: Always On, Vel Curve, etc are stored in the __unknown_009E array (ar_sound_t)

  2. the MFX Level is stored in the __unknown_arr1b array (ar_kit_t)

"The Retrig Parameters: Always On, Vel Curve, etc are stored in the __unknown_009E array (ar_sound_t)"
Oh this is a good find! I was also thinking about those time to time. I couldn't find them before :)

Aha..

   s_u16_t track_levels[12];   /* @0x0014..0x002b   (note) LSB (track_levels[i].b.hi) is unused (always 0x00) */
   sU8 __unknown_arr1b[0x2];   /* @0x002c..0x002d */

These are adjacent and the first one is 12 bytes long I should have seen that! :)

bsp2 commented

of course, the FX track has a level setting, too. Regarding the retrig fields in the kit:

       - @0x09DD: track "always on" retrig flags hi ($0800=trk12, .., $0002=trk2, $0001=trk1)
       - @0x09DE: track "always on" retrig flags lo
       - @0x09DF: track 1 retrig ($00=1/1, $09=1/16, .., $10=1/80)
       - @0x09E0: track 1 retrig length ($00=0.125, ..)
       - @0x09E1: track 1 retrig vel curve (-128..127)
       - @0x09E2: ?
       - @0x09E3: track 2 retrig ($0B=1/24, ..)
       - @0x09E4: track 2 retrig length ($2E=1/4, ..)
       - @0x09E5: track 2 retrig vel curve
       - @0x09E6: ?

=> kit.h

bsp2 commented

(off topic) I just built a hihat machine in my SW modular (the one I mentioned last night). here's a YT demo video of it:
https://www.youtube.com/watch?v=NyD5j7OjLWA (it's a 4k video, had to select 2160p manually)
(see video description for further info)

Some more findings โœจ
#10

Shouldn't fx_dist_reverb_send in ar_kit_t be fx_dist_delay_overdrive?

Yep it should, I also found that out but forgot to point it out :)

Also for the name consistency:

   sU8 fx_dist_reverb_send;    /* @0x07DA ? */
   sU8 __unused_pad9;          /* @0x07DB ? */

   sU8 fx_dist_delay_pre_post; /* @0x07DC ? */
   sU8 __unused_pad11;         /* @0x07DD ? */

is a better alternative to:

   sU8 fx_dist_reverb_pre_post;    /* @0x07DA ? */
   sU8 __unused_pad9;          /* @0x07DB ? */

   sU8 fx_dist_delay_pre_post; /* @0x07DC ? */
   sU8 __unused_pad11;         /* @0x07DD ? */