3.3.4 Experimenting with Music in MATLAB

3.3.4 Experimenting with Music in MATLAB

Chapter 2 introduces some command line arguments for evaluating sine waves, playing sounds, and plotting graphs in MATLAB. For this chapter, you may find it more convenient to write functions in an external program. Files that contain code in the MATLAB language are called M-files, ending in the .m suffix. Here’s how you proceed when working with M-files:

  • Create an M-file using MATLAB’s built-in text editor (or any text editor)
  • Write a function in the M-file.
  • Name the M-file fun1.m where fun1 is the name of function in the file.
  • Place the M-file in your current MATLAB directory.
  • Call the function from the command line, giving input arguments as appropriate and accepting the output by assigning it to a variable if necessary.

[wpfilebase tag=file id=53 tpl=supplement /]

The M-file can contain internal functions that are called from the main function. We refer you to MATLAB’s Help for details of syntax and program structure, but offer the program in Algorithm 3.1 to get you started. This program allows the user to create major and minor scales beginning with a start note. The start note is represented as a number of semitones offset from middle C. The function plays eight seconds of sound at a sampling rate of 44,100 samples per second and returns the raw data to the user, where it can be assigned to a variable on the command line if desired.

function outarray = MakeScale(startnoteoffset, isminor)
%outarray is an array of sound samples on the scale of (-1,1)
%outarray contains the 8 notes of a diatonic musical scale
%each note is played for one second
%the sampling rate is 44100 samples/s 
%startnoteoffset is the number of semitones up or down from middle C at
%which the scale should start.
%If isminor == 0, a major scale is played; otherwise a minor scale is played.
sr = 44100;
s = 8;
outarray = zeros(1,sr*s);
majors=[0 2 4 5 7 9 11 12];
minors=[0 2 3 5 7 8 10 12];
if(isminor == 0)
    scale = majors;
else
    scale = minors;
end
scale = scale/12;
scale = 2.^scale;
%.^ is element-by-element exponentiation
t = [1:sr]/sr;
%the statement above is equivalent to 
startnote = 220*(2^((startnoteoffset+3)/12))
scale = startnote * scale;
%Yes, ^ is exponentiation in MATLAB, rather than bitwise XOR as in C++
for i = 1:8
    outarray(1+(i-1)*sr:sr*i) = sin((2*pi*scale(i))*t);
end
 sound(outarray,sr);

Algorithm 3.1 Generating scales

The variables majors and minors hold arrays of integers, which can be created in MATLAB by placing the integers between square brackets, with no commas separating them. This is useful for defining, for each note in a diatonic scale, the number of semitones that the note is away from the key note.

[wpfilebase tag=file id=55 tpl=supplement /]

The lineThe variable scale is also an array, the same length as majors and minors (eight elements, because eight notes are played for the diatonic scale). Say that a major scale is to be created. Each element in scale is set to $$2^{\frac{majors\left [ i \right ]}{12}}$$, where majors[i] is the original value of element i in the array majors. (Note that arrays are numbered beginning at 1 in MATLAB.) This sets scale equal to $$\left [ 1,2^{\frac{2}{12}},2^{\frac{4}{12}},2^{\frac{5}{12}},2^{\frac{7}{12}},2^{\frac{9}{12}},2^{\frac{11}{12}} \right ]$$. When the start note is multiplied by each of these numbers, one at a time, the frequencies of the notes in a scale are produced.

x = [1:sr]/sr;

creates an array of 44,100 points between 0 and 1 at which the sine function is evaluated. (This is essentially equivalent to x = linspace(0,1, sr), which we used in previous examples.)

A3 with a frequency of 220 Hz is used as a reference point from which all other frequencies are built. Thus

startnote = 220*(2^((startnoteoffset+3)/12));

sets the start note to be middle C plus the user-defined offset.

In the for loop that repeats for eight seconds, the statement

outarray(1+(i-1)*sr:sr*i) = sin((2*pi*scale(i))*t);

writes the sound data into the appropriate section of outarray. It generates these samples by evaluating a sine function of the appropriate frequency across the 44,100-element array x. This statement is an example of how conveniently MATLAB handles array operations.   A single call to a sine function can be used to evaluate the function over an entire array of values. The statement

scale = scale/12;

works similarly, dividing each element in the array scale by 12. The statement

scale = 2.^scale;

is also an element-by-element array operation, but in this case a dot has to be added to the exponentiation operator since ^ alone is matrix exponentiation, which can have only an integer exponent.