{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\"FMP\"\n", "\"AudioLabs\"\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\"C1\"\n", "

Frequency and Pitch

\n", "
\n", "\n", "
\n", "\n", "

\n", "Following Section 1.3.2 of [Müller, FMP, Springer 2015], we cover in this notebook the relation between frequency and pitch. \n", "

" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Sinusoids\n", "\n", "A sound wave can be visually represented by a [waveform](../C1/C1S3_Waveform.html). If the points of high and low air pressure repeat in an alternating and regular fashion, the resulting waveform is called **periodic**. In this case, the *period* of the wave is defined as the time required to complete a cycle. The **frequency**, measured in **Hertz** (Hz), is the reciprocal of the period. The simplest type of periodic waveform is a **sinusoid**, which is completely specified by its **frequency**, its **amplitude** (the peak deviation of the sinusoid from its mean), and its **phase** (determining where in its cycle the sinusoid is at time zero). The following figure shows a sinusoid with frequency $4~\\mathrm{Hz}$.\n", "\n", "\"C1\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Audible Frequency Range\n", "\n", "The higher the frequency of a sinusoidal wave, the higher it sounds. The audible frequency range for humans is between about $20~\\mathrm{Hz}$ and $20000~\\mathrm{Hz}$ ($20~\\mathrm{kHz}$). Other species have different hearing ranges. For example, the top end of a dog's hearing range is about $45~\\mathrm{kHz}$, a cat's is $64~\\mathrm{kHz}$, while bats can even detect frequencies beyond $100~\\mathrm{kHz}$. This is why one can use a dog whistle, which emits **ultrasonic sound** beyond the human hearing capability, to train and to command animals without disturbing nearby people.\n", "\n", "In the following experiment, we generate a **chirp signal** which frequency increases by a factor of two (one octave) every second. Starting with $80~\\mathrm{Hz}$, the frequency raises to $20480~\\mathrm{Hz}$ over a total duration of $8$ seconds. " ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "execution": { "iopub.execute_input": "2024-02-15T08:45:15.142303Z", "iopub.status.busy": "2024-02-15T08:45:15.142005Z", "iopub.status.idle": "2024-02-15T08:45:18.134248Z", "shell.execute_reply": "2024-02-15T08:45:18.133326Z" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import IPython.display as ipd\n", "import numpy as np\n", "import sys\n", "\n", "sys.path.append('..')\n", "import libfmp.c1\n", "\n", "Fs = 44100\n", "dur = 1\n", "freq_start = 80 * 2**np.arange(8)\n", "for f in freq_start:\n", " if f==freq_start[0]:\n", " chirp, t = libfmp.c1.generate_chirp_exp_octave(freq_start=f, dur=dur, Fs=Fs, amp=1)\n", " else:\n", " chirp_oct, t = libfmp.c1.generate_chirp_exp_octave(freq_start=f, dur=dur, Fs=Fs, amp=1)\n", " chirp = np.concatenate((chirp, chirp_oct))\n", "\n", "ipd.display(ipd.Audio(chirp, rate=Fs))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Similarly, we generate a chirp signal starting with $640~\\mathrm{Hz}$ and going down $20~\\mathrm{Hz}$ over a total duration of $10$ seconds." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "execution": { "iopub.execute_input": "2024-02-15T08:45:18.193006Z", "iopub.status.busy": "2024-02-15T08:45:18.192589Z", "iopub.status.idle": "2024-02-15T08:45:18.232231Z", "shell.execute_reply": "2024-02-15T08:45:18.231474Z" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Fs = 8000\n", "dur = 2\n", "freq_start = 20 * 2**np.arange(5)\n", "for f in freq_start:\n", " if f==freq_start[0]:\n", " chirp, t = libfmp.c1.generate_chirp_exp_octave(freq_start=f, dur=dur, Fs=Fs, amp=1)\n", " else:\n", " chirp_oct, t = libfmp.c1.generate_chirp_exp_octave(freq_start=f, dur=dur, Fs=Fs, amp=1)\n", " chirp = np.concatenate((chirp,chirp_oct)) \n", " \n", "chirp = chirp[::-1] \n", "ipd.display(ipd.Audio(chirp, rate=Fs))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Pitches and Center Frequencies\n", "\n", "The sinusoid can be considered the prototype of an acoustic realization of a musical note. Sometimes the sound resulting from a sinusoid is called a **harmonic sound** or **pure tone**. The notion of frequency is closely related to what determines the **pitch** of a sound. In general, pitch is a subjective attribute of sound. In the case of complex sound mixtures its relation to frequency can be especially ambiguous. In the case of pure tones, however, the relation between frequency and pitch is clear. For example, a sinusoid having a frequency of $440~\\mathrm{Hz}$ corresponds to the pitch $\\mathrm{A4}$. This particular pitch is known as **concert pitch**, and it is used as the reference pitch to which a group of musical instruments are tuned for a performance. Since a slight change in frequency does not necessarily lead to a perceived change, one usually associates an entire range of frequencies with a single pitch.\n", "\n", "Two frequencies are perceived as similar if they differ by a power of two, which has motivated the notion of an **octave**. For example, the perceived distance between the pitches $\\mathrm{A3}$ ($220~\\mathrm{Hz}$) and $\\mathrm{A4}$ ($440~\\mathrm{Hz}$ is the same as the perceived distance between the pitches $\\mathrm{A4}$ and $\\mathrm{A5}$ ($880~\\mathrm{Hz}$). In other words, the human perception of pitch is logarithmic in nature. This perceptual property has already been used to define the [equal-tempered scale](../C1/C1S1_MusicalNotesPitches.html) that subdivides an octave into twelve semitones based on a logarithmic frequency axis. More formally, using the [MIDI note numbers](../C1/C1S2_MIDI.html), we can associate to each pitch $p\\in[0:127]$ a **center frequency** $F_\\mathrm{pitch}(p)$ (measured in Hz) defined as follows:\n", "\n", "$$\n", "F_\\mathrm{pitch}(p) = 2^{(p-69)/12} \\cdot 440.\n", "$$\n", "\n", "The MIDI note number $p=69$ serves as reference and corresponds to the pitch $\\mathrm{A4}$ ($\\mathrm{440~Hz}$). Increasing the pitch number by $12$ (an octave) leads to an increase by a factor of two. The following figure shows a portion of a piano keyboard with the keys labeled by the pitch names and the MIDI note numbers. \n", "\n", "\"C1\"\n", "\n", "Using the formula for $F_\\mathrm{pitch}$, we compute in the next code cell the center frequencies for all MIDI pitch $p\\in[21:108]$ corresponding to the notes of a standard piano keyboard (A0 to C8)." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2024-02-15T08:45:18.237394Z", "iopub.status.busy": "2024-02-15T08:45:18.237093Z", "iopub.status.idle": "2024-02-15T08:45:18.246869Z", "shell.execute_reply": "2024-02-15T08:45:18.245760Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "p = 21 (A 0), freq = 27.50 \n", "p = 22 (A#0), freq = 29.14 \n", "p = 23 (B 0), freq = 30.87 \n", "p = 24 (C 1), freq = 32.70 \n", "p = 25 (C#1), freq = 34.65 \n", "p = 26 (D 1), freq = 36.71 \n", "p = 27 (D#1), freq = 38.89 \n", "p = 28 (E 1), freq = 41.20 \n", "p = 29 (F 1), freq = 43.65 \n", "p = 30 (F#1), freq = 46.25 \n", "p = 31 (G 1), freq = 49.00 \n", "p = 32 (G#1), freq = 51.91 \n", "p = 33 (A 1), freq = 55.00 \n", "p = 34 (A#1), freq = 58.27 \n", "p = 35 (B 1), freq = 61.74 \n", "p = 36 (C 2), freq = 65.41 \n", "p = 37 (C#2), freq = 69.30 \n", "p = 38 (D 2), freq = 73.42 \n", "p = 39 (D#2), freq = 77.78 \n", "p = 40 (E 2), freq = 82.41 \n", "p = 41 (F 2), freq = 87.31 \n", "p = 42 (F#2), freq = 92.50 \n", "p = 43 (G 2), freq = 98.00 \n", "p = 44 (G#2), freq = 103.83 \n", "p = 45 (A 2), freq = 110.00 \n", "p = 46 (A#2), freq = 116.54 \n", "p = 47 (B 2), freq = 123.47 \n", "p = 48 (C 3), freq = 130.81 \n", "p = 49 (C#3), freq = 138.59 \n", "p = 50 (D 3), freq = 146.83 \n", "p = 51 (D#3), freq = 155.56 \n", "p = 52 (E 3), freq = 164.81 \n", "p = 53 (F 3), freq = 174.61 \n", "p = 54 (F#3), freq = 185.00 \n", "p = 55 (G 3), freq = 196.00 \n", "p = 56 (G#3), freq = 207.65 \n", "p = 57 (A 3), freq = 220.00 \n", "p = 58 (A#3), freq = 233.08 \n", "p = 59 (B 3), freq = 246.94 \n", "p = 60 (C 4), freq = 261.63 \n", "p = 61 (C#4), freq = 277.18 \n", "p = 62 (D 4), freq = 293.66 \n", "p = 63 (D#4), freq = 311.13 \n", "p = 64 (E 4), freq = 329.63 \n", "p = 65 (F 4), freq = 349.23 \n", "p = 66 (F#4), freq = 369.99 \n", "p = 67 (G 4), freq = 392.00 \n", "p = 68 (G#4), freq = 415.30 \n", "p = 69 (A 4), freq = 440.00 \n", "p = 70 (A#4), freq = 466.16 \n", "p = 71 (B 4), freq = 493.88 \n", "p = 72 (C 5), freq = 523.25 \n", "p = 73 (C#5), freq = 554.37 \n", "p = 74 (D 5), freq = 587.33 \n", "p = 75 (D#5), freq = 622.25 \n", "p = 76 (E 5), freq = 659.26 \n", "p = 77 (F 5), freq = 698.46 \n", "p = 78 (F#5), freq = 739.99 \n", "p = 79 (G 5), freq = 783.99 \n", "p = 80 (G#5), freq = 830.61 \n", "p = 81 (A 5), freq = 880.00 \n", "p = 82 (A#5), freq = 932.33 \n", "p = 83 (B 5), freq = 987.77 \n", "p = 84 (C 6), freq = 1046.50 \n", "p = 85 (C#6), freq = 1108.73 \n", "p = 86 (D 6), freq = 1174.66 \n", "p = 87 (D#6), freq = 1244.51 \n", "p = 88 (E 6), freq = 1318.51 \n", "p = 89 (F 6), freq = 1396.91 \n", "p = 90 (F#6), freq = 1479.98 \n", "p = 91 (G 6), freq = 1567.98 \n", "p = 92 (G#6), freq = 1661.22 \n", "p = 93 (A 6), freq = 1760.00 \n", "p = 94 (A#6), freq = 1864.66 \n", "p = 95 (B 6), freq = 1975.53 \n", "p = 96 (C 7), freq = 2093.00 \n", "p = 97 (C#7), freq = 2217.46 \n", "p = 98 (D 7), freq = 2349.32 \n", "p = 99 (D#7), freq = 2489.02 \n", "p = 100 (E 7), freq = 2637.02 \n", "p = 101 (F 7), freq = 2793.83 \n", "p = 102 (F#7), freq = 2959.96 \n", "p = 103 (G 7), freq = 3135.96 \n", "p = 104 (G#7), freq = 3322.44 \n", "p = 105 (A 7), freq = 3520.00 \n", "p = 106 (A#7), freq = 3729.31 \n", "p = 107 (B 7), freq = 3951.07 \n", "p = 108 (C 8), freq = 4186.01 \n" ] } ], "source": [ "def f_pitch(p):\n", " \"\"\"Compute center frequency for (single or array of) MIDI note numbers\n", "\n", " Notebook: C1/C1S3_FrequencyPitch.ipynb\n", "\n", " Args:\n", " p (float or np.ndarray): MIDI note numbers\n", "\n", " Returns:\n", " freq_center (float or np.ndarray): Center frequency\n", " \"\"\"\n", " freq_center = 2 ** ((p - 69) / 12) * 440\n", " return freq_center\n", "\n", "chroma = ['A ', 'A#', 'B ', 'C ', 'C#', 'D ', 'D#', 'E ', 'F ', 'F#', 'G ', 'G#']\n", "\n", "for p in range(21, 109):\n", " print('p = %3d (%2s%1d), freq = %7.2f ' % (p, chroma[(p-69) % 12], (p//12-1), f_pitch(p)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "From this formula, it follows that the frequency ratio of two subsequent pitches $p+1$ and $p$ is constant:\n", "\n", "$$\n", "F_\\mathrm{pitch}(p+1)/F_\\mathrm{pitch}(p) = 2^{1/12} \\approx 1.059463\n", "$$\n", "\n", "Generalizing the notion of semitones, the **cent** denotes a logarithmic unit of measure used for musical intervals. By definition, an octave is divided into $1200$ cents, so that each semitone corresponds to $100$ cents. The difference in cents between two frequencies, say $\\omega_1$ and $\\omega_2$, is given by \n", "\n", "$$\n", "\\log_2\\left(\\frac{\\omega_1}{\\omega_2}\\right)\\cdot 1200.\n", "$$\n", "\n", "The interval of one cent is much too small to be heard between successive notes. The threshold of what is perceptible, also called the **just noticeable difference**, varies from person to person and depends on other aspects such as the timbre and the musical context. As a rule of thumb, normal adults are able to recognize pitch differences as small \n", "as $25$ cents very reliably, with differences of $10$ cents being recognizable only by trained listeners. As in illustration, we generate a sinusoid of $440~\\mathrm{Hz}$ used as reference and further sinusoids with various differences." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2024-02-15T08:45:18.250857Z", "iopub.status.busy": "2024-02-15T08:45:18.250461Z", "iopub.status.idle": "2024-02-15T08:45:18.303971Z", "shell.execute_reply": "2024-02-15T08:45:18.303049Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "freq = 440.0 Hz (MIDI note number 69 + 0.00 cents)\n" ] }, { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "freq = 442.0 Hz (MIDI note number 69 + 7.85 cents)\n" ] }, { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "freq = 445.0 Hz (MIDI note number 69 + 19.56 cents)\n" ] }, { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "freq = 450.0 Hz (MIDI note number 69 + 38.91 cents)\n" ] }, { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "freq = 880.0 Hz (MIDI note number 69 + 1200.00 cents)\n" ] }, { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "def difference_cents(freq_1, freq_2):\n", " \"\"\"Difference between two frequency values specified in cents\n", "\n", " Notebook: C1/C1S3_FrequencyPitch.ipynb\n", "\n", " Args:\n", " freq_1 (float): First frequency\n", " freq_2 (float): Second frequency\n", "\n", " Returns:\n", " delta (float): Difference in cents\n", " \"\"\"\n", " delta = np.log2(freq_1 / freq_2) * 1200\n", " return delta\n", " \n", "def generate_sinusoid(dur=5, Fs=1000, amp=1, freq=1, phase=0):\n", " \"\"\"Generation of sinusoid\n", "\n", " Notebook: C1/C1S3_FrequencyPitch.ipynb\n", "\n", " Args:\n", " dur (float): Duration (in seconds) (Default value = 5)\n", " Fs (scalar): Sampling rate (Default value = 1000)\n", " amp (float): Amplitude of sinusoid (Default value = 1)\n", " freq (float): Frequency of sinusoid (Default value = 1)\n", " phase (float): Phase of sinusoid (Default value = 0)\n", "\n", " Returns:\n", " x (np.ndarray): Signal\n", " t (np.ndarray): Time axis (in seconds)\n", "\n", " \"\"\"\n", " num_samples = int(Fs * dur)\n", " t = np.arange(num_samples) / Fs\n", " x = amp * np.sin(2*np.pi*(freq*t-phase))\n", " return x, t\n", "\n", "dur = 5\n", "Fs = 4000\n", "pitch = 69\n", "ref = f_pitch(pitch)\n", "freq_list = ref + np.array([0,2,5,10,ref])\n", "for freq in freq_list:\n", " x, t = generate_sinusoid(dur=dur, Fs=Fs, freq=freq)\n", " print('freq = %0.1f Hz (MIDI note number 69 + %0.2f cents)' % (freq, difference_cents(freq,ref)))\n", " ipd.display(ipd.Audio(data=x, rate=Fs)) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "Acknowledgment: This notebook was created by Meinard Müller and Stefan Balke.\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "
\"C0\"\"C1\"\"C2\"\"C3\"\"C4\"\"C5\"\"C6\"\"C7\"\"C8\"
" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.16" } }, "nbformat": 4, "nbformat_minor": 1 }