/***************************************************************************************/ /* Dan Sheldon Assignment 3 */ /***************************************************************************************/ /* Concept: Generate random melodies taken from a scale based on the harmonics series. Each scale contains anywhere from 2 to 10 notes and is of the form: fund, fund*(n/n-1), fund*(n-1/n-2), ..., fund*(4/3), fund*(3/2), fund*2 where fund is the fundamental frequency, and n is the number of notes in the scale. Once these parameters are specified by the user, the patch will play random melodies chosen from this scale. The user will also be able to adjust the rhythmic qualities of the melody using the two parameters dur (max.duration) and div (# subdivisions). The duration of each note is determined by picking a random number i between 1 and div, and dividing dur by 2^i. In effect, dur sets the tempo by defining a reference duration such that all actual note durations are some subdivision of dur by a power of 2. Finally, the user can control the percussiveness of the instrument by specifying the ratio of decay time to attack time. Thus a number close to 1 is very percussive in the sense that attack time is short and decay time is long. */ ( var w, fundText, fundBox, fundSlider, // variables for fundamental notesText, notesBox, notesSlider, // variables for number of notes per scale durText, durBox, durSlider, // variables for max note duration divText, divBox, divSlider, // variables for number of subdivisions percText, percBox, percSlider, // variables for percussiveness (value ranges from 0 to 1) duration, // utility variable j, // indexes current note in scale ratio, // ratio multiplier for fundamental env; // an envelope variable // initialize GUI variables w = GUIWindow.new("Melodies", Rect.newBy( 170, 67, 391, 146 )); fundText = StringView.new( w, Rect.newBy( 14, 11, 128, 20 ), "fundamental freq"); fundBox = NumericalView.new( w, Rect.newBy( 155, 11, 57, 20 ), "NumericalView", 440, 100, 1000, 1, 'exponential'); fundSlider = SliderView.new( w, Rect.newBy( 239, 11, 128, 20 ), "SliderView", 440, 100, 1000, 1, 'exponential'); notesText = StringView.new( w, Rect.newBy( 14, 35, 128, 20 ), "# notes in scale"); notesBox = NumericalView.new( w, Rect.newBy( 155, 35, 57, 20 ), "NumericalView", 2, 2, 10, 1, 'linear'); notesSlider = SliderView.new( w, Rect.newBy( 239, 35, 128, 20 ), "SliderView", 2, 2, 10, 1, 'linear'); durText = StringView.new( w, Rect.newBy( 14, 59, 128, 20 ), "max duration"); durBox = NumericalView.new( w, Rect.newBy( 155, 59, 57, 20 ), "NumericalView", 1, 0.1, 2, 0.01, 'linear'); durSlider = SliderView.new( w, Rect.newBy( 239, 59, 128, 20 ), "SliderView", 1, 0.1, 2, 0.01, 'linear'); divText = StringView.new( w, Rect.newBy( 14, 83, 128, 20 ), "subdivisions"); divBox = NumericalView.new( w, Rect.newBy( 155, 83, 57, 20 ), "NumericalView", 1, 1, 8, 1, 'linear'); divSlider = SliderView.new( w, Rect.newBy( 239, 83, 128, 20 ), "SliderView", 1, 1, 8, 1, 'linear'); percText = StringView.new( w, Rect.newBy( 14, 107, 128, 20 ), "percussiveness"); percBox = NumericalView.new( w, Rect.newBy( 155, 107, 57, 20 ), "NumericalView", 0.9, 0.05, 0.999, 0.001, 'linear'); percSlider = SliderView.new( w, Rect.newBy( 239, 107, 128, 20 ), "SliderView", 0.9, 0.05, 0.999, 0.001, 'linear'); // link sliders, number boxes, and variable values fundBox.action = {fundSlider.value = fundBox.value;}; fundSlider.action = {fundBox.value = fundSlider.value;}; notesBox.action = {notesSlider.value = notesBox.value;}; notesSlider.action = {notesBox.value = notesSlider.value;}; durBox.action = {durSlider.value = durBox.value;}; durSlider.action = {durBox.value = durSlider.value;}; divBox.action = {divSlider.value = divBox.value;}; divSlider.action = {divBox.value = divSlider.value;}; percBox.action = {percSlider.value = percBox.value;}; percSlider.action = {percBox.value = percSlider.value;}; // now play the sounds.... Synth.play({ Spawn.ar( { arg spawn, i, synth; duration = durBox.value / (2**(floor(rand(divBox.value)))); // create an envelope based on duration and percussiveness: // attack time is: (1 - percBox.value) * duration // decay time is: percBox.value * duration env = Env.perc( (1 - percBox.value)*duration, percBox.value*duration, 1, -4); // make sure to update the duration spawn.nextTime = duration; // the value for j scrolls through the values 1...notes j = floor(rand(notesBox.value)) + 1; // the value for ratio is ratio = if(j == 1, 1, (j / (j - 1))); FSinOsc.ar(fundBox.value*ratio, EnvGen.ar(env)) } , 1 , durBox.value , nil , 0.6) }); w.close; )