Detecting Musical Notes in Python - Updates

Introduction

In my last post, I introduced my pitch_detector project, the goal of which is to detect musical notes from recorded audio. In this post, I'll provide a quick update on my (very slow) progress on the project:
  • Using uv
  • Calculating musical notes from frequency
  • Writing tests
Please feel free to download and run the program yourself to test it out! (Link again to code)

Using uv

One of the first things I did when returning to this project was initialize the project with uv, which was introduced to me by Todd Valentic. It's really simple to use and makes tracking dependencies, creating virtual environments, and sharing code really easy.

Calculating musical notes from frequency

I also finally finished the main goal of the program: converting frequencies into musical notes. I'll ignore how I extracted frequencies from recorded audio. Instead, I'll just focus on how I came up with a function that does this:
f ( frequency_Hz ) = musical_note, octave

12 equal temperament 

As discussed in previous posts, musical notes of the chromatic scale are equal-tempered (equidistant) on a logarithmic scale, which we can see clearly if we look at frequencies of the 0th octave:
Frequencies of octave 0 plotted logarithmically

If we just look at the 0th octave, we can determine which musical note a given frequency is closest to (logarithmically) with this formula:
note = min ( abs ( log2(notes) - log2(frequency) ) )
This works for:
min(notes) < frequency < max(notes)
In non-mathy terms, this is:
  • Take the log of everything.
  • Calculate the distance between the input frequency and known musical frequencies.
  • Find the distance that's closest to 0 (the minimum distance).
This approach breaks when we test frequencies outside of the 0th octave range, since the "distances" calculated will not hover around 0, but will hover around the input frequency's octave. I'll skip the derivation, but the formula we actually want is:
octave = round ( frequency - MIDDLE )
    note = min ( abs ( octave - (log2(notes) - log2(frequency) ) ) )
Where MIDDLE is the logarithmic middle of the 0th octave.

Writing tests

I'm now writing test cases to see where my code fails. I'm working to keep my tests readable and modular, guided by a testing blog sent to me by a friend. I'm utilizing pytest and hope to take full advantage of its features (such as fixtures) going forward.

Moving forward

My goal is a command-line program that can do real-time audio collect and processing, similar to a guitar tuner. Stay tuned for that (ba dum tss) haha.

Acknowledgements

Thanks for Todd Valentic for his guidance during the work day! The stuff I learn from him has been widely applied here. Thanks also to Mike Fosco for his advice on coding and software.

Comments

Popular Posts