Fractional MIDI Note Values


#1

I'm struggling a bit with concepts around fixed-point and fractional representations of numbers in Axoloti objects.

I want to make an object to convert waveform cycle-lengths (in S-Rate cycles) into MIDI note numbers, but the results are likely to be fractional (ie none will exactly translate to MIDI notes).

I can see that note outputs and pitch inputs are all frac32.bipolar, but internal variables dealing with pitch and note values inside objects always seem to be unsigned integer.

I'm unclear what variable types to use to do the conversion, or the ranges involved.

Sorry if this is basic stuff, but I find the way the same data is represented in multiple ways in the Patcher environment confusing as hell.

Can someone help me out, please?

a|x


#2

see https://sebiik.github.io/community.axoloti.com.backup/t/coding-axoloti-objects/2606


#3

So, if I was to make a lookup table for cycle-lengths 0 to 160, I'd use.. signed integer values in the 0 to 1<<27 range?

a|x


#4

I think I now have the fractional MIDI note numbers for 8kHz cycle-length 1 to 160.

a|x


#5

I'm still not sure what to do with those numbers, to convert them to integers in the correct range.

a|x


#6

I worked it out, and make a lookup table to convert directly from 8kHz cycle-length to uint32_t MIDI pitch values, in the standard 27-bit range.

a|x


#7

@SirSickSik has made an FTOM function that is used in some of his objects e.g. sss/conv/freq2pitch. Pass it the reciprocal of the cycle length and you get an axo standard pitch value.

It's very useful - I've seen several people on here, including me, wanting to convert cycle lengths to pitch values. An official optimized FTOM function in the firmware would be an idea. Perhaps an optimized log2 would follow...


#8

That's useful. Not sure of advisability of including a file from another user, though.

This was the code I used to generate the lookup-table (note it's for 8kHz, not 48kHz cycles).

#include <stdio.h>
#include <math.h>

double hz = 0;
double note_fract = 0;
int32_t note_axo = 0;

double freq_to_note(float hz) {
    return 5 + 12 * log2(hz / 440);
};

int32_t note_to_axo(double note) {
    int32_t multiplier = 1<<21;
    return (int32_t)(note * multiplier);
}

int main(int argc, const char * argv[]) {
    
    for(int i = 1; i <= 160; i++) {
        // Herz from cycles
        hz = 8000 / i;
        // Hz to fractional MIDI note (-64 to 64 range)
        note_fract = freq_to_note(hz);
        // Axoloti note value
        printf("%i,\n", note_to_axo(note_fract));
    }

    return 0;
}

a|x


#9

you can also use PLL, as in some cases it works better then zero-crossings.
use a comparator to check whether the input signal is higher or lower then an internal sine oscillator.
Send the comparator output through a lowpass filter and add this to the pitch of the sine oscillator.
With the right values, it will stabalize pretty quickly (phase lock) and directly give you the frequency of the input signal (depending on the base pitch and lowpass filter cutoff, this might be offsetting in octaves).
Just send the total pitch value (base+lowpass output) to the output of the module and you'll have your frequency converter based on some signal input.


#10

That's very cool (though a bit off-topic) :wink:

Can you get it to work on a single 16-sample buffer, or would you need a larger buffer for reliable tracking?

Have you made one of these pitch-tracker objects, by any chance @SirSickSik ?

a|x


#11

You can also do an FFT, and derive an approximate pitch from the bin with largest magnitude, I think.

Might be a bit resource-intensive for the Axoloti, though.

a|x


#12

fft isn't really useful for the axoloti. Will take waaay to much cpu for that little function..
nice.. we got a frequency... but no room for actually doing something with it.. now what? XD

ehm, 16 samples? Well, 16 samples is a bit short isn't it?
You won't even be able to track frequencies below 3000hz...

a pitch tracker should be somewhere in my folders.. I guess conversion.. But gotta go to work ríght now, so can't check it out to be sure..
laterzzzz


#13

You'd definitely need a longer buffer, then.

a|x