├── .gitignore ├── README.md ├── scales-notebook ├── .ipynb_checkpoints │ └── tutorial_scales_v01-checkpoint.ipynb ├── CPvsPop_Scales.png ├── K501_themeA.wav ├── K573_themeA.wav ├── M501_00_01a_a.krn ├── M573_00_01a_a.krn ├── TheWho_SDdistribution.png ├── TheWho_WontGetFooledAgain_Verse.wav ├── Tutorial_Scales.ipynb └── wont_get_fooled_again_dt.har └── slides ├── cadenceSlides.pdf ├── chordSlides.pdf ├── overviewSlides.pdf ├── scalesSlides.pdf ├── scalesSlides_handout.pdf └── wrapUpSlides.pdf /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ISMIR-musicTheoryTutorial 2 | 3 | This repository has slides and Jupyter notebooks for the ISMIR 2021 tutorial Scales, Chords, and Cadences: Practical Music Theory for MIR Researchers 4 | (https://ismir2021.ismir.net/tutorials/#3-scales-chords-and-cadences-practical-music-theory-for-mir-researchers) 5 | 6 | ## Tutorial Bibliography 7 | 8 | https://www.zotero.org/groups/4502273/ismir-musictheorytutorial 9 | 10 | ## Tutorial Description 11 | 12 | Much pitch-related MIR research builds either implicitly or explicitly on music-theoretic domain knowledge. Unfortunately, music theory is an esoteric discipline, with many of its canonical organizational principles presented in textbooks with dozens of classical musical examples and little indication of how these principles can be applied to other musical traditions. This tutorial will introduce fundamental pitch-related concepts in music theory for the ISMIR community and relate them to tasks associated with melodic, chord, and structural audio analysis for a range of musical styles. It will include sections on the scales, chords, and cadences routinely associated with Western art music of the common-practice tradition (~1650-1900), as well as non-Western folk musics and the popular music traditions of the twentieth and twenty-first centuries. The three sections will be broken down as follows, with both lecture and hands-on coding demonstration components: 13 | 14 | ### Scales 15 | -Scale formation (octave equivalence, mathematical properties) 16 | 17 | -Scale and mode types (western and non-Western) 18 | 19 | -Implications for scale and key identification, automatic melody extraction 20 | 21 | ### Chords 22 | -Types (triads, seventh chords, extensions) 23 | 24 | -Representation schemes (e.g., chord labeling) 25 | 26 | -Syntactic principles (e.g., functional harmony, grammars) 27 | 28 | -Implications for automatic chord recognition, pattern discovery 29 | 30 | ### Cadences 31 | -Types 32 | 33 | -Linear/voice-leading patterns 34 | 35 | -Relationship to large-scale formal types (phrases, themes, sonata, etc.) 36 | 37 | -Implications for cadence discovery/classification, automatic segmentation 38 | 39 | This tutorial will be of interest to a broad range of the ISMIR community, but will be of specific interest to MIR researchers with limited formal training in music theory. This workshop assumes a basic understanding of musical notation, but does not assume prior knowledge of Western music theory. It will be accessible to researchers new to the field, but will also be of interest to experienced researchers hoping to incorporate more music-theoretically based models into their research. 40 | 41 | -------------------------------------------------------------------------------- /scales-notebook/CPvsPop_Scales.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jcdevaney/ISMIR-musicTheoryTutorial/4360af13a09467734c159916dc5d4c0925cd1530/scales-notebook/CPvsPop_Scales.png -------------------------------------------------------------------------------- /scales-notebook/K501_themeA.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jcdevaney/ISMIR-musicTheoryTutorial/4360af13a09467734c159916dc5d4c0925cd1530/scales-notebook/K501_themeA.wav -------------------------------------------------------------------------------- /scales-notebook/K573_themeA.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jcdevaney/ISMIR-musicTheoryTutorial/4360af13a09467734c159916dc5d4c0925cd1530/scales-notebook/K573_themeA.wav -------------------------------------------------------------------------------- /scales-notebook/M501_00_01a_a.krn: -------------------------------------------------------------------------------- 1 | !!!COM: Mozart 2 | !!!OTL: Andante and variations in G for piano duet, K501 3 | !!!Variation: Theme a 4 | **function **harm **kern **kern **kern **kern 5 | * * *clefF4 *clefF4 *clefG2 *clefG2 6 | * * *k[f#] *k[f#] *k[f#] *k[f#] 7 | *M2/4 *M2/4 *M2/4 *M2/4 *M2/4 *M2/4 8 | *G: *G: *G: *G: *G: *G: 9 | 8T 8I 8r 8r 8r (8dd 10 | =1 =1 =1 =1 =1 =1 11 | 4T 4I 8G 8d 8g 8b 8dd 8ggL 12 | . . 8r 8r 8r 8bbJ 13 | 4T 4V7 8D 8d 8f# 8cc 8dd 8aaL 14 | . . 8r 8r 8r 8ff#J 15 | =2 =2 =2 =2 =2 =2 16 | 2T 2I 8GG 8d 8g 8b 8dd 4.ddd) 17 | . . 8r 8r 8r . 18 | . . 4r 4r 4r . 19 | . . . . . (16.bbLL 20 | . . . . . 32ggJJk) 21 | =3 =3 =3 =3 =3 =3 22 | 4D 4Cc/V 8AA 8d 8f# 8a 8dd 8aaL 23 | . . 8r 8r 8r (16.ff#L 24 | . . . . . 32ddJJk) 25 | 4D 4V7/V 8AA 8A 8g 8a 8cc# 8eeL 26 | . . 8r 8r 8r (16.eeL 27 | . . . . . 32ff#JJk) 28 | =4 =4 =4 =4 =4 =4 29 | 4.D 4.V 8DD 8D 8f# 8a 4.dd 30 | . . 4r 4r 4r . 31 | *- *- *- *- *- *- 32 | -------------------------------------------------------------------------------- /scales-notebook/M573_00_01a_a.krn: -------------------------------------------------------------------------------- 1 | !!!COM: Mozart 2 | !!!OTL: 9 Variationen uber ein Minuett von Duport 3 | !!!Variation: Theme a 4 | **function **harm **kern **kern 5 | * * *clefG2 *clefG2 6 | * * *k[f#c#] *k[f#c#] 7 | *M3/4 *M3/4 *M3/4 *M3/4 8 | *D: *D: *D: *D: 9 | =1 =1 =1 =1 10 | * * *^ * 11 | 2.T 2.I (8f#L 2.d 2aa 12 | . . 8aJ . . 13 | . . 8f#L . . 14 | . . 8aJ . . 15 | . . 8f#L . (8dddL 16 | . . 8aJ . 8aaJ 17 | =2 =2 =2 =2 =2 18 | 2.T 2.I 8f#L 2.d 2ff#) 19 | . . 8aJ . . 20 | . . 8f#L . . 21 | . . 8aJ . . 22 | . . 8f#L . (8aaL 23 | . . 8aJ . 8ff#J 24 | =3 =3 =3 =3 =3 25 | 2D 2Cc 8f#L 2.A 4dd) 26 | . . 8aJ . . 27 | . . 8f#L . 4dd 28 | . . 8aJ . . 29 | 4D 4V7 8c#L 8g . (8.eeL 30 | . . 8aJ . . 31 | . . . . 32ddLL 32 | . . . . 32eeJJJ 33 | =4 =4 =4 =4 =4 34 | 2.T 2.I 4f#) 4d 4ff# 35 | * * *v *v * 36 | . . 4r 4dd 37 | . . 4r 4a) 38 | *- *- *- *- 39 | -------------------------------------------------------------------------------- /scales-notebook/TheWho_SDdistribution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jcdevaney/ISMIR-musicTheoryTutorial/4360af13a09467734c159916dc5d4c0925cd1530/scales-notebook/TheWho_SDdistribution.png -------------------------------------------------------------------------------- /scales-notebook/TheWho_WontGetFooledAgain_Verse.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jcdevaney/ISMIR-musicTheoryTutorial/4360af13a09467734c159916dc5d4c0925cd1530/scales-notebook/TheWho_WontGetFooledAgain_Verse.wav -------------------------------------------------------------------------------- /scales-notebook/Tutorial_Scales.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "70ab7b26", 6 | "metadata": {}, 7 | "source": [ 8 | "# ISMIR 2021 -- Scales, chords, and cadences: Practical music theory for MIR researchers\n", 9 | "# Session 1 -- Scales" 10 | ] 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "id": "f728e9c8", 15 | "metadata": { 16 | "tags": [] 17 | }, 18 | "source": [ 19 | "***\n", 20 | "## 1 Scale Identification (Key Finding) -- Major/Minor\n", 21 | "### Music21 -- Symbolic Music\n", 22 | "Import libraries." 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": 14, 28 | "id": "f6cf8bb2", 29 | "metadata": {}, 30 | "outputs": [ 31 | { 32 | "ename": "ModuleNotFoundError", 33 | "evalue": "No module named 'pyACA'", 34 | "output_type": "error", 35 | "traceback": [ 36 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 37 | "\u001b[0;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)", 38 | "\u001b[0;32m/var/folders/qm/x0jnv3rn5nggh1kkgswgjjlc0000gn/T/ipykernel_18660/376771865.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mcollections\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mdeque\u001b[0m \u001b[0;31m# rotate function\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 8\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mIPython\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdisplay\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mipd\u001b[0m \u001b[0;31m# audio playback\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 9\u001b[0;31m \u001b[0;32mimport\u001b[0m \u001b[0mpyACA\u001b[0m \u001b[0;31m# audio content analysis\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 10\u001b[0m \u001b[0;31m#from IPython.display import Image # display png files\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mscipy\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstats\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mpearsonr\u001b[0m \u001b[0;31m# Pearson correlation\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 39 | "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'pyACA'" 40 | ] 41 | } 42 | ], 43 | "source": [ 44 | "from music21 import * # analysis package for symbolic data\n", 45 | "import numpy as np # matrix computing\n", 46 | "import librosa # music/audio package \n", 47 | "import librosa.display # for librosa chroma plot\n", 48 | "import matplotlib.pyplot as plt # plotting\n", 49 | "import seaborn as sns # data visualization based on matplotlib\n", 50 | "from collections import deque # rotate function\n", 51 | "import IPython.display as ipd # audio playback\n", 52 | "import pyACA # audio content analysis\n", 53 | "#from IPython.display import Image # display png files\n", 54 | "from scipy.stats import pearsonr # Pearson correlation" 55 | ] 56 | }, 57 | { 58 | "cell_type": "markdown", 59 | "id": "cf2e3d5b", 60 | "metadata": {}, 61 | "source": [ 62 | "#### 1.1 Example 1 -- TAVERN, K. 573\n", 63 | "Let's import the opening phrase from the first movement of Mozart's K. 573, encoded in .krn in the TAVERN data set. (You can download TAVERN here: https://github.com/jcdevaney/TAVERN.)" 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": 4, 69 | "id": "3784b5e6", 70 | "metadata": {}, 71 | "outputs": [ 72 | { 73 | "ename": "NameError", 74 | "evalue": "name 'converter' is not defined", 75 | "output_type": "error", 76 | "traceback": [ 77 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 78 | "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", 79 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtheme1\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mconverter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mparse\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"M573_00_01a_a.krn\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 80 | "\u001b[0;31mNameError\u001b[0m: name 'converter' is not defined" 81 | ] 82 | } 83 | ], 84 | "source": [ 85 | "theme1 = converter.parse(\"M573_00_01a_a.krn\")" 86 | ] 87 | }, 88 | { 89 | "cell_type": "markdown", 90 | "id": "09a35b64", 91 | "metadata": {}, 92 | "source": [ 93 | "Now let's visualize the score using Music21." 94 | ] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "execution_count": null, 99 | "id": "0c8543da", 100 | "metadata": {}, 101 | "outputs": [], 102 | "source": [ 103 | "theme1.show()" 104 | ] 105 | }, 106 | { 107 | "cell_type": "markdown", 108 | "id": "c34aeb56", 109 | "metadata": {}, 110 | "source": [ 111 | "One typical approach to identifying the operative key/scale is to compare a 0th-order distribution of pitch classes to the Krumhansl-Kessler (1982) key profiles. Here's the major-key profile for C major." 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": null, 117 | "id": "ca377582", 118 | "metadata": {}, 119 | "outputs": [], 120 | "source": [ 121 | "# KS algorithm.\n", 122 | "ks = analysis.discrete.KrumhanslSchmuckler()\n", 123 | "maj = ks.getWeights('major')\n", 124 | "min1 = ks.getWeights('minor')\n", 125 | "pcs = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']\n", 126 | "\n", 127 | "# Plot KS major-key figure.\n", 128 | "fig = plt.figure()\n", 129 | "ax = fig.add_axes([0,0,1,1])\n", 130 | "ax.plot(pcs,maj)\n", 131 | "ax.plot(pcs,min1)\n", 132 | "ax.set_ylabel('Degree of Fit')\n", 133 | "ax.set_xlabel('Pitch Classes')\n", 134 | "plt.ylim([1,7])\n", 135 | "plt.title('KS Major-Key Profile (1982)')\n", 136 | "plt.rcParams.update({'font.size': 14})\n", 137 | "plt.show()" 138 | ] 139 | }, 140 | { 141 | "cell_type": "markdown", 142 | "id": "91a15621", 143 | "metadata": {}, 144 | "source": [ 145 | "Now let's compute the distribution of pitch classes found in the Mozart theme." 146 | ] 147 | }, 148 | { 149 | "cell_type": "code", 150 | "execution_count": null, 151 | "id": "07fb86b0-2ed9-4141-93c3-06b148923d7c", 152 | "metadata": {}, 153 | "outputs": [], 154 | "source": [ 155 | "# Count PCs.\n", 156 | "cts = {'C':0, 'C#':0, 'D':0, 'D#':0, 'E':0, 'F':0, 'F#':0, 'G':0, 'G#':0, 'A':0, 'A#/Bb':0, 'B':0}\n", 157 | "PC = analysis.pitchAnalysis.pitchAttributeCount(theme1,'name')\n", 158 | "cts.update(PC)\n", 159 | "\n", 160 | "# Plot.\n", 161 | "fig = plt.figure()\n", 162 | "ax = fig.add_axes([0,0,1,1])\n", 163 | "ax.bar(cts.keys(),cts.values())\n", 164 | "ax.set_ylabel('Count')\n", 165 | "ax.set_xlabel('Pitch Classes')\n", 166 | "plt.title('PC Histogram')" 167 | ] 168 | }, 169 | { 170 | "cell_type": "markdown", 171 | "id": "b02e9773", 172 | "metadata": {}, 173 | "source": [ 174 | "Unsurprisingly, the PC content emphasizes scale-degrees associated with the D major scale. The KS algorithm identifies the key/scale of an excerpt by correlating the major and minor-key profiles for every starting pitch class with the distribution of pitch classes in the excerpt. Let's take this approach for our example and print the correlation coefficient for the most correlated key." 175 | ] 176 | }, 177 | { 178 | "cell_type": "code", 179 | "execution_count": null, 180 | "id": "ce1563ca", 181 | "metadata": {}, 182 | "outputs": [], 183 | "source": [ 184 | "key = theme1.analyze('key.krumhanslschmuckler')\n", 185 | "print(key)\n", 186 | "key.correlationCoefficient" 187 | ] 188 | }, 189 | { 190 | "cell_type": "markdown", 191 | "id": "5f74d595", 192 | "metadata": {}, 193 | "source": [ 194 | "The KS algorithm matches our intuitions! Here's the D major scale for comparison." 195 | ] 196 | }, 197 | { 198 | "cell_type": "code", 199 | "execution_count": null, 200 | "id": "d58e56a6", 201 | "metadata": {}, 202 | "outputs": [], 203 | "source": [ 204 | "from music21 import *\n", 205 | "m1 = stream.Measure()\n", 206 | "m1.timeSignature = meter.TimeSignature('4/4')\n", 207 | "m1.keySignature = key.KeySignature(2)\n", 208 | "m1.append([note.Note('D'), note.Note('E'), note.Note('F#'), note.Note('G')])\n", 209 | "m2 = stream.Measure()\n", 210 | "m2.append([note.Note('A'),note.Note('B'),note.Note('C#5'),note.Note('D5')])\n", 211 | "p = stream.Part()\n", 212 | "p.append([m1, m2])\n", 213 | "p.show()" 214 | ] 215 | }, 216 | { 217 | "cell_type": "markdown", 218 | "id": "f9c43036", 219 | "metadata": {}, 220 | "source": [ 221 | "#### 1.2 Example 2 -- TAVERN, K. 501\n", 222 | "What about for an excerpt that modulates from one key to another? Let's look at the opening phrase from Mozart, K. 501, which tonicizes (or modulates to) the key of the dominant." 223 | ] 224 | }, 225 | { 226 | "cell_type": "code", 227 | "execution_count": null, 228 | "id": "7c51009c", 229 | "metadata": {}, 230 | "outputs": [], 231 | "source": [ 232 | "theme2 = converter.parse(\"M501_00_01a_a.krn\")\n", 233 | "theme2.show()" 234 | ] 235 | }, 236 | { 237 | "cell_type": "markdown", 238 | "id": "e5b70cce", 239 | "metadata": {}, 240 | "source": [ 241 | "What's the key across the entire excerpt?" 242 | ] 243 | }, 244 | { 245 | "cell_type": "code", 246 | "execution_count": null, 247 | "id": "39fc6bf0", 248 | "metadata": {}, 249 | "outputs": [], 250 | "source": [ 251 | "# Count PCs.\n", 252 | "cts2 = {'C':0, 'C#':0, 'D':0, 'D#':0, 'E':0, 'F':0, 'F#':0, 'G':0, 'G#':0, 'A':0, 'A#/Bb':0, 'B':0}\n", 253 | "PC = analysis.pitchAnalysis.pitchAttributeCount(theme2,'name')\n", 254 | "cts2.update(PC)\n", 255 | "\n", 256 | "# KS algorithm\n", 257 | "key = theme2.analyze('key.krumhanslschmuckler')\n", 258 | "print(key)\n", 259 | "key.correlationCoefficient" 260 | ] 261 | }, 262 | { 263 | "cell_type": "markdown", 264 | "id": "bbcdea77", 265 | "metadata": {}, 266 | "source": [ 267 | "The excerpt begins in G major, but the KS algorithm predicts that the entire excerpt is in D major instead. We can address this issue by performing a windowed analysis using the KS algorithm." 268 | ] 269 | }, 270 | { 271 | "cell_type": "code", 272 | "execution_count": null, 273 | "id": "af6a18a5", 274 | "metadata": {}, 275 | "outputs": [], 276 | "source": [ 277 | "# Key finding over windows of various sizes (1-12 quarter-note beats).\n", 278 | "p = graph.plot.WindowedKey(theme2)\n", 279 | "p.run()" 280 | ] 281 | }, 282 | { 283 | "cell_type": "markdown", 284 | "id": "f090866c", 285 | "metadata": {}, 286 | "source": [ 287 | "The windowed analysis is better, but still doesn't really conform to musical intuitions. Perhaps listeners use more than 0th-order statistics to determine the key?" 288 | ] 289 | }, 290 | { 291 | "cell_type": "markdown", 292 | "id": "0396f886", 293 | "metadata": {}, 294 | "source": [ 295 | "***\n", 296 | "## 2 Scale Identification (Key Finding) -- Major/Minor \n", 297 | "### librosa -- Audio\n", 298 | "#### 2.1 Example 1 -- TAVERN, K. 573\n", 299 | "Sanity check: Let's try all of this again for the audio representation. You can get the audio for the first example here: https://imslp.org/wiki/9_Variations_on_a_Minuet_by_Duport%2C_K.573_(Mozart%2C_Wolfgang_Amadeus)" 300 | ] 301 | }, 302 | { 303 | "cell_type": "markdown", 304 | "id": "62ba1f97-5bab-4b23-8e0a-f1e3dd0fdbfc", 305 | "metadata": {}, 306 | "source": [ 307 | "Play the audio file." 308 | ] 309 | }, 310 | { 311 | "cell_type": "code", 312 | "execution_count": null, 313 | "id": "738bc384", 314 | "metadata": {}, 315 | "outputs": [], 316 | "source": [ 317 | "sig, sr = librosa.load('K573_themeA.wav',mono=True,sr=None)\n", 318 | "ipd.Audio('K573_themeA.wav') " 319 | ] 320 | }, 321 | { 322 | "cell_type": "markdown", 323 | "id": "62738b04", 324 | "metadata": {}, 325 | "source": [ 326 | "Separate the harmonic and percussive components." 327 | ] 328 | }, 329 | { 330 | "cell_type": "code", 331 | "execution_count": null, 332 | "id": "36670d1d", 333 | "metadata": {}, 334 | "outputs": [], 335 | "source": [ 336 | "sig_harmonic, sig_percussive = librosa.effects.hpss(sig)\n", 337 | "chroma = librosa.feature.chroma_cqt(sig_harmonic,sr)" 338 | ] 339 | }, 340 | { 341 | "cell_type": "markdown", 342 | "id": "c4213f85-83bf-498a-87b2-d868403a26e5", 343 | "metadata": {}, 344 | "source": [ 345 | "Plot the chromagram." 346 | ] 347 | }, 348 | { 349 | "cell_type": "code", 350 | "execution_count": null, 351 | "id": "4acb93a7", 352 | "metadata": {}, 353 | "outputs": [], 354 | "source": [ 355 | "plt.figure(figsize=(15, 4))\n", 356 | "librosa.display.specshow(chroma, y_axis='chroma', x_axis='time')\n", 357 | "plt.colorbar()\n", 358 | "plt.title('Power spectrum chromagram')\n", 359 | "plt.tight_layout()\n", 360 | "plt.show()" 361 | ] 362 | }, 363 | { 364 | "cell_type": "markdown", 365 | "id": "086706f0", 366 | "metadata": {}, 367 | "source": [ 368 | "Plot the chroma vector and compare it with the PC distribution we plotted earlier." 369 | ] 370 | }, 371 | { 372 | "cell_type": "code", 373 | "execution_count": null, 374 | "id": "9808efe7", 375 | "metadata": {}, 376 | "outputs": [], 377 | "source": [ 378 | "# Calculate average energy in each chroma bin.\n", 379 | "chroma_mean = np.mean(chroma,axis=1)\n", 380 | "\n", 381 | "# Replot PC histogram from symbolic representation.\n", 382 | "fig, axs = plt.subplots(1,2,figsize=(15,5))\n", 383 | "axs[0].bar(cts.keys(),cts.values())\n", 384 | "axs[0].set_ylabel('Count')\n", 385 | "axs[0].set_xlabel('Pitch Classes')\n", 386 | "axs[0].set_title('Symbolic')\n", 387 | "\n", 388 | "# Plot Chroma histogram from audio representation.\n", 389 | "axs[1].bar(pcs,chroma_mean)\n", 390 | "axs[1].set_ylabel('Proportion')\n", 391 | "axs[1].set_xlabel('Pitch Classes')\n", 392 | "axs[1].set_title('Audio')" 393 | ] 394 | }, 395 | { 396 | "cell_type": "markdown", 397 | "id": "3315ec8e", 398 | "metadata": {}, 399 | "source": [ 400 | "Let's compare this chroma vector to the KS major- and minor-key profiles using pyACA. (Note that pyACA uses a distance metric (Manhattan distance) instead of a correlation coefficient, so we'll have to compute the Pearson correlation manually.)" 401 | ] 402 | }, 403 | { 404 | "cell_type": "code", 405 | "execution_count": null, 406 | "id": "59ad4e36", 407 | "metadata": {}, 408 | "outputs": [], 409 | "source": [ 410 | "# Estimate key using pyACA.\n", 411 | "key = pyACA.computeKey(sig_harmonic,sr)\n", 412 | "print(key[0])\n", 413 | "\n", 414 | "# Correlate manually in order to compare with the symbolic example.\n", 415 | "chromas = np.roll(chroma_mean,-2) # shift to D major at starting position\n", 416 | "cor = pearsonr(maj,chromas)\n", 417 | "print(cor[0])" 418 | ] 419 | }, 420 | { 421 | "cell_type": "markdown", 422 | "id": "28638edf-3c9b-4561-82b0-a6d5b6ee01bc", 423 | "metadata": {}, 424 | "source": [ 425 | "#### 2.2 Example 2 -- TAVERN, K. 501" 426 | ] 427 | }, 428 | { 429 | "cell_type": "markdown", 430 | "id": "21ffbe4a", 431 | "metadata": {}, 432 | "source": [ 433 | "How will this method fare for K 501?" 434 | ] 435 | }, 436 | { 437 | "cell_type": "code", 438 | "execution_count": null, 439 | "id": "e22d25a4", 440 | "metadata": {}, 441 | "outputs": [], 442 | "source": [ 443 | "# Import and play the file.\n", 444 | "sig, sr = librosa.load('K501_themeA.wav',mono=True,sr=None)\n", 445 | "ipd.Audio('K501_themeA.wav') " 446 | ] 447 | }, 448 | { 449 | "cell_type": "markdown", 450 | "id": "94a817e3-b03c-47a2-be22-146abb5a9a63", 451 | "metadata": {}, 452 | "source": [ 453 | "Repeat the analysis from above." 454 | ] 455 | }, 456 | { 457 | "cell_type": "code", 458 | "execution_count": null, 459 | "id": "a54a13bd-53b9-42f7-b377-19c9d1631dcd", 460 | "metadata": {}, 461 | "outputs": [], 462 | "source": [ 463 | "# Separate harmonic and percussive components.\n", 464 | "sig_harmonic, sig_percussive = librosa.effects.hpss(sig)\n", 465 | "chroma = librosa.feature.chroma_cqt(sig_harmonic,sr)\n", 466 | "\n", 467 | "# Calculate average energy in each chroma bin.\n", 468 | "chroma_mean = np.mean(chroma,axis=1)\n", 469 | "\n", 470 | "# Replot PC histogram from symbolic representation.\n", 471 | "fig, axs = plt.subplots(1,2,figsize=(15,5))\n", 472 | "axs[0].bar(cts2.keys(),cts2.values())\n", 473 | "axs[0].set_ylabel('Count')\n", 474 | "axs[0].set_xlabel('Pitch Classes')\n", 475 | "axs[0].set_title('Symbolic')\n", 476 | "\n", 477 | "# Plot Chroma histogram from audio representation.\n", 478 | "axs[1].bar(pcs,chroma_mean)\n", 479 | "axs[1].set_ylabel('Proportion')\n", 480 | "axs[1].set_xlabel('Pitch Classes')\n", 481 | "axs[1].set_title('Audio')\n", 482 | "\n", 483 | "# Estimate key using pyACA.\n", 484 | "key = pyACA.computeKey(sig_harmonic,sr)\n", 485 | "print(key[0])\n", 486 | "\n", 487 | "# Correlate manually in order to compare with the symbolic example.\n", 488 | "chromas = np.roll(chroma_mean,-11) # shift to G major at starting position\n", 489 | "cor = pearsonr(min1,chromas)\n", 490 | "print(cor[0])" 491 | ] 492 | }, 493 | { 494 | "cell_type": "markdown", 495 | "id": "5c375ded-e2e1-4f5e-b47b-a65f52b48f21", 496 | "metadata": {}, 497 | "source": [ 498 | "Clearly key-finding for the audio representation is more challenging!" 499 | ] 500 | }, 501 | { 502 | "cell_type": "markdown", 503 | "id": "95290ceb", 504 | "metadata": {}, 505 | "source": [ 506 | "***\n", 507 | "\n", 508 | "## 3 Scale Identification (Key Finding) -- Beyond Major/Minor\n", 509 | "### librosa -- Audio\n", 510 | "#### 3.1 The Who\n", 511 | "\n", 512 | "Many of the popular music traditions encountered on the radio reflect other scale systems. The Who's \"Won't Get Fooled Again\" is one such example. Let's listen to the verse." 513 | ] 514 | }, 515 | { 516 | "cell_type": "code", 517 | "execution_count": null, 518 | "id": "d796d8f8", 519 | "metadata": {}, 520 | "outputs": [], 521 | "source": [ 522 | "sig, sr = librosa.load('TheWho_WontGetFooledAgain_Verse.wav',mono=True,sr=None)\n", 523 | "ipd.Audio(sig,rate=sr)" 524 | ] 525 | }, 526 | { 527 | "cell_type": "markdown", 528 | "id": "ea407b70", 529 | "metadata": {}, 530 | "source": [ 531 | "Now let's estimate a chromagram using librosa." 532 | ] 533 | }, 534 | { 535 | "cell_type": "code", 536 | "execution_count": null, 537 | "id": "3bdbdd5c", 538 | "metadata": {}, 539 | "outputs": [], 540 | "source": [ 541 | "# Separate harmonic and percussive components.\n", 542 | "sig_harmonic, sig_percussive = librosa.effects.hpss(sig)\n", 543 | "chroma = librosa.feature.chroma_cqt(sig_harmonic,sr)\n", 544 | "\n", 545 | "# Calculate average energy in each chroma bin.\n", 546 | "chroma_mean = np.mean(chroma,axis=1)\n", 547 | "\n", 548 | "# Replot PC histogram from symbolic representation.\n", 549 | "fig = plt.figure()\n", 550 | "ax = fig.add_axes([0,0,1,1])\n", 551 | "ax.bar(pcs,chroma_mean)\n", 552 | "ax.set_ylabel('Proportion')\n", 553 | "ax.set_xlabel('Chromas')\n", 554 | "ax.set_title('The Who -- Wont Get Fooled Again')" 555 | ] 556 | }, 557 | { 558 | "cell_type": "markdown", 559 | "id": "5cc025cf", 560 | "metadata": {}, 561 | "source": [ 562 | "#### 3.2 Annotations -- RS-200\n", 563 | "Rather than work from the audio representation, let's look at the chord annotations for this song in the RollingStone-200 data set: http://rockcorpus.midside.com/" 564 | ] 565 | }, 566 | { 567 | "cell_type": "code", 568 | "execution_count": null, 569 | "id": "41c432d5", 570 | "metadata": {}, 571 | "outputs": [], 572 | "source": [ 573 | "text_file = open('wont_get_fooled_again_dt.har')\n", 574 | "file_content = text_file.read()\n", 575 | "print(file_content)" 576 | ] 577 | }, 578 | { 579 | "cell_type": "markdown", 580 | "id": "6c486508", 581 | "metadata": {}, 582 | "source": [ 583 | "Here's a plot of the scale-degree content extracted from the chord labels in the RollingStone-200 data set for this song. (The code is available here: https://github.com/PeARL-laboratory/ScalesinPop)" 584 | ] 585 | }, 586 | { 587 | "cell_type": "code", 588 | "execution_count": null, 589 | "id": "cee38b54", 590 | "metadata": {}, 591 | "outputs": [], 592 | "source": [ 593 | "Image(filename='TheWho_SDdistribution.png',width=500,height=500)" 594 | ] 595 | }, 596 | { 597 | "cell_type": "markdown", 598 | "id": "d06aa0fc", 599 | "metadata": {}, 600 | "source": [ 601 | "This looks like the mixolydian mode! Flat-side modes like mixolydian, dorian, and aeolian are common in popular music." 602 | ] 603 | }, 604 | { 605 | "cell_type": "markdown", 606 | "id": "aa14bf85", 607 | "metadata": {}, 608 | "source": [ 609 | "We can expand this discussion to look at the scale systems found in the Billboard and RS-200 data sets using a topic model that identifies topics reflected in the chord annotations. When we visualize the scale-degree content found in those topics, distinct scale systems emerge. (Check out my late-breaking demo with Justin Glosson for further details! https://archives.ismir.net/ismir2021/latebreaking/000048.pdf)" 610 | ] 611 | }, 612 | { 613 | "cell_type": "code", 614 | "execution_count": null, 615 | "id": "763dfc30", 616 | "metadata": {}, 617 | "outputs": [], 618 | "source": [ 619 | "Image(filename='CPvsPop_Scales.png',width=900,height=500)" 620 | ] 621 | }, 622 | { 623 | "cell_type": "markdown", 624 | "id": "c5a0c93e-109f-4b54-a8d3-05cc7e580bb3", 625 | "metadata": {}, 626 | "source": [ 627 | "## Takeaways:\n", 628 | "1) Develop unsupervised/supervised methods for scale/key detection that rely on richer (i.e., temporal) representations of the musical object (e.g., higher-order distributions).\n", 629 | "2) Look beyond the major/minor dichotomy!" 630 | ] 631 | } 632 | ], 633 | "metadata": { 634 | "interpreter": { 635 | "hash": "beb8ae63a74e57b2145d688a52e22bfb5105f39ea9e8338900fb11620b79bce8" 636 | }, 637 | "kernelspec": { 638 | "display_name": "Python 3.7.3 64-bit ('3.7.3': pyenv)", 639 | "name": "python3" 640 | }, 641 | "language_info": { 642 | "codemirror_mode": { 643 | "name": "ipython", 644 | "version": 3 645 | }, 646 | "file_extension": ".py", 647 | "mimetype": "text/x-python", 648 | "name": "python", 649 | "nbconvert_exporter": "python", 650 | "pygments_lexer": "ipython3", 651 | "version": "3.7.3" 652 | } 653 | }, 654 | "nbformat": 4, 655 | "nbformat_minor": 5 656 | } 657 | -------------------------------------------------------------------------------- /scales-notebook/wont_get_fooled_again_dt.har: -------------------------------------------------------------------------------- 1 | 2 | % Won't Get Fooled Again 3 | 4 | BP: I | bVII IV | % Often very ambiguous. Very clear in the intro; in the verse, could just be I or sometimes I IV. 5 | BP2: I | IV | % Again ambiguous; sometimes just I, e.g. in parts of the coda 6 | In: I |*17 $BP*4 % Could be analyzed as changing harmony 7 | Vr: $BP*3 V | bIII bVII | % Sometimes mm. 5-6 are I | IV I | 8 | CP: IV I | 9 | Ch: $CP*6 bVII | V | bVII | V | bVII | IV | | | | $BP*2 10 | Br: II | | V | | I | | II | | [B] $BP*12 [A] $BP*4 11 | Coda: $BP2*16 I |*39 [2/4] | [4/4] $BP*10 I | | | % 32 mm. of band; then 39.5 mm. of synth (EE-DD in lower voice starts on 12.5; 12 | % unison A starts on 32.5; drums on 33), then scream and 20 mm. of band, then 13 | % 3 mm. of final I. 14 | % Total from end of 3rd chorus vocal to scream: 4 + 32 + 39.5 = 75.5 15 | S: [A] $In $Vr*2 $Ch $BP*2 $Vr*2 $Ch $BP2*8 $Br $Vr*2 $Ch $Coda 16 | 17 | -------------------------------------------------------------------------------- /slides/cadenceSlides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jcdevaney/ISMIR-musicTheoryTutorial/4360af13a09467734c159916dc5d4c0925cd1530/slides/cadenceSlides.pdf -------------------------------------------------------------------------------- /slides/chordSlides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jcdevaney/ISMIR-musicTheoryTutorial/4360af13a09467734c159916dc5d4c0925cd1530/slides/chordSlides.pdf -------------------------------------------------------------------------------- /slides/overviewSlides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jcdevaney/ISMIR-musicTheoryTutorial/4360af13a09467734c159916dc5d4c0925cd1530/slides/overviewSlides.pdf -------------------------------------------------------------------------------- /slides/scalesSlides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jcdevaney/ISMIR-musicTheoryTutorial/4360af13a09467734c159916dc5d4c0925cd1530/slides/scalesSlides.pdf -------------------------------------------------------------------------------- /slides/scalesSlides_handout.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jcdevaney/ISMIR-musicTheoryTutorial/4360af13a09467734c159916dc5d4c0925cd1530/slides/scalesSlides_handout.pdf -------------------------------------------------------------------------------- /slides/wrapUpSlides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jcdevaney/ISMIR-musicTheoryTutorial/4360af13a09467734c159916dc5d4c0925cd1530/slides/wrapUpSlides.pdf --------------------------------------------------------------------------------