Manually writing specific voltages to a raw file to be read as a table


#1

Hell all,
I have searched around and found a few similar posts (https://sebiik.github.io/community.axoloti.com.backup/t/types-conversion-bit-shifting-and-friends/2329), but I couldn't find anything that clarified things for me, so I'm starting a new thread.

I am working on a granular audio freezer. I am using jt/granular/graintable.
I am hoping to feed a value from a table into the bipolar "pos" input on graintable. I want this to be very precise so I can play grains from a very specific position in the audio table. I am using a similar granular synth in supercollider (where I have more programming ability) to "explore" the sound file using my mouse x position. In the SuperCollider synth, the pointer into the sound file is a value from 0 to 1.0. So I need to translate this value into a very precise bipolar Axoloti voltage. (The reason I am using SuperCollider is because I like the pitch detectors in SC, and I am trying to "freeze" the granular sample player on regions of the audio file that have a clear pitch.)

I have been on a bit of a journey to try to figure out how to do this. I wrote a table with some bipolar values in axoloti, saved that tabel to a raw file, and then inspected it with a hex editor. As far as I can tell, these are the rules for writing values which can be loaded into a table to read on the axoloti to provide the bipolar voltage values:

pointer from 0..1
[0.0-0.5) corresponds to negatives on a bipolar dial
in hex string, F in first place (0xFXXXX) indicates negative
0 in pointer is (-)0, or 0xF0000 in hex string
0.5 in pointer is (-)65536, or 0xFFFFF in hex string
so take integer (0 to 65536) as Hex String + 0xF0000 to get number
take that number and write it to the binary file as a 16 bit Little Endian integer
[0.5-1.0] corresponds to positives on bipolar dial
this is simply (0-65536) as Hex String written to a binary file as a 16 bit Little Endian integer

However, when I tested my mental model in Supercollider, trying to go from values read on a bipolar dial display in Axoloti to the hex values to write in the raw file, I didn't get the expected behavior.

//testing my ability to go from desired place on bipolar knob to raw binary hex file
//test numbers: -26.70, 5.64, -4.70, 56.10
//-26.7:
64-26.7; // 37.3
(37.3/64.0)*65536; // ~38195
(38195 + 0xF0000).asHexString; // 0xF9533 or
0xF9533; // 1021235
// BUT this is actually -53.4 on the axoloti dial

(5.64/128.0)*65536; // ~5775, simpler because positive
5775.asHexString; // 0x0168F
0x0168F; //5775
// BUT this is actually 11.28 on the axoloti dial

64-4.7; // 59.3
(59.3/128.0)*65536; // ~60723
(60723+0xF0000).asHexString; // 0xFED33
0xFED33; // 1043763
// BUT this is actually -9.4 on the axoloti dial

(56.10/128.0)*65536; // ~57446
57446.asHexString; // 0x0000E066
0x0000E066; // 57446
// BUT this is actually -15.8 on the axoloti dial

Can anyone help me understand what I'm getting wrong in trying to write data to a file manually to be read into a table in the Axoloti and produce a desired voltage? Thanks!


#2

My apologies, this should read 0x0E066


#3

I can't understand what you have written, but maybe you don't understand twos-complement representation of negative numbers?


#4

I do not. That term can inform my googling, thanks!


#5

Note: my previous reply wasn’t meant as a solution, but :
“ F in first place (0xFXXXX) indicates negative “
is incorrect.

Also investigate fixed-point arithmetic.


#6

Definitely, after reading about twos complement I see that what I had written is incorrect. I don’t have much of any experience with these lower-level representations of numbers, so finding out what to google is in fact the solution, thanks!


#7

OK, I understand now. In case anyone else finds this thread in their google result and is confused by my incoherent rambling earlier:

What I didn't fully understand about handling 16 bit representations of bipolar signals is how exactly one of the 16 bits is sacrificed to represent the negative sign. So instead of having 65536 (2 to the 16) possible values, you have 32768 (2 to the 15) possible values. So if you want to go from the number on a bipolar dial in the axoloti patcher (say, 27.0) to a hex representation, the math is:

(27.0/64.0)*32768 = 13824
13824 as hex string is 0x03600000

If instead you want to get the hex string representation of -27.0 on the dial, and for some reason you are trying to get there from (27.0/64,0) like I was, add one to the bitwise not representation of 13824.

If any of that is inaccurate or unclear, someone let me know so I can correct the record.


#8

13824 (decimal) in hex is 0x3600 . The extra zeroes on the right in 0x3600000 are for the fractional part.


'ctrl/dial b' outputs a value in Q11.21 format. Meaning, integer part of the number is in the high 11 bits, the fractional part is in the low 21 bits.

Example:
So, if 'ctrl/dial b' is set to 27.0:
27 is the integer portion
0 is the fractional portion
The integer portion needs to be shifted left 21 bits to create the space for the 21 bit fractional part.

Decimal 27 is 0x1B
0x1B << 21 = 0x03600000
In binary, that's 0b00000011011000000000000000000000

(above, the 0x prefix denotes a hex number, 0b prefix denote a binary number)