Playing Musical Notes

Marc-Julien objois, Catherine Single, Charlena Fong, and Mariya Shterngartz

Introduction

Playing music from a digital circuit is easy. Especially if you only use square waves. In this form, a digital output can simply be amplified as-is. However, even this requires some musical background.


Theory

All musical notes in Western notation correspond to certain base frequencies. "Middle A" is the standard by which most instruments are tuned. It corresponds to a frequency of 440 Hz. Consecutive "A" notes above middle A correspond to multiples of 440 Hz. For instance, the next A up is 880 Hz. Similarly, halving a frequency gives the frequency corresponding to an octave below. The A below middle A has a base frequency of 220 Hz. Middle A is usually referred to as A 2. The range of notes is divided numbered octaves, separated at each C.

This is a logarithmic scale. In Western music, the frequencies between octaves are separated into twelve semi-tones. To find the frequency of any given note, use the following formula, where n is the "distance" from middle A.


Fig. 1: Frequency formula

So for instance, A#2 is +1 away from middle A, whereas C 2 (C of same octave as middle A) is -9.

Provided is a chart of frequencies for one octave above and one below middle A.


Fig. 2: Notes and frequencies


Implementation

To output a note of a given frequency (using square waves), one must simply use a clock divider to create a clock signal with the desired period. Let us take the example of middle A (440 Hz). Given a master clock of 25MHz, 25,000,000 Hz / 440 Hz gives us approximately 56818. Thus, every 56818 clock cycles must contain a complete period. To do this, halve the number (in this case, to 28409) and count up to this number repeatedly, looping back to 0 every time. Every time the code loops back to 0, invert the current logic value of the note output clock. This will generate a note of the desired frequency. This can be applied to any note.

Here is a sample block of VHDL code that outputs a square wave at 440 Hz.

  signal middle_a : std_logic := '1';

  process (clk)  -- Going at 25 MHz
    variable count : natural := 0;
  begin
    if rising_edge(clk) then
      if count = 28409 then
        count := 0;
        middle_a <= not middle_a;
      else
        count := count + 1;
      end if;
    end if;
  end process;