{ "cells": [ { "cell_type": "markdown", "id": "c9f1fab6-fafc-4735-84b2-1466cf6114df", "metadata": { "tags": [] }, "source": [ "# Week 14: Generating Music\n", "\n", " Laboratory 9
\n", " Last updated November 28, 2023 " ] }, { "cell_type": "markdown", "id": "e432c64d-f45c-4434-9636-8e6a5501d779", "metadata": {}, "source": [ "## 00. Content " ] }, { "cell_type": "markdown", "id": "134e8e5b-6874-4c95-a28f-931bf238b4ab", "metadata": { "tags": [] }, "source": [ " Mathematics \n", "- N/A\n", " \n", " Programming Skills \n", "- Functions\n", "- Arrays\n", " \n", " Embedded Systems \n", "- N/A" ] }, { "cell_type": "markdown", "id": "1d6078ec-48f8-4153-a1b5-91ae76996fd2", "metadata": {}, "source": [ "

Write your name and email below:

\n", "\n", "**Name:** \n", "\n", "**Email:** " ] }, { "cell_type": "markdown", "id": "64a8fa7a-58da-44bb-aeb4-2b8a5d617efb", "metadata": {}, "source": [ "## 1. Music Generation from Random Processes \n", "\n", "In this lab, we will use random processes to automatically generate music. \n", " \n", "So we will now move from the concept of random variable to the concept of random process, which we define below." ] }, { "cell_type": "markdown", "id": "5de33e44-642a-45b2-88ce-8adde3412d37", "metadata": {}, "source": [ "

Definitions:

\n", "A (discrete-time) random process is a sequence of random variables $X_n$, where $n$ ranges over a finite or countable number of integers.\n", "Another name for “random process” is “stochastic process.” You may also encounter the term “time series.” A time series is a sample from a random process. In other works, a time series is a set of values $x_n$, where $x_n$ is a sample (i.e., a set value) of the random variable $X_n$.\n", " " ] }, { "cell_type": "markdown", "id": "61d6130a-b3f2-475b-8b08-6fcf6fcee1f4", "metadata": {}, "source": [ "\n", "Let us construct a very simple piece of music at random. \n", " \n", "The piece of music will be in the “C Major” tonality (i.e. the key of C Major), which means that the notes that we play will be restricted to the notes of the C Major scale, which is composed of the following sequence of notes: \n" ] }, { "cell_type": "code", "execution_count": 15, "id": "38b7a5fd-fb2c-4bdb-a1aa-0818dcfb41ae", "metadata": { "tags": [] }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Frequency
C261.625565
C#277.182631
D293.664768
D#311.126984
E329.627557
F349.228231
F#369.994423
G391.995436
G#415.304698
A440.000000
A#466.163762
B493.883301
\n", "
" ], "text/plain": [ " Frequency\n", "C 261.625565\n", "C# 277.182631\n", "D 293.664768\n", "D# 311.126984\n", "E 329.627557\n", "F 349.228231\n", "F# 369.994423\n", "G 391.995436\n", "G# 415.304698\n", "A 440.000000\n", "A# 466.163762\n", "B 493.883301" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import numpy as np\n", "import pandas as pd\n", "\n", "notes = \"C C# D D# E F F# G G# A A# B\"\n", "scale = pd.Series(440*2**((np.arange(12)-9)/12), index = notes.split())\n", "\n", "\n", "summary = pd.DataFrame({\n", " 'Frequency': scale,\n", "})\n", "summary" ] }, { "cell_type": "markdown", "id": "2b9b8148-98d5-460f-838e-141cdfbfcb0e", "metadata": {}, "source": [ "The following code will play a note at a frequency of 262 Hz for 1 second.\n" ] }, { "cell_type": "code", "execution_count": 20, "id": "e233cf28-b238-4c52-88c0-4b77a97de1f7", "metadata": { "tags": [] }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from IPython.display import Audio\n", "\n", "\n", "def get_sine_wave(frequency, duration, sample_rate=44100, amplitude=4096):\n", " t = np.linspace(0, duration, int(sample_rate*duration))\n", " wave = amplitude*np.sin(2*np.pi*frequency*t)\n", " return wave\n", "\n", "\n", "wave = get_sine_wave(262, 1)\n", "\n", "Audio(wave, rate = 44100)" ] }, { "cell_type": "markdown", "id": "38d444da-170d-447f-9235-1bea536d4fce", "metadata": {}, "source": [ "### Exercise 1\n", "\n", "\n", "Modify this code to play a tune consisting of sequence of 20 notes with the same duration, where each note is randomly chosen following a uniform distribution on the 8 notes of the C Major scale. \n" ] }, { "cell_type": "code", "execution_count": null, "id": "8380d25a-2da3-431c-a697-283843c21db1", "metadata": { "tags": [] }, "outputs": [], "source": [] }, { "cell_type": "markdown", "id": "39dc17f6-ffc2-4da4-b70b-c4874f194713", "metadata": {}, "source": [ "Which of the results that you produced is a time series? Which one is a random process?\n", " \n", "Each of the tunes you generated is a time series. The process used to generate it is a random (stochastic) process. You can reuse the same process to generate a different music (different time series, but the process would be the same.)\n", " \n", "\n", " " ] }, { "cell_type": "markdown", "id": "36bc22dc-e0c7-48b8-9e13-3c51c5bd607b", "metadata": {}, "source": [] }, { "cell_type": "markdown", "id": "75b240b0-1e45-40f4-b8a9-d370f6924823", "metadata": {}, "source": [ "### Exercise 2\n", "\n", "\n", "Modify the probability distribution in the previous exercise to try to improve your tune. Listen to the tunes played by other students. Is there a probability distribution that sounds more pleasing? (hint: try to play the 1st, 3rd, and 5th note most often, and increase the probability of the 4th note slightly over that of the others.) \n", " \n" ] }, { "cell_type": "code", "execution_count": null, "id": "1291f0cd-63e0-4d49-ad16-66902670fd55", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "id": "d8c6d6b7-90cb-4a12-b6b6-580456e689cb", "metadata": {}, "source": [ "What is the probability mass function corresponding to the tune you played ?\n", "\n", "The probability mass function is a vector containing the probability of playing each note. The sum of the entries of the vector should equal to 1.\n", "\n", "\n", "Now we will use two dimensional random variables to add rhythm to the tune.\n", " " ] }, { "cell_type": "markdown", "id": "366d9047-1da7-4098-8cc0-99f8bd4fc1bf", "metadata": { "tags": [] }, "source": [ "### Exercise 3\n", "\n", "Create a sequence of 20 two dimensional random variables ($X_n$ ,$Y_n$ ), for $n$=1,…,20, where $X_n$ is an integer between 1 and 8 representing the note, and $Y_n$ is a fraction (either ¼, ½, or 1) representing the duration of the note. \n", " \n", "Question: What is the probability mass function you used for each $X_n$ ? What is the probability mass function you used for each $Y_n$ ? What is the joint probability mass function you used for the two dimensional random variables ($X_n$,$Y_n$)? Are $X_n$ and $Y_n$ independent in your model?\n", " " ] }, { "cell_type": "code", "execution_count": null, "id": "dcfca719-2455-4879-8569-7921bb77d4e7", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "id": "62fc8734-e6e5-4f8b-bd0a-fad17bff694d", "metadata": {}, "source": [ " Adding Rhythm - Patterns \n", "\n", "A more natural sounding way to add rhythm is to pick among short rhythm patterns. For example, one can pick (e.g., uniformly) from the following rhythm patterns:\n", " \n", "- Rhythm Pattern 1: 1 note lasting 1 second\n", "- Rhythm Pattern 2: 2 notes per second, each lasting one half second\n", "- Rhythm Pattern 3: 3 notes per second- each lasting one third of a second\n", "- Rhythm Pattern 4: 4 notes per second – each lasting one quarter of a second\n", "- Rhythm Pattern 5: 1 note for half a second, followed by 2 notes each lasting a quarter of a second." ] }, { "cell_type": "markdown", "id": "92932045-2dce-4afb-8723-d6ebe2c01dbe", "metadata": {}, "source": [ "### Exercise 4\n", "\n", " \n", "Pick the rhythm pattern at random, and assign the notes in the pattern at random, to play a song in the C Major scale. " ] }, { "cell_type": "code", "execution_count": null, "id": "a6f2d3f5-0b85-413f-b2c0-97009afe5df4", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "id": "16aa40de-5ea8-4fc6-afdc-c421048cda48", "metadata": {}, "source": [ " Combining note patterns and rhythms patterns \n", "\n", "Define a number of two-note patterns, for example:\n", " \n", "- two-note patterns 1: Note, Note+1\n", "- two-note patterns 2: Note, Note+2\n", "- two-note patterns 3: Note, Note+4\n", "\n", "Similarly, define a number of three-note patterns and 4 note patterns, for example:\n", "\n", "- three-note patterns 1: Note, Note+1, Note+2\n", "- three-note patterns 2: Note, Note+2, Note+4\n", "- four-note patterns 1: Note, Note+1, Note+2, Note+3\n", "- four-note patterns 2: Note, Note+2, Note, Note+4" ] }, { "cell_type": "markdown", "id": "b0d19d56-ad1b-49e3-9a99-6abfac08b584", "metadata": {}, "source": [ "### Exercise 5\n", "\n", "Create a tune by picking values of a series of 20 two-dimensional random variables ($X_n$,$Y_n$)\n", "Where $X_n$ is the Rhythm pattern and $Y_n$ is the Note pattern that fits $X_n$.\n", "\n", "Are $X_n$ and $Y_n$ independent?" ] }, { "cell_type": "code", "execution_count": null, "id": "1ae881c2-9b59-4643-9c10-4ae92679afa5", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "id": "fd49dd16-8af9-400e-8154-2fb83cf1b355", "metadata": {}, "source": [ "\n", "In the previous exercise, the note pattern was fixed. \n", "Now let us try to generate the note patterns at random using a transition matrix.\n", "\n", "\n", "### Exercise 6\n", "\n", "The given code uses a transition matrix for generating musical notes (all of the same duration).\n", "\n", "Run the code and explain how we are generating random sequences of music. (Hint: random.choices() function)\n", "\n", "This code uses mingus and midiutil which can be installed from terminal by using: pip install < package> or conda install < package>.\n", "\n", "MIDI is the shortform for Music Instrument Digital Interface. It is a standard industrial format for creating digital music. This is the file format used by Artists all over the world to create there music at the moment using applications like Logic Pro, Garageband etc.\n", "midiutil library is a popular library for writing music in python. It provides a simple way to add notes to a music track by just specifing the note, its durarion and amplitude. \n", "\n", "The resulting midi file can be played using vlc player, garageband, logic pro etc.\n", "\n", "If for some reason this code is causing issues, I have added a more compatible version in the next code block. (P.S the MIDI version sounds the best).\n", "\n", "Usage of Mingus will be explained below." ] }, { "cell_type": "code", "execution_count": 6, "id": "6f676c77-e19e-4e28-b124-d0d805a93dfe", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "******************************************************************************\n", "--------------------- T R A N S I T I O N M A T R I X ---------------------\n", "[[0.3 0. 0. 0. 0.3 0. 0. 0.3 0. 0. 0. 0.1]\n", " [0.1 0.3 0. 0. 0. 0.3 0. 0. 0.3 0. 0. 0. ]\n", " [0. 0.1 0.3 0. 0. 0. 0.3 0. 0. 0.3 0. 0. ]\n", " [0. 0. 0.1 0.3 0. 0. 0. 0.3 0. 0. 0.3 0. ]\n", " [0. 0. 0. 0.1 0.3 0. 0. 0. 0.3 0. 0. 0.3]\n", " [0.3 0. 0. 0. 0.1 0.3 0. 0. 0. 0.3 0. 0. ]\n", " [0. 0.3 0. 0. 0. 0.1 0.3 0. 0. 0. 0.3 0. ]\n", " [0. 0. 0.3 0. 0. 0. 0.1 0.3 0. 0. 0. 0.3]\n", " [0.3 0. 0. 0.3 0. 0. 0. 0.1 0.3 0. 0. 0. ]\n", " [0. 0.3 0. 0. 0.3 0. 0. 0. 0.1 0.3 0. 0. ]\n", " [0. 0. 0.3 0. 0. 0.3 0. 0. 0. 0.1 0.3 0. ]\n", " [0. 0. 0. 0.3 0. 0. 0.3 0. 0. 0. 0.1 0.3]]\n", "******************************************************************************\n", "******************************************************************************\n", "------------------------------------ N O T E S -------------------------------\n", "['A', 'F', 'C', 'C', 'E', 'E', 'G', 'B', 'A', 'C', 'C', 'F', 'G', 'D', 'D', 'D']\n", "******************************************************************************\n" ] } ], "source": [ "#imports\n", "import numpy as np\n", "import random\n", "\n", "from mingus.core import chords\n", "from midiutil import MIDIFile\n", "\n", "\n", "keys = ['C', 'C#', 'D', 'Eb', 'E', 'F', 'F#', 'G', 'Ab', 'A', 'Bb', 'B']\n", "#major_7th_chords = ['CM7', 'C#M7', 'DM7', 'EbM7', 'EM7', 'FM7', 'F#M7', 'GM7', 'AbM7', 'AM7', 'BbM7', 'BM7']\n", "key_map = {\"C\": 0, \"C#\":1, \"D\":2, \"Eb\":3, \"E\":4, \"F\":5, \"F#\":6, \"G\":7, \"Ab\":8, \"A\":9, \"Bb\":10, \"B\":11}\n", "\n", "def standardize_notation(note):\n", " \n", " if note == 'A#':\n", " note = 'Bb'\n", " elif note == 'E#':\n", " note = 'F'\n", " elif note == 'Gb':\n", " note = 'F#'\n", " elif note == 'G#':\n", " note = 'Ab'\n", " elif note == 'B#':\n", " note = 'C'\n", " elif note == 'Db':\n", " note = 'C#'\n", " elif note == 'D#':\n", " note = 'Eb'\n", "\n", " return note\n", "\n", "# here we are using midi-util library to generate MIDI which is shortform for Music Instrument Digital Interface.\n", "# For working with the library we need to convert the notes into numbers.\n", "\n", "def get_midi_representation_of_note(note, octave):\n", " \n", " note = standardize_notation(note)\n", " assert(octave <= 12 and octave >= 0)\n", "\n", " note = key_map[note]\n", " note += (12 * octave)\n", "\n", " assert(note <= 127 and note >= 0)\n", "\n", " return note\n", "\n", "transition_matrix = np.zeros((12, 12))\n", "\n", "\n", "#create transition matrix. This Transition matrix is made assuming all the chords are Major 7 chords\n", "for key in keys:\n", " \n", " #major seventh chord\n", " chord = key + \"M7\"\n", " preferred_keys = chords.from_shorthand(chord)\n", " \n", " major_chord_pos = key_map[key]\n", " \n", " assert(len(preferred_keys) == 4)\n", " \n", " for i in range(3):\n", " transition_matrix[major_chord_pos, key_map[standardize_notation(preferred_keys[i])]] = 0.3\n", " \n", " transition_matrix[major_chord_pos, key_map[standardize_notation(preferred_keys[3])]] = 0.1\n", "print(\"******************************************************************************\")\n", "print(\"--------------------- T R A N S I T I O N M A T R I X ---------------------\")\n", " \n", "print(transition_matrix)\n", "print(\"******************************************************************************\")\n", "\n", "#major 7 chords\n", "chord_progression = [\"F\", \"C\", \"F\", \"G\"]\n", "\n", "notes = []\n", "\n", "for chord in chord_progression:\n", " chord = standardize_notation(chord)\n", " \n", " key_probabilities_for_chord = transition_matrix[key_map[chord]]\n", " \n", " # play 4 notes for each chord\n", " for i in range(4):\n", " notes.extend(random.choices(keys, weights=key_probabilities_for_chord, k=1))\n", "print(\"******************************************************************************\")\n", "print(\"------------------------------------ N O T E S -------------------------------\")\n", "\n", "print(notes)\n", "\n", "print(\"******************************************************************************\")\n", "\n", "t = 0 # In beats\n", "dur = 1 # In beats\n", "tempo = 120 # In BPM\n", "vol = 100 # 0-127, as per the MIDI standard\n", "\n", "MyMIDI = MIDIFile(1) \n", "MyMIDI.addTempo(0, t, tempo)\n", "\n", "\n", "for i, note in enumerate(notes):\n", " # get note number\n", " pitch = get_midi_representation_of_note(note = note, octave = 4)\n", " \n", " # adding individual note to the track\n", " MyMIDI.addNote(track = 0, channel = 0, pitch = pitch, time = t + i, duration = dur, volume = vol)\n", "\n", "\n", "\n", "with open(\"midi1.mid\", \"wb\") as output_file:\n", " MyMIDI.writeFile(output_file)\n", " \n", " " ] }, { "cell_type": "code", "execution_count": 7, "id": "51ba4943-2a65-410d-9dcf-194e50b8a8b5", "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "******************************************************************************\n", "--------------------- T R A N S I T I O N M A T R I X ---------------------\n", "[[0.3 0. 0. 0. 0.3 0. 0. 0.3 0. 0. 0. 0.1]\n", " [0.1 0.3 0. 0. 0. 0.3 0. 0. 0.3 0. 0. 0. ]\n", " [0. 0.1 0.3 0. 0. 0. 0.3 0. 0. 0.3 0. 0. ]\n", " [0. 0. 0.1 0.3 0. 0. 0. 0.3 0. 0. 0.3 0. ]\n", " [0. 0. 0. 0.1 0.3 0. 0. 0. 0.3 0. 0. 0.3]\n", " [0.3 0. 0. 0. 0.1 0.3 0. 0. 0. 0.3 0. 0. ]\n", " [0. 0.3 0. 0. 0. 0.1 0.3 0. 0. 0. 0.3 0. ]\n", " [0. 0. 0.3 0. 0. 0. 0.1 0.3 0. 0. 0. 0.3]\n", " [0.3 0. 0. 0.3 0. 0. 0. 0.1 0.3 0. 0. 0. ]\n", " [0. 0.3 0. 0. 0.3 0. 0. 0. 0.1 0.3 0. 0. ]\n", " [0. 0. 0.3 0. 0. 0.3 0. 0. 0. 0.1 0.3 0. ]\n", " [0. 0. 0. 0.3 0. 0. 0.3 0. 0. 0. 0.1 0.3]]\n", "******************************************************************************\n", "******************************************************************************\n", "------------------------------------ N O T E S -------------------------------\n", "['E', 'F', 'A', 'C', 'E', 'C', 'G', 'B', 'A', 'A', 'C', 'A', 'D', 'D', 'D', 'G']\n", "******************************************************************************\n" ] }, { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "notes = \"A Bb B C C# D Eb E F F# G Ab\"\n", "\n", "\n", "full_scale = {}\n", "for i in range(1, 7):\n", " for j, letter in enumerate(notes.split()):\n", " full_scale[letter + str(i)] = 440 * 2 ** (i - 4 + j / 12)\n", "full_scale\n", "\n", "\n", "#imports\n", "import numpy as np\n", "import random\n", "from IPython.display import Audio\n", "\n", "from mingus.core import chords\n", "\n", "\n", "keys = ['C', 'C#', 'D', 'Eb', 'E', 'F', 'F#', 'G', 'Ab', 'A', 'Bb', 'B']\n", "#major_7th_chords = ['CM7', 'C#M7', 'DM7', 'EbM7', 'EM7', 'FM7', 'F#M7', 'GM7', 'AbM7', 'AM7', 'BbM7', 'BM7']\n", "key_map = {\"C\": 0, \"C#\":1, \"D\":2, \"Eb\":3, \"E\":4, \"F\":5, \"F#\":6, \"G\":7, \"Ab\":8, \"A\":9, \"Bb\":10, \"B\":11}\n", "\n", "def standardize_notation(note):\n", " \n", " if note == 'A#':\n", " note = 'Bb'\n", " elif note == 'E#':\n", " note = 'F'\n", " elif note == 'Gb':\n", " note = 'F#'\n", " elif note == 'G#':\n", " note = 'Ab'\n", " elif note == 'B#':\n", " note = 'C'\n", " elif note == 'Db':\n", " note = 'C#'\n", " elif note == 'D#':\n", " note = 'Eb'\n", "\n", " return note\n", "\n", "def get_sine_wave(frequency, duration, sample_rate=44100, amplitude=4096):\n", " t = np.linspace(0, duration, int(sample_rate*duration))\n", " wave = amplitude*np.sin(2*np.pi*frequency*t)\n", " return wave\n", "\n", "\n", "transition_matrix = np.zeros((12, 12))\n", "\n", "\n", "#create transition matrix. Transition matrix is made assuming all the chords are Major 7 chords\n", "for key in keys:\n", " \n", " #major seventh chord\n", " chord = key + \"M7\"\n", " preferred_keys = chords.from_shorthand(chord)\n", " \n", " major_chord_pos = key_map[key]\n", " \n", " assert(len(preferred_keys) == 4)\n", " \n", " for i in range(3):\n", " transition_matrix[major_chord_pos, key_map[standardize_notation(preferred_keys[i])]] = 0.3\n", " \n", " transition_matrix[major_chord_pos, key_map[standardize_notation(preferred_keys[3])]] = 0.1\n", " \n", "print(\"******************************************************************************\")\n", "print(\"--------------------- T R A N S I T I O N M A T R I X ---------------------\")\n", " \n", "print(transition_matrix)\n", "print(\"******************************************************************************\")\n", "\n", "#major 7 chords\n", "chord_progression = [\"F\", \"C\", \"F\", \"G\"]\n", "\n", "notes = []\n", "\n", "for chord in chord_progression:\n", " chord = standardize_notation(chord)\n", " \n", " key_probabilities_for_chord = transition_matrix[key_map[chord]]\n", " \n", " # play 4 notes for each chord\n", " for i in range(4):\n", " notes.extend(random.choices(keys, weights=key_probabilities_for_chord, k=1))\n", "\n", "print(\"******************************************************************************\")\n", "print(\"------------------------------------ N O T E S -------------------------------\")\n", "\n", "print(notes)\n", "\n", "print(\"******************************************************************************\")\n", "\n", "track1 = []\n", "\n", "\n", "for i, note in enumerate(notes):\n", " \n", " frequency = full_scale[note+\"4\"]\n", " track1.extend(get_sine_wave(frequency = frequency, duration = 1, sample_rate=44100, amplitude=4096))\n", " \n", "\n", "Audio(track1, rate = 44100)" ] }, { "cell_type": "markdown", "id": "c987acbd-0791-4be4-abc5-f690a5055003", "metadata": {}, "source": [ " Mingus Python Package \n", "\n", "\n", "Mingus is an advanced, cross-platform music theory and notation package for Python with MIDI file and playback support.\n", "\n", "Python code to get the note combinations in a chord:" ] }, { "cell_type": "code", "execution_count": 11, "id": "bf62080c-fbbc-401e-9995-3db3b35bd321", "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['C', 'E', 'G']\n" ] } ], "source": [ "from mingus.core import chords\n", "\n", "# To find the notes in C Major chord.\n", "result = chords.from_shorthand(\"Cmaj\")\n", "\n", "print(result)" ] }, { "cell_type": "markdown", "id": "2681c9dc-0ccb-4cd9-8f15-cd9672615eaf", "metadata": { "tags": [] }, "source": [ " Playing chords \n", "\n", "Chords are played throughout a piece of music to create the foundation of a tune. \n", "The basic (major) chords consist of a root note and two other notes, namely “root note+2” and “root note+4”. So there is a total of 3 notes in a (major) chord. For example, the C (major) chord consists of C, E, and G.\n", "\n", "The 3 notes of a basic (major) chord can be played all together (adding the sound waves together), as an arpeggio (e.g. CEGECEG…), or in any other sequence (e.g. CEGCEGCEG… or CCCEGCCCEGCCCEG… etc.) There are many other, more complicated possibilities, including adding other notes here and there, but these are some of the basic ways to play an arpeggio.\n", "\n", "Use mingus to find out the components of a given chord if you do not know.\n", "\n", "\n", "### Exercise 7\n", " \n", "\n", "Play all 3 notes of the C chord together for 1 second, and subsequently play them in an arpeggio pattern (or other pattern of your choice).\n", "- Repeat with the G chord.\n", "- Repeat with the F chord." ] }, { "cell_type": "code", "execution_count": null, "id": "4bcc60f1-85f5-4ad3-9475-1925424f9a4d", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "id": "49eae91b-18aa-410d-a829-1d8563c5ef9b", "metadata": {}, "source": [ " Sequences of chords \n", "\n", "\n", "A simple model for generating music is based on generating a sequence of Chords. There are many ways to generate the sequence of chords. One simple way to set the key (e.g., C key) and then to play at random the chord with root note equal to either the first, fourth, and firth note in the scale of the key. For example, in the C key, one could pick at random (e.g., uniformly) to play either the C chord, the F chord, or the G chord. \n", "\n", "Use mingus to find out the components of a given chord if you do not know.\n", "\n", "### Exercise 8\n", "\n", "\n", "Play a sequence of chords (notes played together) in the C key following the probability model \n", "- C chord probability =2/5\n", "- G chord probability =2/G\n", "- F chord probability =1/5. \n", " \n" ] }, { "cell_type": "code", "execution_count": null, "id": "7512ea30-48d4-4082-80df-5d07b64417f4", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "id": "eb5cf0ee-cb54-446d-9678-e31cdd2a1e1f", "metadata": {}, "source": [ " Adding melody to a chord sequence \n", "\n", "\n", "Given a sequence of chords, one can generate music by adapting the notes played to the chord played at the time. For example, one could prioritize (high probablity) 1st, 3rd, 4th, 5th notes of the scale of the root of the chord, over the other notes of the scale. \n", "\n", "\n", "### Exercise 9\n", "\n", "\n", "Play a sequence of chord (randomly generates) in the C key (notes played together). Then add a melody using a probability model for the notes that is chord dependent. For example, when playing the C chord, the notes could be restricted to C,D,E (uniformly) and for the F chord, the notes could be restricted to (FAC). Add a rhythm to your melody.\n", " " ] }, { "cell_type": "code", "execution_count": null, "id": "6130e34a-aa91-484c-a12e-75cb6d6c37f1", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "id": "42807340-b888-4aa1-b79e-7543be65489c", "metadata": {}, "source": [ " \n", "### Exercise 10\n", "\n", "Pick your own chord, use code given to play music that goes well with it.\n", " \n", "\n", " " ] }, { "cell_type": "code", "execution_count": null, "id": "0f2ffffc-3fd2-44cd-a9db-0b5cf20ce23a", "metadata": {}, "outputs": [], "source": [ "#imports\n", "import numpy as np\n", "import random\n", "\n", "from mingus.core import chords\n", "from midiutil import MIDIFile\n", "\n", "\n", "keys = ['C', 'C#', 'D', 'Eb', 'E', 'F', 'F#', 'G', 'Ab', 'A', 'Bb', 'B']\n", "#major_7th_chords = ['CM7', 'C#M7', 'DM7', 'EbM7', 'EM7', 'FM7', 'F#M7', 'GM7', 'AbM7', 'AM7', 'BbM7', 'BM7']\n", "key_map = {\"C\": 0, \"C#\":1, \"D\":2, \"Eb\":3, \"E\":4, \"F\":5, \"F#\":6, \"G\":7, \"Ab\":8, \"A\":9, \"Bb\":10, \"B\":11}\n", "\n", "def standardize_notation(note):\n", " \n", " if note == 'A#':\n", " note = 'Bb'\n", " elif note == 'E#':\n", " note = 'F'\n", " elif note == 'Gb':\n", " note = 'F#'\n", " elif note == 'G#':\n", " note = 'Ab'\n", " elif note == 'B#':\n", " note = 'C'\n", " elif note == 'Db':\n", " note = 'C#'\n", " elif note == 'D#':\n", " note = 'Eb'\n", "\n", " return note\n", "\n", "def get_midi_representation_of_note(note, octave):\n", " \n", " note = standardize_notation(note)\n", " assert(octave <= 12 and octave >= 0)\n", "\n", " note = key_map[note]\n", " note += (12 * octave)\n", "\n", " assert(note <= 127 and note >= 0)\n", "\n", " return note\n", "\n", "\n", "\n", "transition_matrix = np.zeros((12, 12))\n", "\n", "\n", "#create transition matrix. Transition matrix is made assuming all the chords are Major 7 chords\n", "for key in keys:\n", " \n", " #major seventh chord\n", " chord = key + \"M7\"\n", " preferred_keys = chords.from_shorthand(chord)\n", " \n", " major_chord_pos = key_map[key]\n", " \n", " assert(len(preferred_keys) == 4)\n", " \n", " for i in range(3):\n", " transition_matrix[major_chord_pos, key_map[standardize_notation(preferred_keys[i])]] = 0.3\n", " \n", " transition_matrix[major_chord_pos, key_map[standardize_notation(preferred_keys[3])]] = 0.1\n", " \n", "print(\"******************************************************************************\")\n", "print(\"--------------------- T R A N S I T I O N M A T R I X ---------------------\")\n", " \n", "print(transition_matrix)\n", "print(\"******************************************************************************\")\n", "\n", "\n", "#add custom chords here\n", "#major 7 chords\n", "chord_progression = [\"F\", \"C\", \"F\", \"G\"]\n", "\n", "notes = []\n", "\n", "for chord in chord_progression:\n", " chord = standardize_notation(chord)\n", " \n", " key_probabilities_for_chord = transition_matrix[key_map[chord]]\n", " \n", " # play 4 notes each chord\n", " for i in range(4):\n", " notes.extend(random.choices(keys, weights=key_probabilities_for_chord, k=1))\n", "\n", "print(\"******************************************************************************\")\n", "print(\"------------------------------------ N O T E S -------------------------------\")\n", "\n", "print(notes)\n", "\n", "print(\"******************************************************************************\")\n", "\n", "t = 0 # In beats\n", "dur = 1 # In beats\n", "tempo = 120 # In BPM\n", "vol = 100 # 0-127, as per the MIDI standard\n", "\n", "MyMIDI = MIDIFile(1) \n", "MyMIDI.addTempo(0, t, tempo)\n", "\n", "\n", "for i, note in enumerate(notes):\n", " pitch = get_midi_representation_of_note(note = note, octave = 4)\n", " \n", " MyMIDI.addNote(track = 0, channel = 0, pitch = pitch, time = t + i, duration = dur, volume = vol)\n", "\n", "for i, note in enumerate(chord_progression):\n", " pitch = get_midi_representation_of_note(note = note, octave = 3)\n", " \n", " MyMIDI.addNote(track = 0, channel = 1, pitch = pitch, time = t + (i*4), duration = dur, volume = vol)\n", "\n", "with open(\"latest.mid\", \"wb\") as output_file:\n", " MyMIDI.writeFile(output_file)\n", " \n", " " ] }, { "cell_type": "markdown", "id": "04d036ed-8ead-4e11-9433-66db5c86df53", "metadata": {}, "source": [ "More Compatible Version of the Code Above:" ] }, { "cell_type": "code", "execution_count": null, "id": "7ab85e18-53e4-4cd5-8950-1644369cdfaa", "metadata": {}, "outputs": [], "source": [ "notes = \"A Bb B C C# D Eb E F F# G Ab\"\n", "\n", "\n", "full_scale = {}\n", "for i in range(1, 7):\n", " for j, letter in enumerate(notes.split()):\n", " full_scale[letter + str(i)] = 440 * 2 ** (i - 4 + j / 12)\n", "full_scale\n", "\n", "\n", "#imports\n", "import numpy as np\n", "import random\n", "from IPython.display import Audio\n", "\n", "from mingus.core import chords\n", "\n", "\n", "keys = ['C', 'C#', 'D', 'Eb', 'E', 'F', 'F#', 'G', 'Ab', 'A', 'Bb', 'B']\n", "#major_7th_chords = ['CM7', 'C#M7', 'DM7', 'EbM7', 'EM7', 'FM7', 'F#M7', 'GM7', 'AbM7', 'AM7', 'BbM7', 'BM7']\n", "key_map = {\"C\": 0, \"C#\":1, \"D\":2, \"Eb\":3, \"E\":4, \"F\":5, \"F#\":6, \"G\":7, \"Ab\":8, \"A\":9, \"Bb\":10, \"B\":11}\n", "\n", "def standardize_notation(note):\n", " \n", " if note == 'A#':\n", " note = 'Bb'\n", " elif note == 'E#':\n", " note = 'F'\n", " elif note == 'Gb':\n", " note = 'F#'\n", " elif note == 'G#':\n", " note = 'Ab'\n", " elif note == 'B#':\n", " note = 'C'\n", " elif note == 'Db':\n", " note = 'C#'\n", " elif note == 'D#':\n", " note = 'Eb'\n", "\n", " return note\n", "\n", "def get_sine_wave(frequency, duration, sample_rate=44100, amplitude=4096):\n", " t = np.linspace(0, duration, int(sample_rate*duration))\n", " wave = amplitude*np.sin(2*np.pi*frequency*t)\n", " return wave\n", "\n", "\n", "transition_matrix = np.zeros((12, 12))\n", "\n", "\n", "#create transition matrix. Transition matrix is made assuming all the chords are Major 7 chords\n", "for key in keys:\n", " \n", " #major seventh chord\n", " chord = key + \"M7\"\n", " preferred_keys = chords.from_shorthand(chord)\n", " \n", " major_chord_pos = key_map[key]\n", " \n", " assert(len(preferred_keys) == 4)\n", " \n", " for i in range(3):\n", " transition_matrix[major_chord_pos, key_map[standardize_notation(preferred_keys[i])]] = 0.3\n", " \n", " transition_matrix[major_chord_pos, key_map[standardize_notation(preferred_keys[3])]] = 0.1\n", " \n", "print(\"******************************************************************************\")\n", "print(\"--------------------- T R A N S I T I O N M A T R I X ---------------------\")\n", " \n", "print(transition_matrix)\n", "print(\"******************************************************************************\")\n", "\n", "#major 7 chords\n", "chord_progression = [\"F\", \"C\", \"F\", \"G\"]\n", "\n", "notes = []\n", "\n", "for chord in chord_progression:\n", " chord = standardize_notation(chord)\n", " \n", " key_probabilities_for_chord = transition_matrix[key_map[chord]]\n", " \n", " # play 4 notes each chord\n", " for i in range(4):\n", " notes.extend(random.choices(keys, weights=key_probabilities_for_chord, k=1))\n", "\n", "print(\"******************************************************************************\")\n", "print(\"------------------------------------ N O T E S -------------------------------\")\n", "\n", "print(notes)\n", "\n", "print(\"******************************************************************************\")\n", "\n", "track1 = []\n", "track2 = []\n", "\n", "for i, note in enumerate(notes):\n", " \n", " frequency = full_scale[note+\"4\"]\n", " track1.extend(get_sine_wave(frequency = frequency, duration = 1, sample_rate=44100, amplitude=4096))\n", " \n", "for i, note in enumerate(chord_progression):\n", " frequency = full_scale[note+\"3\"]\n", " track2.extend(get_sine_wave(frequency = frequency, duration = 1, sample_rate=44100, amplitude=2048))\n", " track2.extend(np.zeros(3*44100))\n", "\n", "\n", "wave = np.add(track1, track2)\n", "\n", "Audio(wave, rate = 44100)\n" ] }, { "cell_type": "markdown", "id": "baff7340-1f56-48a5-8721-80e5e82cd428", "metadata": { "tags": [] }, "source": [ "## Reflection \n", "\n", "1. Which part of the lab did you find the most challenging?\n", "2. Which part of the lab was the easiest?\n", "3. Please list any other comments below.\n", "

Write Answers for the Reflection Below

" ] }, { "cell_type": "code", "execution_count": null, "id": "9fd33554-3154-44ed-8735-26f3f336e266", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.11.5" } }, "nbformat": 4, "nbformat_minor": 5 }