Convert semi-tones (steps) to frequency

Using Nyquist scripts in Audacity.
Post and download new plug-ins.
Forum rules
If you require help using Audacity, please post on the forum board relevant to your operating system:
Windows
Mac OS X
GNU/Linux and Unix-like
Post Reply
steve
Site Admin
Posts: 81653
Joined: Sat Dec 01, 2007 11:43 am
Operating System: Linux *buntu

Convert semi-tones (steps) to frequency

Post by steve » Fri Oct 29, 2010 2:52 pm

The proportional relationship between any two adjacent pitches in a normal scale (12 tone equal temperament) is the 12th root of 2.
There's various ways that this may be calculated in Nyquist, but one easy method is:

Code: Select all

(expt 2 (/ 12.0))
(note that we need 12.0 as a float so as to avoid division by zero error)

So if we have a frequency of 440 Hz (A4) and we want to calculate the frequency of the note that is one semi-tone higher (A#4)

Code: Select all

(* 440 (expt 2 (/ 12.0)))
which is approximately 466.16

To calculate the frequency 2 semi-tones above 440 we need to calculate ((semi-tone)^2) and multiply that by our base frequency of 440
so this gives us:
440 * [{(2 ^ (1/12)} 2]
which in Nyquist we can write as:

Code: Select all

(* 440 (expt (expt 2 (/ 12.0)) 2.0))
so the general formula for 'n' semitones above a base frequency 'f0' can be written as a function:

Code: Select all

(defun fcalc (f0 n)
   (* f0 (expt (expt 2 (/ 12.0)) n)))
There's a table here that can help to check that the results are correct http://www.phy.mtu.edu/~suits/notefreqs.html

However, Nyquist has been written specifically with music in mind, so for many applications there is an even easier way.
Nyquist provides two functions:
(hz-to-step) and (step-to-hz)

As a convention that is used in the MIDI 1.0 specification, the note A4 (which is the standard pitch that orchestras tune to and almost everything else uses as the tuning reference) is assigned the number 69 and corresponds to the frequency 440 Hz.

Code: Select all

(step-to-hz 69) ; returns 440.0
(hz-to-step 440) ; returns 69
so to calculate the frequency 2 semitones above 440 Hz we can use:

Code: Select all

(step-to-hz (+ 2 (hz-to-step 440)))
To write this as a general function for 'n' semitones above a base frequency 'f0'

Code: Select all

(defun fcalc2 (f0 n)
 (step-to-hz (+ n (hz-to-step f0))))
Either of these (fcalc) or (fcalc2) functions can be useful where regular pitch spacing is required, for example, to calculate frequencies in the range 20 Hz to (over) 20 kHz that are (approximately) evenly spaced in 1/3 octave intervals:

1/3 octave is 4 semi-tones (12 semi-tone steps to an octave) so we can iterate through a loop:

Code: Select all

(defun fcalc (f0 n)
 (step-to-hz (+ n (hz-to-step f0))))

(setf frequency-list
   (do* ((count 0 (setq count (1+ count)))(freq 20)(flist (list freq)))
      ((> freq 20000)(reverse flist))
         (setq freq (round (fcalc freq 4)))
         (setf flist (cons freq flist))))

(format NIL "~a" frequency-list)
9/10 questions are answered in the FREQUENTLY ASKED QUESTIONS (FAQ)

steve
Site Admin
Posts: 81653
Joined: Sat Dec 01, 2007 11:43 am
Operating System: Linux *buntu

Re: Convert semi-tones (steps) to frequency

Post by steve » Fri Oct 29, 2010 3:15 pm

It's occurred to me that the final code example may be difficult to follow.

There's a good explanation of 'Do' loops here: http://www.audacity-forum.de/download/e ... ef-093.htm
and for DO* http://www.audacity-forum.de/download/e ... ef-094.htm

Breaking down the loop structure into lines:

Code: Select all

(setf frequency-list
   (do* ((count 0 (setq count (1+ count)))(freq 20)(flist (list freq)))
      ((> freq 20000)(reverse flist))
         (setq freq (round (fcalc freq 4)))
         (setf flist (cons freq flist))))

Code: Select all

(setf frequency-list ; sets the variable 'frequency-list' to the result that is returned from the loop

Code: Select all

(do* ((count 0 (setq count (1+ count)))(freq 20)(flist (list freq)))
; sets local variables that will be used in the loop
; 'count' is the loop counter. It is initialised at 0 and then incremented on each loop by the code (setq count (1+ count))
; 'freq' is our initial frequency and is set at 20 (Hz)
; 'flist' is our list of frequencies and initialised to hold the first frequency
; Note that we use DO* and not just 'DO'. This is important because DO* evaluates these initial values in sequential order.
; If we just used 'DO' then we would get an error from (flist (list freq)), but using 'DO*' ensures that 'freq' is evaluated before 'flist'.

Code: Select all

((> freq 20000)(reverse flist))
; The 'test' that allows the loop to exit is (> freq 20000)
; The loop returns the value (reverse flist)

Code: Select all

(setq freq (round (fcalc freq 4)))
; the start of the actual loop code block. Calculates the next frequency using the function (fcalc). The value is rounded to the nearest whole number.

Code: Select all

(setf flist (cons freq flist))))
; adds the new value to the list 'flist', or to put it more technically, it constructs a new list node.
9/10 questions are answered in the FREQUENTLY ASKED QUESTIONS (FAQ)

Post Reply