EE 305: Applied Probability MATLAB Project: Markov Music - Fall 2019 Assigned: Friday, Nov. 8 Due: Friday, Dec. 6 1 Introduction 1.1 Project Overview In this project, you will do the following: 1. Conduct a self-study on discrete-time Markov chains. 2. Create random music in MATLAB using discrete-time Markov chains. 1.2 MATLAB Learning Outcomes While completing this assignment, you will learn the following about MATLAB: 1. How to create a two-dimensional matrix in MATLAB. 2. How to extract a row vector from a two-dimensional matrix in MATLAB. 3. How to generate random variables from a discrete PMF defined in a vector. 4. How to define and use a simple cell array. 5. How to access the fields of a struct. 1.3 Files To complete this assignment, you will need the following files, which are posted on Piazza: 1. AddNote.m: A function that adds the signal of a note to a song. Type help AddNote to get help information for this function. 2. CreateScale.m: A function that converts a cell array of note strings into note indices. Type help CreateScale to get help information for this function. 3. PlayTune.m: A function that plays a song. Type help PlayTune to get help information for this function. 1 4. SaveTune.m: A function that saves a song to a .wav file. Type help SaveTune to get help information for this function. 5. parameters.m: This file contains parameters and constants that are used in the files described above. 6. rando.m: A function that draws a random variable according to a given PMF. 7. mydatasample.m: A function that draws one sample randomly and uniformly from a data vector. 2 2.1 Preliminaries Notes on an 88-key Piano In practice, when a piano string is excited (or any oscillator, from a guitar to a flute), it will naturally vibrate at a series of distinct frequencies known as normal modes. The lowest normal mode is known as the fundamental frequency, while the higher frequencies are called overtones. Harmonics are overtones whose frequencies are integer multiples of the fundamental frequency (including the fundamental frequency which is 1 times itself). The fundamental frequency of each successive key on a standard modern 88-key piano is derived by multiplying the previous key’s frequency by the twelfth root of two. The following equation gives the frequency f of the nth key: n−49 f (n) = 2 12 × 440 Hz, where 440 Hz is the note A4, which is the 49th key on the piano. 2.2 Note Value and Tempo A whole note has a length equal to four beats in 4/4 time, which is the most popular time signature in American pop music. Half, quarter, eighth, and sixteenth notes have durations that are 1/2, 1/4, 1/8, and 1/16 the length of a whole note, respectively. The relative duration of a note with respect to a whole note is called the note value. The tempo of a piece of music is typically specified in beats per minute (BPM). Assuming 4 beats for a whole note, a note’s duration measured in seconds can be determined as: 4 × 60 × v seconds BPM where v is the note value. 2.3 Generating a Note Signal The function AddNote, which is provided to you on Piazza, automatically generates the audio signal of a given note as described in this subsection. A note with fundamental frequency f0 (n) is generated as a weighted sum of the M overtones (including the fundamental frequency): M X 2πfm (n)t −λt A wm e sin , for t = tstart , . . . , tend . Fs m=1 2 where n is the index of the key. A is the intensity of the note, which can be thought of as the volume with which it is played or how hard the key is struck. wm is the weight of the mth overtone, with m = 0 corresponding to the fundamental frequency. It is assumed that w0 = 1. λ is the attenuation parameter, which causes the note’s signal amplitude to decay in time. fm (n) is the frequency of the mth overtone/harmonic of key n, where fm (n) = mf0 (n). Fs is the sampling frequency, i.e., the number of sound samples per second. tstart is the time at which the note’s signal begins. tend is the time at which the note’s signal ends. 2.4 Useful Resources Some useful resources can be found at the following links: 1. Piano key frequencies: http://en.wikipedia.org/wiki/Piano_key_frequencies 2. Piano scales: http://www.pianoscales.org/ 3. MATLAB cell array: http://www.mathworks.com/help/matlab/cell-arrays.html 4. MATLAB structures: http://www.mathworks.com/help/matlab/structures.html 3 New MATLAB Commands You will now use the MATLAB Command Window to perform several operations to get more familiar with basic MATLAB and the functions that have been provided to you. To enter information into the Command Window, simply put your cursor where you see the >> symbol. You can then type a command and hit the ENTER key to execute it. Type in the following commands: 1. >> DMajorStr = {‘D3’,‘E3’,‘F#3’,‘G3’,‘A3’,‘B3’,‘C#4’,‘D4’} This command creates a MATLAB cell array containing strings of the notes that are in the D Major scale. A cell array is a complex and flexible data type. Cell arrays are similar to vectors, but instead of each index pointing to a single number or character, as is the case with vectors, each index of a cell array can point to a number, string of characters, or even a vector. You will not need to know much about cell arrays to complete this project other than the two commands that we show you in this document; however, if you are interested in learning more about cell arrays, please see the online documentation that is linked to in Section 2.4. Let us look closer at the strings that define a note. The string ‘D3’ indicates the 3rd octave of the note D. You can get different notes by changing the character, e.g., replacing ‘D’ with ‘E’. 3 You can change the octave by using a different number, e.g., replacing ‘3’ with ‘4’. Finally, you can make the note sharp or flat by writing ‘D#3’ or ‘Db3’, respectively. 2. >> DMajorStr{3} This returns the third string in the cell array DMajorStr, i.e., ‘F#3’. Note that you use the notation {i} to return the ith entry of a cell array. This is in contrast to arrays, for which you use the notation (i). 3. >> DMajor = CreateScale(DMajorStr) The function CreateScale converts the cell array of note strings into a vector of note indices. Each of these indices corresponds to one of the 88 keys on the piano. 4. Input the following code into a new MATLAB script file and then run it. In this code, random notes are played with random durations, intensities, and attenuations. Note that the first line of this script actually executes the script parameters.m, which creates the param structure. A structure is a complex data type that groups multiple fields of related data into one variable. For example, to access a field in the param structure you simply write param.varName, where varName is the name of the field that you want. For more information about MATLAB structures, see the link in Section 2.4. For more information about the custom MATLAB function mydatasample, use the MATLAB help command. Important: In the below code, some lines wrap-around to the next line. You must make sure to keep these on a single line in your code; otherwise, MATLAB will throw an error. In particular, if you copy-and-paste the below code, it will not work until you correct it. % Demo 1: Independent and Identically Distributed Music Test parameters % Load parameters from parameters . m song = zeros (1 ,240* param . Fs ) ; % Initialize 240 seconds of song samples start = 1; % Index of first audio sample for i = 1:30 % Generate 30 random notes % Select random note , note duration , note intensity , and note attenuation note = mydatasample ( param . DMajor ) ; duration = mydatasample ( param . durationSet ) ; intensity = mydatasample ( param . intensitySet ) ; attenuation = mydatasample ( param . attenuationSet ) ; [ song , next ] = AddNote ( song , start , note , duration , intensity , attenuation , param ) ; start = next ; % Index of next audio sample end mySong = song (1: next ) ; % Remove empty samples from song PlayTune ( mySong , param ) ; % Play your tune ! 4 We now introduce how to simulate a discrete-time Markov chain in MATLAB. Before you input these commands, it is recommended that you conduct the discrete-time Markov chain self-study as described in Section 4.1. 5. >> stateSet = [10 12 13 16]; This is a vector defining the set of states (right now, the states are arbitrary, but in the final example they will represent notes from a scale). We refer to the values 10, 12, 13, and 16 as states. When we write stateSet(i) we refer to i as the ith state index and stateSet(i) as the ith state. 6. >> stateTPF = [0.75 0.25 0 0; 0 0.5 0.5 0; 0 0 0.5 0.5; 0 0 0.25 0.75] This defines a 4x4 matrix. The first four numbers occupy the first row of the matrix. The numbers after the first semicolon occupy the second row of the matrix. The numbers after the second semicolon occupy the third row of the matrix, and so on. In other words, a semicolon is used to separate rows of the matrix. Note that each row of a matrix must have the same number of elements (in this case 4); otherwise, MATLAB will give you an error. The 4x4 matrix in stateTPF actually represents a Markovian state-transition probability matrix over the four states in stateSet. You can see that it is a state-transition probability matrix because each of its rows contain positive elements that sum to 1. 7. >> stateTPF(2,3) This gives us the probability of transitioning to the 3rd state (i.e., state 13) from the 2nd state (i.e., state 12). In general, stateTPF(i,j) gives the probability of transitioning from the ith state to the jth state. 8. >> stateTPF(2,:) This gives us the conditional PMF of the next state, given that we are currently in the 2nd state. 9. sum(stateTPF(2,:)) The built-in MATLAB function sum takes the sum of the values in an array (in this case, the array represents the conditional PMF). If stateTPF(i,:) does not sum to 1, for all i, then it is not a valid state transition probability matrix. 10. rando(stateTPF(2,:)) This returns a random next state index according to the conditional PMF. Given our definition of stateTPF, if we start in the 2nd state, then we either remain in the 2nd state with probability 0.5 or transition to the 3rd state with probability 0.5. 11. Input the following code into a new MATLAB script file and then run it. In this code, the sequence of notes played follows a discrete-time Markov chain. Important: In the below code, some lines wrap-around to the next line. You must make sure to keep these on a single line in your code; otherwise, MATLAB will throw an error. In particular, if you copy-and-paste the below code, it will not work until you correct it. 5 % Demo 2: Markov Chain Music Test parameters % Load parameters from parameters . m % Initialize 240 seconds of song samples song = zeros (1 ,240* param . Fs ) ; % Set the state set to the notes of the Phrygian dominant scale stateSet = param . PhrygianDom ; % Initialize the transition probability matrix % (8 x8 matrix since there are 8 notes in the Phrygian dominant scale ) % NOTE : The ellipsis (...) allow you to continue your code on the next line . stateTPF = [0.25 0.5 0 0 0 0 0 0.25;... 0.25 0.5 0.25 0 0 0 0 0;... 0 0.25 0.5 0.25 0 0 0 0;... 0 0 0.25 0.5 0.25 0 0 0;... 0 0 0 0.25 0.5 0.25 0 0;... 0 0 0 0 0.25 0.5 0.25 0;... 0 0 0 0 0 0.25 0.5 0.25;... 0.25 0 0 0 0 0 0.5 0.25]; stateIdx = 1; % Start in 1 st state start = 1; % Index of first audio sample for i = 1:30 % Generate 30 notes note = stateSet ( stateIdx ) ; % Get note corresponding to current state index duration = param . durationSet (3) ; % All 1/4 notes intensity = param . intensitySet ( end ) ; % All maximum intensity attenuation = param . attenuationSet (45) ; % All high attenuation [ song , next ] = AddNote ( song , start , note , duration , intensity , attenuation , param ) ; start = next ; % Index of next audio sample % Markov state transition stateIdx = rando ( stateTPF ( stateIdx ,:) ) ; % Get random next state end mySong = song (1: next ) ; % Remove empty samples from song PlayTune ( mySong , param ) ; % Play your tune ! % Save your tune to my Fi rs tM ar ko vM us ic . wav 6 SaveTune ( mySong , param , ' my Fi rs tM ar ko vM us ic ' ) ; 4 Assignment 4.1 What You Need to Do 1. Discrete-time Markov chain self-study: For this part of the project, you will teach yourself about discrete-time Markov chains. I recommend that you start by reading Sections 7.1 and 7.2 in the textbook. You may also search Wikipedia and any other resources available. Make sure to cite your sources. As part of your self-study, you must type up brief answers to the following questions. (a) What does the key word discrete-time refer to in discrete-time Markov chain? (b) What is a state? (c) What is a transition probability? (d) What is the Markov property? (e) What is a transition probability matrix? How should one interpret the row and column indices of a transition probability matrix? What does a row in the transition probability matrix represent? (f) What is a transient state? (g) What is a recurrent state? (h) What is an absorbing state? (i) What does it mean for a Markov chain to be time-homogeneous. 2. Let’s make music! The Markov chain music that we created in the final example of Section 3 was fairly boring: all notes had the same duration, intensity, and attenuation, and there was very little structure or variation to the song. For this part of the project, I want you to get creative and figure out how to make more interesting music using Markov chains. The following is a list of suggestions ranging from easy to hard. You must implement at least two (2) easy modifications and one (1) medium difficulty modification to receive full credit on the assignment. You may implement a hard modification instead of a medium difficulty modification for extra credit. All of your modifications must be integrated into a single song. (a) [Easy] Add and use a new scale. You can find scales on one of the websites listed in Section 2.4. You can add more scales to the param structure in parameters.m. (b) [Easy] Insert rest (silence) into your song by using note index 89. (c) [Easy] Change the sound of the instrument by modifying param.overtoneWeights in parameters.m. (d) [Easy] Change the sound of the instrument by assigning different decay rates to each overtone. (Note: This is harder than the other easy modifications.) (e) [Easy] Create longer sequences of music (more than 30 notes). 7 (f) [Easy] Come up with your own idea, but please check with the instructor to ensure that it has the appropriate difficulty. (g) [Medium] Generate separate Markov chains to not only randomly adapt the notes played, but also randomly adapt their durations, intensities, and/or attenuations. (h) [Medium] Use a Bernoulli process to randomly switch between two different transition probability matrices or two different octaves of the same scale. (i) [Medium] Have two instruments play at the same time. For example, they may use different octaves, durations, intensities, attenuations, overtone weights, and/or transition probability matrices. (j) [Medium] Create a more structured song by looping your generated notes. For example, you may generate one bar of music and repeat it several times. Variations on this may have three repeated bars followed by a fourth unique bar, or the addition of new instruments or note patterns for every set of four bars. (k) [Medium] Come up with your own idea, but please check with the instructor to ensure that it has the appropriate difficulty. (l) [Hard] Define a second-order Markov chain, where the state is based on the last two notes played, instead of only the previous note played. (m) [Hard] Use MATLAB to create a graphical user interface (GUI) that is simple enough for a grade school student to use to generate Markov music. For example, you may include a drop down menu or other mechanism for selecting among several available scales. You may include some user interface to allow people to change the transition probabilities without them needing to understand the underlying details. You may include some mechanism to overlay multiple instruments or create repeated loops. (n) [Hard] Generate a Markov chain to jump around the Circle of Fifths (https://www. youtube.com/watch?v=62tIvfP9A2w) while using another Markov chain to play notes in each of the 12 scales. (o) [Hard] Use the concepts of Prime, Inversion, Retrograde, and Retrograde Inversion (https://www.youtube.com/watch?v=Dfr7GE28Qp8). For example, randomly generate a bar of music using a Markov chain and then algorithmically generate its Inversion, Retrograde, or Retrograde Inversion. You could also have multiple instruments playing these different musical voices on top of each other. (p) [Hard] Come up with your own idea, but please check with the instructor to ensure that it has the appropriate difficulty. 4.2 What You Need to Turn In You will submit the Markov music project through UBlearns. You will receive an announcement when the submission system is open (it is not set up yet). Please carefully follow the instructions below on how to package your submission. Your entire submission must be packaged in a folder named <lastname> <firstname>, where you will replace <lastname> with your last name and <firstname> with your first name. Compress the entire folder containing the items below into a zip file named <lastname> <firstname>.zip. You will upload this zip file to UBlearns. Your zip file must include the following items. 8 1. A typed document containing answers to the self-study questions that are given in Section 4.1. You must save this to a file with the name <lastname> <firstname>.pdf, where you replace <lastname> with your last name and <firstname> with your first name. 2. In the same typed document, you must provide a brief description of the easy, medium, and hard modifications that you made to generate your Markov chain music. That is, you should describe in words the specific modifications that you made to the baseline Markov Chain Music Test in order to create your song. 3. A saved copy of your song in .wav format. You must save the song to a file with the name <lastname> <firstname>.wav, where you replace <lastname> with your last name and <firstname> with your first name. This single WAV file must contain ALL of your modifications. 4. Within your .zip file, include a folder with the name “code” that includes all of the MATLAB files that are needed to generate your song in MATLAB. This means that you need to include every .m file provided to you in the assignment with whatever modifications that you have made. I recommend that, before submission, you test all of the necessary code by running your script from the “code” directory. If it executes properly for you, then it should execute properly for us. The script that can be run to generate your song must be named <lastname> <firstname>.m. An example of a correctly formatted submission package is provided with the project. The assignment is due at 11:59:59 PM on the due date. 4.3 What is this Project Worth? The MATLAB assignments are worth 20% of your total grade. Each of the first three MATLAB assignments are worth 100 points. This project is worth 200 points. You can receive an extra 50 points for completing a hard modification instead of a medium difficulty modification. 9