package rice2604hw; /* ==================================================================== 2604-JSYN/JMSL FINAL PROJECT - KeyPlayer J.Rice -------------------------------------------------------------------- This applet uses KeyListener to receive character info from the computer keyboard. This data is used to create musical notes via Jsyn Circuits. First the ASCII data is made linear via a *long* case statement. Once the keys are in numbered order they are scaled to pitches and sounded using the DetunedOcts synth circuit. Base frequency, octave stretch, envelope & parameter resetting are all within the users control. As you play, JMSL data structures called MusicShapes are filled with your material. If accompaniment is set to 'true', the computer will throw play the MusicShapes back at you based on many randomly-derived decisions. These shapes will be run through manipulations so as the piece progresses it gets more warped and interesting! There is on-screen reporting of most computer and user actions. The user can launch any valid shape numbered 0-9 by pressing *shift* and the corresponding number key ie: '!' through ')'. As this is done the shape is further manipulated results of which are reported on screen. -------------------------------------------------------------------- 11/2000 ==================================================================== */ import java.awt.*; import java.awt.event.*; import java.applet.Applet; import com.softsynth.jsyn.*; import com.softsynth.jmsl.*; public class KeyPlayerApplet extends Applet implements KeyListener, ActionListener { Button keyButton; int shapeMax = 15; //amount of shapes allowed before //previous shapes are overwritten TextArea myTextOut; Panel p0, p, p2; TextField octaveStrField, baseFreqField, noteKountField, keyLevField, accompOnField, timeThreshField; double octaveStretch = 2.0, baseFreq = 220, timeThreshold = 1.5, keyLevel = 0.15; int lastKeyDown, noteNum, accompOn = 0; //accompOn used as boolean double durTop, gapTop, dur; //timing management int noteKount, shapeKount, launchKount = 0; SynthEnvelope ampEnvData, sideEnvData; EnvelopePlayer ampEnvPlayer, sideEnvPlayer; DetunedOcts myCirc; //Circuit for key player LineOut myOut; double[] addNote; //current note temp storage MusicShape shape1; //used by makeShape() [old] MusicShape[] shapeArray; //holds all shapes //========================================================================= public void init() { p0 = new Panel(); p0.setBackground(Color.pink); p0.setLayout(new GridLayout(2, 1)); //=========== p = new Panel(); p.setLayout(new GridLayout(4, 1)); p.setBackground(Color.yellow); addKeyListener( this ); p2 = new Panel(); p2.setLayout(new GridLayout(2, 1)); p2.add(new Label("Computer Keyboard Player/Accompanist", Label.CENTER)); p2.add(keyButton = new Button("Activate KeyBoard")); keyButton.addActionListener(this); p.add(p2); //----------- p2 = new Panel(); p2.setLayout(new GridLayout(2, 3)); p2.add(new Label("- octStretch _", Label.CENTER)); p2.add(new Label("= baseFreq +", Label.CENTER)); p2.add(new Label("noteKount", Label.CENTER)); p2.add(octaveStrField = new TextField("2.0")); p2.add(baseFreqField = new TextField("220.0")); p2.add(noteKountField = new TextField("0")); p.add(p2); //----------- p2 = new Panel(); p2.setLayout(new GridLayout(2, 3)); p2.add(new Label("' gapLimit \"", Label.CENTER)); p2.add(new Label("? accompOn ?", Label.CENTER)); p2.add(new Label("{ keyLvl }", Label.CENTER)); p2.add(timeThreshField = new TextField("1.5")); p2.add(accompOnField = new TextField("false")); p2.add(keyLevField = new TextField("0.15")); p.add(p2); //------------ p2 = new Panel(); p2.setLayout(new GridLayout(1, 3)); p2.add(new Label("newKeyEnvelopes = [", Label.CENTER)); p2.add(new Label("newKeyParameters = ]", Label.CENTER)); p2.add(new Label("newKeyInstrument = |", Label.CENTER)); p.add(p2); p0.add(p); //============ p0.add(myTextOut = new TextArea(8, 50)); add(p0); } /*-------------------------------------------- */ public void start() { JMSL.setSTDOut(new TextAreaSTDOut(myTextOut)); JMSLRandom.randomize(); addNote = new double[3]; dur = JMSL.now(); gapTop = dur; shapeKount = shapeMax; //Filter initial false duration //Then count wraps around for #1 try { Synth.startEngine(0); JMSL.clock = new com.softsynth.jmsl.jsyn.SynthClock(); Synth.verbosity = Synth.SILENT; setupSynthesis(); setParameters(); setEnvelopes(); shapeArray = new MusicShape[shapeMax]; shapeArray[0] = new MusicShape(3); } catch (SynthException e) { SynthAlert.showError(this,e); } getParent().validate(); getToolkit().sync(); } //-------------------------------------------------------- public void setupSynthesis() { try { myCirc = new DetunedOcts(); //Hook up the circuit and turn it on myOut = new LineOut(); myCirc.output0.connect( 0, myOut.input, 0 ); myCirc.output1.connect( 0, myOut.input, 1 ); ampEnvPlayer = new EnvelopePlayer(); sideEnvPlayer = new EnvelopePlayer(); ampEnvPlayer.output.connect(0, myCirc.amp, 0); sideEnvPlayer.output.connect(0, myCirc.sideamps, 0); myCirc.start(); myOut.start(); ampEnvPlayer.start(); sideEnvPlayer.start(); } catch (SynthException e) { SynthAlert.showError(this, e); } } //======================================================== public void deleteAll() { myCirc.delete(); myOut.delete(); ampEnvPlayer.delete(); sideEnvPlayer.delete(); myCirc = null; myOut = null; ampEnvPlayer = null; sideEnvPlayer = null; } //------------------------------------- public void resetAll() { deleteAll(); setupSynthesis(); setParameters(); setEnvelopes(); } //------------------------------------- public void setParameters() { //Randomly generates keyboard sound parameters double vari; vari = (1 / Math.pow(2, JMSLRandom.choose(4))); myCirc.octaveMult.set(vari); JMSL.out.println("octaveMult = " + vari); vari = ((int)(JMSLRandom.choose(20.1) * 1000.0)/100000.0); JMSL.out.println("detune = " + vari); myCirc.detune.set(vari); vari = (JMSLRandom.choose(10000)); myCirc.cutoff.set((int)vari); JMSL.out.println("cutoff = " + vari); } //-------------------------------------------------------- public double[] makeEnvelope2() { // ARRAY: double[] d = new double[8]; //Even #s are timing info double x, y, z, dur; //Odds are ADSR levels z = JMSLRandom.choose(0.5); //edit 11/05--Attack time x = JMSLRandom.choose(1.0); d[0] = z; d[1] = .75; //Attack level y = Math.abs(x - z); //edit 11/05 d[2] = y; d[3] = (JMSLRandom.choose(0.3) + 0.3); d[4] = x * 0.5; d[5] = (JMSLRandom.choose(0.3) + 0.3); d[6] = x; d[7] = 0.0; dur = z + y + x + (x * .5); dur = ((int)(dur * 1000.0) / 1000.0); JMSL.out.print("[Envelope total dur: " + dur + "] " ); return d; } //-------------------------------------------------------- public void setEnvelopes() { ampEnvData = new SynthEnvelope(makeEnvelope2()); if ((JMSLRandom.choose(2) % 2) == 0) sideEnvData = new SynthEnvelope(makeEnvelope2()); else { sideEnvData = ampEnvData; JMSL.out.println(" ** [Both use same envs]"); } JMSL.out.println(); } //-------------------------------------------------------- public void stop() { myOut.delete(); myOut = null; myCirc.delete(); myCirc = null; Synth.verbosity = Synth.SILENT; Synth.stopEngine(); } //====================MAP ASCII NUMBERS TO LINEAR ORDER=================== public void handleKeyDown( int key ) { noteNum = -1; switch(key) { case (int)'a': noteNum=9; break; case (int)'b': noteNum=4; break; case (int)'c': noteNum=2; break; case (int)'d': noteNum=11; break; case (int)'e': noteNum=21; break; case (int)'f': noteNum=12; break; case (int)'g': noteNum=13; break; case (int)'h': noteNum=14; break; case (int)'i': noteNum=26; break; case (int)'j': noteNum=15; break; case (int)'k': noteNum=16; break; case (int)'l': noteNum=17; break; case (int)'m': noteNum=6; break; case (int)'n': noteNum=5; break; case (int)'o': noteNum=27; break; case (int)'p': noteNum=28; break; case (int)'q': noteNum=19; break; case (int)'r': noteNum=22; break; case (int)'s': noteNum=10; break; case (int)'t': noteNum=23; break; case (int)'u': noteNum=25; break; case (int)'v': noteNum=3; break; case (int)'w': noteNum=20; break; case (int)'x': noteNum=1; break; case (int)'y': noteNum=24; break; case (int)'z': noteNum=0; break; case (int)',': noteNum=7; break; case (int)'.': noteNum=8; break; case (int)';': noteNum=18; break; case (int)'1': noteNum=29; break; case (int)'2': noteNum=30; break; case (int)'3': noteNum=31; break; case (int)'4': noteNum=32; break; case (int)'5': noteNum=33; break; case (int)'6': noteNum=34; break; case (int)'7': noteNum=35; break; case (int)'8': noteNum=36; break; case (int)'9': noteNum=37; break; case (int)'0': noteNum=38; break; case (int)'!': launchShape(1); break; //Map control characters case (int)'@': launchShape(2); break; // To methods case (int)'#': launchShape(3); break; case (int)'$': launchShape(4); break; case (int)'%': launchShape(5); break; case (int)'^': launchShape(6); break; case (int)'&': launchShape(7); break; case (int)'*': launchShape(8); break; case (int)'(': launchShape(9); break; case (int)')': launchShape(0); break; case (int)'_': handleOctaveStr(key); break; case (int)'-': handleOctaveStr(key); break; case (int)'\\': makeMusicShape(); break; case (int)'+': handleBaseFreq(key); break; case (int)'=': handleBaseFreq(key); break; case (int)'{': handleKeyLevel(key); break; case (int)'}': handleKeyLevel(key); break; case (int)'[': JMSL.out.println(); setEnvelopes(); break; case (int)']': setParameters(); break; case (int)'|': resetAll(); break; case (int)'"': handleTimeThr(key); break; case (int)''': handleTimeThr(key); break; case (int)'?': handleAccompOn(); break; } // - - - - - -IF KEY IS PLAYABLE, CALL APPROPRIATE METHODS- - - - - - - if (noteNum != -1) { addToCurrentShape(); //duration completes PREVIOUS note here... if (accompOn == 1) //won't LAUNCH random shapes IF 'false' if ((noteKount % (JMSLRandom.choose(8) + 5)) == 0) launchInst(); makeNote(); //pitch and velocity are stored here for THIS note noteKount++; noteKountField.setText("" + noteKount); } } //=========================MAKENOTE AND SHAPE MANAGEMENT======================== public void addToCurrentShape() { double delay = 0; durTop = JMSL.now(); dur = durTop - dur; //TIME between now and last keydown if (noteKount == 15) handleAccompOn(); //Wait 15 notes before launching shapes if (shapeKount == shapeMax) { //ONLY happens first time... shapeKount = 0; JMSL.out.println("- - - - - - - - - - - - - - - - - - - - - - - new shape #" + (shapeKount)); } else { delay = durTop - gapTop; //TIME between now and last keyup addNote[0] = (int)(dur*100.0) / 100.0; JMSL.out.println("ADDED to shape #"+ shapeKount + " dur: " + addNote[0] + "; elem# " + (shapeArray[shapeKount].size()+1)); shapeArray[shapeKount].add(addNote[0], addNote[1], addNote[2]); //STORE in shape dur = durTop; //STORE 'now' for nextime around... //------------------------CREATE NEW SHAPE WHEN------------------------ if ((delay > timeThreshold) && (shapeArray[shapeKount].size() > 6)){ shapeArray[shapeKount].set(10.0, shapeArray[shapeKount].size()-1, 0); if (shapeKount < (shapeMax - 1)) shapeKount++; else shapeKount = 0; //WRAP AROUND When max is hit shapeArray[shapeKount] = new MusicShape(3); JMSL.out.println("- - - - - - - - - - - - - - - - - - - - - - - new shape #" + (shapeKount)); //--------------------------------------------------------------------- } } } //------------------------------------------------------------------------- public void launchShape(int x) { //When launched by user if (shapeArray[x] != null) { JMSL.out.println("------------------------"); JMSL.out.print("User selected shape #" + x + " ** "); if ((JMSLRandom.choose(2) % 2) == 1) //pick which instrument shapeArray[x].setInstrument(new FiltFMInst()); else shapeArray[x].setInstrument(new KeyInstrument()); double mult = (JMSLRandom.choose(0.6) + .7); //prepare to scale duration +/-30% shapeArray[x].scale(mult, 0, (shapeArray[x].size() - 1), 0); JMSL.out.println(); JMSL.out.print("Duration mult'd by: " + ((int)(mult*100.0) / 100.0)); mult = (JMSLRandom.choose(0.6) + .7); //prepare to scale pitch +/-30% shapeArray[x].scale(mult, 0, (shapeArray[x].size() - 1), 1); JMSL.out.print(" ** Pitch mult'd by: " + ((int)(mult*100.0) / 100.0)); shapeArray[x].launch(JMSL.now()); JMSL.out.println(); } else JMSL.out.println("****null MusicShape****"); } //---------------------------------- public void launchInst() { //When launched by computer int pick; if (shapeArray[shapeMax - 1] == null) pick = JMSLRandom.choose(shapeKount); else pick = JMSLRandom.choose(shapeMax - 1); JMSL.out.println("------------------------"); switch(JMSLRandom.choose(4)) { case (1): { shapeArray[pick].reverse(0, (shapeArray[pick].size() - 1), 1); JMSL.out.print("REVERSED PITCH: "); break; } case (2): { shapeArray[pick].scramble(0, (shapeArray[pick].size() - 1), 1); JMSL.out.print("SCRAMBLED PITCH: "); break; } case (3): JMSL.out.print("NO MANIPULATION: "); break; case (0): JMSL.out.print("NO MANIPULATION: "); break; } JMSL.out.println("Program picks shape #" + pick); if ((shapeKount > 5) && ((shapeKount % 2) == 1)) //Save FM sound til later (>5) shapeArray[pick].setInstrument(new FiltFMInst()); else shapeArray[pick].setInstrument(new KeyInstrument()); shapeArray[pick].launch(JMSL.now()); JMSL.out.println(" **launched"); launchKount++; } //------------------------MAKES KEYBOARD NOTES ONLY---------------------- public void makeNote() { double newFreq; newFreq = baseFreq * Math.pow(octaveStretch, noteNum/12.0); JMSL.out.print("*keyboard* " + (((int)(newFreq*100.0))/100.0) + "Hz : "); myCirc.freq.set(newFreq); addNote[1] = newFreq; addNote[2] = keyLevel; int now = Synth.getTickCount(); ampEnvPlayer.amplitude.set(now, keyLevel); sideEnvPlayer.amplitude.set(now, keyLevel); ampEnvPlayer.envelopePort.clear(now); sideEnvPlayer.envelopePort.clear(now); ampEnvPlayer.envelopePort.queue(now, ampEnvData, 0, 2); sideEnvPlayer.envelopePort.queue(now, sideEnvData, 0, 2); ampEnvPlayer.envelopePort.queueLoop(now, ampEnvData, 2, 1); sideEnvPlayer.envelopePort.queueLoop(now, sideEnvData, 2, 1); } //------------------------independent method---------------------------- public void makeMusicShape() { //This is called anytime by key: '\' //Holdover from earlier testing double[] event = new double[3]; int eventNum; shape1 = new MusicShape(3); eventNum = JMSLRandom.choose(10) + 2; for (int i = 0; i < eventNum; i++) { event[0] = (JMSLRandom.choose(2.0) + .1); event[1] = (JMSLRandom.choose(4000) + 200); event[2] = (JMSLRandom.choose(0.4) + .2); shape1.add(event[0], event[1], event[2]); } shape1.setInstrument(new KeyInstrument()); JMSL.out.println(" **TEST SHAPE** "); shape1.launch(JMSL.now()); } //====================CONTROL KEY HANDLING========================= public void handleAccompOn() { if (accompOn == 1) accompOn = 0; else accompOn = 1; if (accompOn == 1) accompOnField.setText("true"); else accompOnField.setText("false"); } //------------------------------- public void handleTimeThr(int key) { if (key == (int)'"') timeThreshold = timeThreshold + 0.1; else timeThreshold = timeThreshold - 0.1; if (timeThreshold < 0.0) timeThreshold = 0.0; timeThreshField.setText(""+ timeThreshold); } //------------------------------- public void handleKeyLevel(int key) { if (key == (int)'}') keyLevel = keyLevel + 0.1; else keyLevel = keyLevel - 0.05; if (keyLevel < 0.0 || keyLevel > 1.0) keyLevel = .3; keyLevField.setText(""+ keyLevel); } //------------------------------------------------ public void handleBaseFreq(int key) { if (key == (int)'+') baseFreq = baseFreq + 20.0; else baseFreq = baseFreq - 10.0; if (baseFreq < 20.0) baseFreq = 20.0; baseFreqField.setText("" + baseFreq); } //------------------------------------ public void handleOctaveStr(int key) { if (key == (int)'_') octaveStretch = octaveStretch + .1; else octaveStretch = octaveStretch - .1; if (octaveStretch < .1) octaveStretch = .5; octaveStrField.setText("" + octaveStretch); } //====================KEYLISTENER INTERFACE METHODS======================= public void keyPressed( KeyEvent e ) { int key = e.getKeyChar(); if (lastKeyDown != key) handleKeyDown(key); lastKeyDown = key; } //------------------------------------- public void keyTyped( KeyEvent e ) {}; //------------------------------------- public void keyReleased( KeyEvent e ) { lastKeyDown = -1; int now = Synth.getTickCount(); gapTop = JMSL.now(); ampEnvPlayer.envelopePort.queue(now, ampEnvData, 3, 1); sideEnvPlayer.envelopePort.queue(now, sideEnvData, 3, 1); }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public void actionPerformed( ActionEvent e) { Object source = e.getSource(); if (source == keyButton) requestFocus(); } }//=====================================END APPLET=============================================