Search code examples
mathaudiologarithmbeep

Convert linear audio frequency distribution to logarithmic/perceptual distribution?


I am creating some audio cues for scripts, and keep running into the same problem:

Human perception is logarithmic. If I want to cut a range of sounds into 10 equal parts, I can't just subtract the lowest pitch from the highest pitch and divide by 10. Those steps will not be perceptually accurate.

So, when dealing with a function that gives output in the 20-20000 range (kHz), is there a basic mathematical function that reasonably approximates a conversion of these numbers to their perceptual equivalents -- while still mapping in the 20-20000 range?

Example:

I am dealing with a "beep" command which takes kHz as a range.

I know from my own system testing that the functional range for this command on the system being tested is 38 to 9900Hz.

So if I was creating an audio countdown to go through 100 items, I would divide 9900-38 / 100 into a 98.62Hz range per item. If I were to create an audio countdown, I would simply create a sequence of beeps starting at max range, decreasing 98.62Hz per beep.

But this doesn't sound perceptually accurate. Most time is spent in high-sounding frequencies. Not enough time is spent in low-sounding frequencies.

I did manage to fix this for my white noise generator, but I can't apply that same kind of fix to an audio countdown. (Mainly because there is quite the leeway for mistakes in a white noise generator.) In that instance, I simply divided by Hz by 10. But I did so randomly. It gave me the desired effect, but not any kind of consistent mapping function:

        if  %@RANDOM[1,10] gt 6 (set BEEP_FREQUENCY_TEMP=%@FLOOR[%@EVAL[BEEP_FREQUENCY_TEMP / 10]] 
        if  %@RANDOM[1,10] gt 9 (set BEEP_FREQUENCY_TEMP=%@FLOOR[%@EVAL[BEEP_FREQUENCY_TEMP / 10]] 
        if  %@RANDOM[1,10] gt 8 (set BEEP_FREQUENCY_TEMP=%@FLOOR[%@EVAL[BEEP_FREQUENCY_TEMP / 10]]

Can you tell I don't even know how to ask my question?


Solution

  • I ended up looking up a list of all frequencies that match notes, making a text file that has a lit of all those frequencies, and simply iterating through that at whatever interval I want. For example, if I want a note that indicates 10% upload progress, it takes the total number of lines, and grabs the line that is 10% through the file. This solved the problem of making my audio countdowns sound linear even though the frequencies required to do this aren't linear.

    A lookup table.

    Not elegant, but works.