Your SlideShare is downloading. ×
Introduction to writing algorithms
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Saving this for later?

Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime - even offline.

Text the download link to your phone

Standard text messaging rates apply

Introduction to writing algorithms

148
views

Published on


0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
148
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
5
Comments
0
Likes
0
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Introduction to Writing Algorithms. (In MINC) Christopher Bailey Hi!! Im Sammy the Serialist, and Im going to show you, through an example, the basic steps in creating a CMIX (MINC) algorithm. Along the way, were going to use "Chris Baileys 8-Step Method for Creating Algorithms"What are algorithms: Basically, the word "algorithm" usually refers simply to a way ormethod of doing a task, with the task usually being of a repetitive nature, or being something thatcan be described easily in an "overall" way. In computer music, examples of such tasks might be"play a chromatic scale" or "play 200 random notes" or "make a very cloudy tremolating sound."As a tool for composers, in other words, algorithms are most often used to generate textures.Generally speaking, in computer music, algorithms do two things: 1. make your life easier: you can use them to do some of the "thinking" for you (but remember, you have to teach the machine "how to think"), creating textures where the details dont matter as much as the overall effect; this is the most common kind of algorithmic composition: using algorithms to create clouds, lines, or whatever: soundfiles that you will insert into a larger piece. 2. For the exploration of things like style modeling, genetics, games, physical laws (gas particles dancing around, et al), artificial intelligence, etc. This gets into "deeper" aspects of algorithmic composition, which we wont get into here (Brad Gartons advanced class begins to explore this wonderful world. . .)What we want to look at here is how to use algorithms to create simple textures and other soundmaterials for use in your music. The idea/example illustrated here is rather assinine, but it doescover all the concepts you will need to know for basic algorithmic design.To make this process easier, I came up with simple way of thinking things through before andwhile writing code to accomplish a given musical task.Here it is: Chris Baileys Basic 8-Step Process 1) Think of and explore your idea, draw pictures, diagrams, and etc. to figure out whats happening in the different musical parameters. How is pitch changing? How is start-time happening? How is duration working? Remember that you dont necessarily have to write all of the notes in their temporal order: some algorithms may splatter notes in time randomly (not in time-order); others may write notes (or other sounds) in rhythm, from
  • 2. first-to-last, time-wise. All sorts of possibilities can happen, so try to open your mind to different ways of solving whatever musical problem you have. 2) Divide job into loops. (Sometimes the task you have can be accomplished with one loop that writes all of the sound involved, other times you may want to have a number of loops write the sounds for different sections of a segment of music, for example.) 3) Basic set-up stuff: Here is where you write statements at the top of the score for things that wont be changing during the algorithmic process. Thus commands like rtsetparams() , opening input and output files with rtinput() and rtoutput() , certain makegen()s, (if theyre not going to be changing throughout the score), and so on. Repeat 4-7 for each loop 4) Outline each loop. Type your while or for statements, and then add brackets below, into which you will eventually insert your loop(s). 5) Action Statements. Write out the statements that actually make the sound. (typically the instrument name(s), as in WAVETABLE or the like.) Then fill in their parameters with variables. 6) Work outwards from the Action Statements, figuring out how each parameter will be determined each time the loop goes around--- pitch,duration,start, etc. 7) Initialize all variables before each loop starts. 8) Run it, test it, fix it, modify it, love it.OK, now I describe these steps in more detail: Step 1) Think of your idea. Draw a picture. Figure out whats happening in terms of pitch, rhythm (start-times), durations, articulations (amplitude envelopes), loudness, stereo placement, and whatever other factors are involved in the instrument(s) youre using. Sammy the Serialist Example, cont.’: Lets see. . . I have an Evil 12 Tone Row, and I want to make a nice leapy melody, with the pitch-classes cycling through my Evil Row. Of course, they must always come in different octaves. The rhythm--well, Im an evil serialist, so of course I want it to be based on my row. The duration of any note will be simply to sound until the next note. I want the articulations to be either an Sfp attack, or else a hairpin (<>) kind of articulation. Loudness-- well, of course, it, too must be based on the row! HaHa! And stereo placement too!! Ha ha ha!!
  • 3. So, now youve thought of how your musical excerpt is going to work. Now, go on to the nextstep-- step 2) Divide your job into as many separate simple-as- possible loops as you can. In general, a single loop should create either a single line, or a uniform or uniformly changing texture. Sammy Example, cont.: I think I will only need one loop, since I am creating one line of pitches. Later on, for counterpoint, I can use more loops. step 3) Begin by defining basic stuff, like your output file, your input files, any makegens that stay the same for the entire thing, etc. Sammy Example, cont.: rtsetparams(44100, 2) rtoutput("evil.rows.aiff") makegen(1, 10,1000, 1, .2, .2, .2, .1) These are the basic setup items for our script. "Timbre" stays the same--(which, for WAVETABLE is makegen slot 1). But articulation, (i.e.--amplitude envelope, which for WAVETABLE is makegen slot 2, (and usually type 24)) changes, so that will be "in" the loop--we’ll get to it later on. step 4)Write the outlines of your loop(s). This means, basically, control (while or for) statements, plus (if necessary) a statement at the end of the loop that increments your counter. while: As long as the expression in () is True, then the stuff below, the statements in {} will execute. After the stuff in {} is done executing, the () expression is tested again, if still True, then the statements in {} are executed again, and so on, until the () expression is False, then the {} stuff is skipped and the program goes on. Example: while (x<100) { statements, commands, n stuff x=x+1 }
  • 4. Here the {} stuff will execute over and over until x=99. Then the {} stuff will execute one more time, then x=x+1=100, and "x<100" will now be a False statement, so the the program will skip the {} stuff and go on. For : Similar to while, but here, in the initial statement, you tell it the initial value of a counter variable, followed by a semicolon, followed by an expression that has to be True for the {} stuff to be executed (usually that the counter is less than some upper limit, as with while ), followed by another semicolon, followed by a command (usually a change in the counter variable) that you execute each time you go through the loop. for (x=0; x<9; x=x+1) { commands & stuff } if: This is another control statement which you may want to use at some point. If the expression is True then the {} stuff will be executed. There can also be an optional else part. If the expression following the if is False then the {} stuff following the else will be executed. If there is no else part, then the program just goes on, skipping the {} stuff after the if statement. if (x<.5) { commands & stuff } else { commands & stuff } Example, cont.:Hi, Sammy here again. . . . I’m going to use While type loops in my example.step 5) Now begin to write the loop. Remember, you DONT,repeat, DONT start at the beginning and work your waydown. You start at the middle, and work your way back tothe beginning. More precisely, you start at the ActionStatement(s), and work backwards. The Action Statement(s)is/are usually your instrument statement(s), such as
  • 5. WAVETABLE, or COMBIT, or whatever, or maybe a makegen forenvelope (articulation), timbre, etc. the Action Statementsusually take a set of Parameters, like pitch, duration,etc., which change each time you go through the loop. Example, cont.: We are going to have two Action Statements. One, of course, makes the notes: WAVETABLE(start, dur, amp, pitch, stereo) The other makes the "articulation" with the appropriate amplitude envelope: makegen(2, 24, 2000, bla bla bla (well fill this in eventually)) So, now our basic loop looks like yay: while (x<100) { WAVETABLE(start, dur, amp, pitch, stereo) makegen(2, 24, 2000, bla bla bla bla) x=x+1 }step 6)Now, work backwards from your Action Statement(s),considering each Parameter in turn, and figuring out how itis calculated. Generally, there are three types ofParameters: CONSTANT: This means that the parameter is the same for every note created by the loop. Constants are easy: you just fill them in with the appropriate number. If the loudness is always the same in a loop, for example, you would just fill in the third slot of the WAVETABLE command with the constant loudness. For example: WAVETABLE(start, dur, 1000, pitch, stereo) NON-DEPENDENT:This means that every time the loop goes around, the parameter is calculated afresh. If in a loop, the statement dur=random()*2, this means that every time the loop goes around, dur is recalculated,
  • 6. regardless (i.e.in- or non-dependent) of its previousvalue. random()= May as well introduce this here: the random() function produces a random value from 0 to 1. Hence x=random() will assign to the variable x, a random number from 0 to 1.DEPENDENT:This is when a variables value depends onthe value it had last time. A typical standard exampleis when the start time of the next note youre goingto create equals the current start time plus the durof the current note. (In other words, "start the nextnote when this one ends.")As a rule of thumb, dependent statements, like ourexample (start=start+dur) usually go after the ActionStatements. Non-dependent values go before the ActionStatements. In other words, you, or rather, the loop,calculates the non-dependent values, then, writes thenote, then calculate the Dependent values (for thenext note), and then goes back to the beginning of theloop (to do the same for the next note.)About choosing values for things: There are, in general, 3ways of choosing values for things. Lets call them RANGE,SELECT and CYCLIC:RANGE: This is just if you want any random valuebetween x and y, then you just write random()*(y-x)+x,where x < y. Remember that equation--you’ll learn tolove it. It gives you floating-point numbers, (i.e.--with a decimal point and lots of stuff after it); ifyou need integers, you can use the trunc() function,which just removes the decimal stuff. (trunc(x.y)=x)(It doesn’t round, it just removes the nastinessbeyond the decimal point.) Because of the truncation,you have to add 1 in the equation to get your range ofnumbers, so it becomes: trunc(random()*(y-x+1)+x)(Remember, this one is for integers from x to y .)SELECT: but lets say you want to select from 5specific values, such as 1, 3, 4, 5, and 6. It wouldbe nice to have these in a table, and then be able tosay, "choose a random value from the table." Well, wecan do this with sampfunc, and the data , or type 2,
  • 7. makegen. First, we make our table of values that wewill select from. Here’s an example: makegen(a, 2, n, 0) 1, 3, 4, 5, 6a is the ["slot"] number of the table: this is thesame in all makegens. If you use more than one table,they should obviously be numbered differently. Also,you shouldnt use the numbers 1, 2, 3, or even 4,because these are often used already by your CMIXinstrument; (remember, for example, that WAVETABLEuses slots 1 and 2 for timbre and amplitude envelope ,respectively.)n is the number of values you are going to store.2 specifies that this is a "data" type makegen (asopposed to a "make a harmonic wave" type (10) or a"make an envelope" (24) type, or any of the othertypes available (see Lukes makegen page.))The second line contains the actual table, with valuesseparated by commas. Sammy example, cont.: Lets make a table for our Evil 12-Tone Row. Heh, heh. makegen(-3, 2, 12, 0) 0, 2, 8 , 11, 10, 6, 3, 4, 7, 9, 5, 1 OK, now, how do we later choose a random value from this table? The answer is: sampfunc(t, i) where t is the table# (referring to the first of the the makegen parameters) and i is the # of the piece of data. (However, note that the data gets numbered from 0, not 1, so the last value in our table is #11, not #12.) Lets pretend we made that table above, and now, what will happen when we call sampfunc ? sampfunc(3, 1)=2 sampfunc(3, 0)=0 sampfunc(3, 6)=3
  • 8. sampfunc(4, 6)=error, because we didnt make a table numbered 4 yet. sampfunc(3, random()*12 ) choose a random number between 0 and 11.99999, and select that # value. Thus if (random()*11) gave us 4.42533, (automatically truncated to 4) then the sampfunc would give us 10 (item#4=10). sampfunc(3, random()*n ) The range of random numbers we select is between 0, and n , the number of items. (Note that if you are selecting from, say, 6 items, they are numbered 0-1-2-3-4-5, but you type random()*6 because the highest possible number, 5.99999, would be truncated automatically (by the sampfunc command) to give you 5. Thus you will never get an actual "6" as a result of the randomizing.)CYCLIC: OK, but lets say we dont want to selectrandomly from our table, but to cycle through it as wecycle through our loop. If our table had, say, sixelements, we could use a counter variable, say c, andeach time we went through the loop, at the end, wewould say: c=c+1; except we would have to check if c >number-of-elements in the table, and if it was, wewould reset it to 0, and the cycling would beginagain. Our example, cont.: So, we are going to want to cycle through the row, over and over, as we go through our loop. So we need a counter, c. Every time were going to need a row pitch, (or pitch-class, since well determine the octave later), we say pitchclass=sampfunc(3, c) Later, we say c=c+1 if (c>11) c=0 That makes sure c keeps : climbing to 11, skipping back to 0, then climbing to 11, skipping back to 0, then climbing to 11, skipping back to 0 . . . . etc. So now lets go ahead and consider each parameter of our Action Statements, one by one, gradually filling in our algorithm. First, lets look at what we have so far: while (x<100) { makegen(2, 24, bla bla bla) WAVETABLE(start, dur, amp, pitch, stereo)
  • 9. x=x+1 }We’ll start with the first thingy, the envelope makegen. Remember, we wantedto have two choices for envelope, either Sfp, or a <> articulation (a hairpin).These can be chosen randomly. (I.e.--this is a non-dependent decision--whatwas decided the last time we went through the loop doesnt matter to us.) Wecan generate a random number twixt 0 and 1, and if the number is <= (lessthan or equal to) .5, then do an Sfp, if its >.5, then do a <> articulation. In otherwords: q=random() if (q<=.5) makegen(2, 24, 2000, 0,0,1,1,2,.5,19,.5,30,0) else makegen(2, 24, 2000, 0,0,1,1,2,0)The first, if you inspect it, is an Sfp envelope, the second, a <> envelope.Oh-kee, now for start-time. Start-time is dependent. It depends on the valuethat start-time had the last time we went through the loop, and the lastduration used. In other words, the start-time of the current note depends onwhen and how long the last note was. Since its dependent, we put it after theAction Statement(s). In our case, its simply going to equal start+dur. So heresour growing algorithm now: while (x<1000) { q=random() if (q<=.5) makegen(2, 24, 2000, 0,0,1,1,2,.5,19,.5,30,0) if (q>.5) makegen(2, 24, 2000,0,0,1,1,2,0) WAVETABLE(start, dur, amp, pitch,stereo) start=start+dur x=x+1 }Now we consider dur. This baby is non-dependent , it doesnt care what the lastdur was, but it is CYCLIC. We want to cycle through the row to get durations.So heres where we use our counter, c. To reiterate what we developed a whileback, the statements c=c+1 if (c>11) c=0will keep our counter going round and round as we go round the loop.Then, before our Action Statements, we put an equation giving us the value ofdur: dur = (sampfunc(3, c) + 2) / 20
  • 10. Remember, what this says is: get the value from our row table (table #3) that cpoints to. Then, add 2, so we dont get any 0 durations (we could have addedany positive number), then, divide by 20 (again, thats arbitrary) to make thedurations shorter (turning up the tempo (I like things fast (I do live in NewYork, after all.)))OK, lets check where we are now: rtsetparams(44100, 2) rtoutput("evil.rows.aiff") makegen(1, 10 , 2000, 1, .2, .2, .2) makegen(3, 2, 12, 0) 0, 2, 8 , 11, 10, 6, 3, 4, 7, 9, 5, 1 while (x<1000) { q=random() if (q<=.5) makegen(2, 24, 2000, 0,0,1,1,2,.5,19,.5,30,0) if (q>.5) makegen(2, 24, 2000, 0,0,1,1,2,0) dur=(sampfunc(3, c)+2)/20 WAVETABLE(start, dur, amp, pitch, stereo) start=start+dur c=c+1 if (c>11) c=0 x=x+1 }Amp and stereo follow similar procedures. For amp, we have to add a number(12 again, I guess) so we dont get an amp of 0 once every 12 notes, (whichwould be pointless (but then again, this whole example is rather pointless)),then multiply it all by 1000 to bring the amplitude values into an audiblerange (remember, RTcmix amplitudes run from 0 to 32768); for stereo, which asyou remember, must be from 0 to 1 (left to right), we must "shrink" the rangefrom 0 - 11 to 0 - 1, by dividing by 11.Hence: amp=(sampfunc(3,c)+12)*1000 stereo=sampfunc(3,c) / 11Pitch is not too much more complicated. Remember that octave-pitch-classnotation has 2 parts, an octave argument, followed by a . , then a pitch-class , orpc argument. So, we will generate a random octave, and then look up ourpitch-class from the table as we discussed earlier, but divide it by 100, so thatit will come after a decimal point. (4 divided by one hundred will be .04, or10/100 will be .10) Then, we just add them, and taadaa!! instant pitch. (Inother words, pc 2 will become .02, if we add that to octave 8, we get 8.02,middle d.)Hence:
  • 11. oct=trunc(random()*5)+6 gives us a random octave between 6 and 11. (since random() gives us numbers with lots of decimal stuff, and we just want integers for the octave, no decimal stuff, we use trunc(). So if random()*7 gives us 6.632453645, then trunc(6.632453645) gives us 6) The following statement gives us our complete pitch argument: pitch=oct + (sampfunc(3, c)/100)step 7) Initialize all variables. Write all the variablesyouve used, at the top of your score, and set them equalto 0 (or another initial value, if necessary). Lets check the whole example out now: rtsetparams(44100,2) rtoutput("evil.rows.aiff") makegen(1, 10 , 2000, 1, .2, .2, .2) makegen(3, 2, 12, 0) 0, 2, 8 , 11, 10, 6, 3, 4, 7, 9, 5, 1 x=0 q=0 dur=0 c=0 amp=0 stereo=0 oct=0 pitch=0 start=0 while (x<100) { /* choose articulation (envelope) */ q=random() if (q<=.5) makegen(2, 24, 2000, 0,0,1,1,2,.5,19,.5,30,0) if (q>.5) makegen(2, 24, 2000, 0,0,1,1,2,0) /* choose duration */ dur=(sampfunc(3, c)+2)/20 /*choose amplitude and stereo position */ amp=(sampfunc(3,c)+12)*1000 stereo=sampfunc(3,c)/11 /*choose pitch*/ oct=trunc(random()*5)+6 pitch=oct + (sampfunc(3, c)/100) /*Action Statement */ WAVETABLE(start, dur, amp, pitch, stereo) /* update dependent variables*/ start=start+dur /* update the cycling counter */ c=c+1
  • 12. if (c>11) c=0 /* update the main loop control counter*/ x=x+1 } step 8) Run it, test it, fix it, love it!!! This baby should work now, but its musical usefulness is currently highly questionable. Ill leave it to you, gentle reader , to modify it into something someone might want to listen to. I chose this example, because it demonstrated all the different kinds of variables and their uses. Most simple, texture-generating algorithms wont use all of the types of variables and variable-uses discussed here. But, now that youve been through this monstrosity, building your own algorithms will be, (hopefully) a much easier task. Whelp, thats about it. (fun with fonts) Lets just review the 8 steps: 1) Explore your idea, whats happening in the different musical parameters . . 2) Divide job into loops 3) Basic stuff: rtsetparams, output files, input files, makegens, etc., repeat 4-7 for each loop: 4) Outlines of loop (while or for statements, counters, etc.) 5) Action Statements 6) Work outwards from Action Statements , figuring out each parameter. 7) Initialize variables. 8) Run it, test it, etc.Note: This tutorial was just to get you started using algorithmic compositional tools. Thereare an infinite number of approaches to algorithmic composition, and we encourage
  • 13. anyone to strike out in different directions. Every new approach will bring new musicalresults, and ways of thinking about music, and that’s always exciting. . . .