/*************************************************************************************/ /* Adam Green and John Cortese, Assignment 3 /*************************************************************************************/ /* Our patch is called "Octave Perception." It allows the user to do several things that test a listener's perception of sound. Specifically, it allows the user to choose amplitudes for an octave of notes and half-steps (13 notes). These are sine waves. It lets the user choose where that octave falls. Next, it has programmed in controls that let the user slightly modify each note randomly up and down in frequency, in order to test classical perception of accepted musical "notes." Finally, it has a special feature that gives the user the opportunity to input how many beats per second he/ she wants to hear between an extra root note and the original root note. This program is useful for testing the traditional perception of sound and what happens when frequencies are either at extremes or are modified very slightly, or both. */ /* This program is not very complicated, but we think it shows good understanding of a couple of particular tools, specifically, the SinOsc function, the GUIs and their placement, and the Rand function. In the GUI window, the top slider determines where the octave starts; the default is 440. The next slider down determines the amplitude of the root. The small rows of sliders underneath determine the amplitudes of all of the half-steps in the octave (the sliders on the left), and then how big a window of change in frequency the Rand function should be given (the sliders on the right) for each note. If a variable has a number, it corresponds to the number half step in the octave, i.e. amp3 determines the amplitude of half-step 3 in the octave, which, for instance, would be a D in a scale whose root was C. The random function is determined using code in the slider definitions themselves, rather than in the SinOsc code, because it works better there; in the SinOsc functions, it was difficult to change frequency randomly during playing of the sound. As it works now, each time the program is run, each slider has a different max and min from the time before and from all the other Rand sliders. The only two variables without numbers are bps and oct. Oct was mentioned before and is the determiner of the root; bps is beats per second, and allows determination of how many beats per second will be heard with the root. The lowest slider (next to the number window) determines the amplitude of this beating note (there has to be positive amplitude in both the root (second slider down) and the beating tone (lowest slider) for beats to be heard. The number window determines beats per second, including fractions, by adding that much frequency to the root. The aim of this is to determine where people stop hearing beats and start hearing another note, since the maximum change in frequency is 35 Hz. The musical idea of this program is to test perception of sound. Because of that, we did not want to use more complicated waveforms that would act as other variables in what people heard; sine waves work best for these simple tools and the experiments that could be performed with them. Also, all the variable determinations are linear for easier control. Originally, both the "oct" and "bps" variables were 'exponential', but these were too hard to control, so "oct" was changed to linear and "bps" to numerical control. In addition, the "play" command is used instead of "scope" so that someone being tested could not see what they were hearing, only hear. Here's an example of a test that could be performed: a test for perfect pitch. Play different notes; then modify the root and see how high or low the perfect perception goes in frequency. Then modify the frequencies randomly and see if the subject can tell what note is being changed, if it's being changed in the first place, and how much the frequency is changing. Finally, test if a subject with perfect pitch starts hearing notes instead of beats earlier than others with the bps function. * Note: No random frequency changer slider is provided for the root so that the other half-step frequencies may change relative to the root. So, here is our first Supercollider program (or any program for that matter) ever. */ /*************************************************************************************/ ( var w, amp1, amp2, amp3, amp4, amp5, amp6, amp7, amp8, amp9, amp10, amp11, amp12, amp13, amp14, bps, rand2, rand3, rand4, rand5, rand6, rand7, rand8, rand9, rand10, rand11, rand12, rand13, oct; w = GUIWindow.new("Octave Sliders", Rect.newBy( 196, 73, 300, 300 )).backColor_(rgb(250,250,250)); amp1 = SliderView.new( w, Rect.newBy( 10, 40, 170, 15 ), "SliderView", 0, 0, 0.8, 0.04, 'linear') .backColor_(rgb(245,104,49)).knobColor_(rgb(209,164,14)); amp2 = SliderView.new( w, Rect.newBy( 15, 78, 75, 10 ), "SliderView", 0, 0, 0.8, 0.04, 'linear') .backColor_(rgb(176,93,113)).knobColor_(rgb(209,164,14)); amp3 = SliderView.new( w, Rect.newBy( 15, 93, 75, 10 ), "SliderView", 0, 0, 0.8, 0.04, 'linear') .backColor_(rgb(176,93,113)).knobColor_(rgb(209,164,14)); amp4 = SliderView.new( w, Rect.newBy( 15, 108, 75, 10 ), "SliderView", 0, 0, 0.8, 0.04, 'linear') .backColor_(rgb(176,93,113)).knobColor_(rgb(209,164,14)); amp5 = SliderView.new( w, Rect.newBy( 15, 123, 75, 10 ), "SliderView", 0, 0, 0.8, 0.04, 'linear') .backColor_(rgb(176,93,113)).knobColor_(rgb(209,164,14)); amp6 = SliderView.new( w, Rect.newBy( 15, 138, 75, 10 ), "SliderView", 0, 0, 0.8, 0.04, 'linear') .backColor_(rgb(176,93,113)).knobColor_(rgb(209,164,14)); amp7 = SliderView.new( w, Rect.newBy( 15, 153, 75, 10 ), "SliderView", 0, 0, 0.8, 0.04, 'linear') .backColor_(rgb(176,93,113)).knobColor_(rgb(209,164,14)); amp8 = SliderView.new( w, Rect.newBy( 15, 168, 75, 10 ), "SliderView", 0, 0, 0.8, 0.04, 'linear') .backColor_(rgb(176,93,113)).knobColor_(rgb(209,164,14)); amp9 = SliderView.new( w, Rect.newBy( 15, 183, 75, 10 ), "SliderView", 0, 0, 0.8, 0.04, 'linear') .backColor_(rgb(176,93,113)).knobColor_(rgb(209,164,14)); amp10 = SliderView.new( w, Rect.newBy( 15, 198, 75, 10 ), "SliderView", 0, 0, 0.8, 0.04, 'linear') .backColor_(rgb(176,93,113)).knobColor_(rgb(209,164,14)); amp11 = SliderView.new( w, Rect.newBy( 15, 213, 75, 10 ), "SliderView", 0, 0, 0.8, 0.04, 'linear') .backColor_(rgb(176,93,113)).knobColor_(rgb(209,164,14)); amp12 = SliderView.new( w, Rect.newBy( 15, 228, 75, 10 ), "SliderView", 0, 0, 0.8, 0.04, 'linear') .backColor_(rgb(176,93,113)).knobColor_(rgb(209,164,14)); amp13 = SliderView.new( w, Rect.newBy( 15, 243, 75, 10 ), "SliderView", 0, 0, 0.8, 0.04, 'linear') .backColor_(rgb(176,93,113)).knobColor_(rgb(209,164,14)); amp14 = SliderView.new( w, Rect.newBy( 15, 273, 75, 20 ), "SliderView", 0, 0, 0.8, 0.04, 'linear') .backColor_(rgb(102,113,209)).knobColor_(rgb(209,164,14)); bps = NumericalView.new(w, Rect.newBy( 165, 275, 40, 15 ), "NumericalView", 10, 0, 35, 0.25); rand2 = SliderView.new( w, Rect.newBy( 150, 78, 75, 10), "SliderView", 0, -30.rand, 30.rand, 1, 'linear') .backColor_(rgb(91,176,173)).knobColor_(rgb(209,164,14)); rand3 = SliderView.new( w, Rect.newBy( 150, 93, 75, 10), "SliderView", 0, -30.rand, 30.rand, 1, 'linear') .backColor_(rgb(91,176,173)).knobColor_(rgb(209,164,14)); rand4 = SliderView.new( w, Rect.newBy( 150, 108, 75, 10), "SliderView", 0, -30.rand, 30.rand, 1, 'linear') .backColor_(rgb(91,176,173)).knobColor_(rgb(209,164,14)); rand5 = SliderView.new( w, Rect.newBy( 150, 123, 75, 10), "SliderView", 0, -30.rand, 30.rand, 1, 'linear') .backColor_(rgb(91,176,173)).knobColor_(rgb(209,164,14)); rand6 = SliderView.new( w, Rect.newBy( 150, 138, 75, 10), "SliderView", 0, -30.rand, 30.rand, 1, 'linear') .backColor_(rgb(91,176,173)).knobColor_(rgb(209,164,14)); rand7 = SliderView.new( w, Rect.newBy( 150, 153, 75, 10), "SliderView", 0, -30.rand, 30.rand, 1, 'linear') .backColor_(rgb(91,176,173)).knobColor_(rgb(209,164,14)); rand8 = SliderView.new( w, Rect.newBy( 150, 168, 75, 10), "SliderView", 0, -30.rand, 30.rand, 1, 'linear') .backColor_(rgb(91,176,173)).knobColor_(rgb(209,164,14)); rand9 = SliderView.new( w, Rect.newBy( 150, 183, 75, 10), "SliderView", 0, -30.rand, 30.rand, 1, 'linear') .backColor_(rgb(91,176,173)).knobColor_(rgb(209,164,14)); rand10 = SliderView.new( w, Rect.newBy( 150, 198, 75, 10), "SliderView", 0, -30.rand, 30.rand, 1, 'linear') .backColor_(rgb(91,176,173)).knobColor_(rgb(209,164,14)); rand11 = SliderView.new( w, Rect.newBy( 150, 213, 75, 10), "SliderView", 0, -30.rand, 30.rand, 1, 'linear') .backColor_(rgb(91,176,173)).knobColor_(rgb(209,164,14)); rand12 = SliderView.new( w, Rect.newBy( 150, 228, 75, 10), "SliderView", 0, -30.rand, 30.rand, 1, 'linear') .backColor_(rgb(91,176,173)).knobColor_(rgb(209,164,14)); rand13 = SliderView.new( w, Rect.newBy( 150, 243, 75, 10), "SliderView", 0, -30.rand, 30.rand, 1, 'linear') .backColor_(rgb(91,176,173)).knobColor_(rgb(209,164,14)); oct = NumericalView.new(w, Rect.newBy( 75, 15, 40, 15), "NumericalView", 440, 20, 2000, 1); StringView.new( w, Rect.newBy( 192, 14, 91, 17 ), "Root Frequency") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 192, 38, 91, 16 ), "Root Amplitude") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 9, 61, 93, 13 ), "Half-Step Amps") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 143, 60, 130, 13 ), "Half-Step Randomizers") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 93, 76, 13, 12 ), "2") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 93, 91, 18, 15 ), "3") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 94, 107, 18, 12 ), "4") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 93, 120, 12, 12 ), "5") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 93, 136, 16, 14 ), "6") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 95, 151, 19, 14 ), "7") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 95, 168, 16, 12 ), "8") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 95, 181, 15, 14 ), "9") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 93, 197, 20, 12 ), "10") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 93, 211, 19, 13 ), "11") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 93, 227, 21, 13 ), "12") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 93, 243, 17, 12 ), "13") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 94, 275, 65, 15 ), "Beat Amp") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 230, 76, 16, 15 ), "2") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 230, 91, 15, 13 ), "3") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 230, 105, 21, 17 ), "4") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 230, 122, 16, 15 ), "5") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 230, 136, 15, 12 ), "6") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 229, 150, 15, 14 ), "7") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 229, 165, 17, 14 ), "8") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 229, 181, 18, 14 ), "9") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 230, 198, 20, 11 ), "10") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 228, 212, 18, 12 ), "11") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 229, 226, 19, 14 ), "12") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 230, 241, 18, 15 ), "13") .backColor_(rgb(250,250,250)); StringView.new( w, Rect.newBy( 206, 274, 79, 17 ), "Beats/sec") .backColor_(rgb(250,250,250)); {SinOsc.ar ((ControlIn.kr(oct)), 0, (ControlIn.kr(amp1))) + SinOsc.ar (((ControlIn.kr(oct))*(16/15)) + (ControlIn.kr(rand2)), 0, (ControlIn.kr(amp2))) + SinOsc.ar (((ControlIn.kr(oct))*(10/9)) + (ControlIn.kr(rand3)), 0, (ControlIn.kr(amp3))) + SinOsc.ar (((ControlIn.kr(oct))*(6/5)) + (ControlIn.kr(rand4)), 0, (ControlIn.kr(amp4))) + SinOsc.ar (((ControlIn.kr(oct))*(5/4)) + (ControlIn.kr(rand5)), 0, (ControlIn.kr(amp5))) + SinOsc.ar (((ControlIn.kr(oct))*(4/3)) + (ControlIn.kr(rand6)), 0, (ControlIn.kr(amp6))) + SinOsc.ar (((ControlIn.kr(oct))*(64/45)) + (ControlIn.kr(rand7)), 0, (ControlIn.kr(amp7))) + SinOsc.ar (((ControlIn.kr(oct))*(3/2)) + (ControlIn.kr(rand8)), 0, (ControlIn.kr(amp8))) + SinOsc.ar (((ControlIn.kr(oct))*(8/5)) + (ControlIn.kr(rand9)), 0, (ControlIn.kr(amp9))) + SinOsc.ar (((ControlIn.kr(oct))*(5/3)) + (ControlIn.kr(rand10)), 0, (ControlIn.kr(amp10))) + SinOsc.ar (((ControlIn.kr(oct))*(9/5)) + (ControlIn.kr(rand11)), 0, (ControlIn.kr(amp11))) + SinOsc.ar (((ControlIn.kr(oct))*(15/8)) + (ControlIn.kr(rand12)), 0, (ControlIn.kr(amp12))) + SinOsc.ar (((ControlIn.kr(oct))*2) + (ControlIn.kr(rand13)), 0, (ControlIn.kr(amp13))) + SinOsc.ar (((ControlIn.kr(bps)) + (ControlIn.kr(oct))), 0, (ControlIn.kr(amp14)))}.play )