/* * Copyright (C) 1996-1997 Intel Corporation * All Rights Reserved * * A royalty-free copyright license to copy, modify and compile this source * code and to distribute it in your own products in source and binary forms * is hereby granted, so long as this copyright notice and this copyright * license appear in all source code copies. No other rights or licenses * are granted. The Intel name or other trademarks may not be used to * endorse or promote products using this header file without Intel's prior * written permission. * * THIS SOURCE CODE IS PROVIDED "AS IS" WITH NO WARRANTIES WHATSOEVER, * INCLUDING ANY WARRANTY OF MERCHANTABILITY, NONINFRINGEMENT, OR FITNESS * FOR ANY PARTICULAR PURPOSE. Intel disclaims all liability, including * liability for infringement of any proprietary rights, relating to use * of this source code. */ import java.applet.*; import java.net.*; import java.awt.*; import java.lang.Math; import java.util.Random; import spatialAudio.*; import java.io.InputStream; import java.io.IOException; public class Streaming extends java.applet.Applet { /* * we play data from the streaming sound source out through * a direct listener */ Environment env; DirectListener directListener; StreamingSoundSource streamingSoundSource; /* * seven buffers for holding the data */ final static int NUM_BUFFERS = 3; BufferHolder buffer1; BufferHolder buffer2; BufferHolder buffer3; /* * seven byte buffers which we fill with our synthesized data */ byte dataRest[]; byte dataG[]; byte dataF[]; byte dataE[]; byte dataD[]; byte dataC[]; byte dataB[]; byte dataA[]; /* * buttons for making a new tune or replaying the last one */ Button newButton; Button replayButton; /* * number of buffers we'll play, and the num done so far */ int NUM_TO_PLAY = 15; int numSubmitted = 0; int numPlayed = 0; /* * number of bytes per buffer */ int NUM_BYTES = 22050; /* * list of notes in the sequence */ int[] noteList; /* * the images */ Image linesImage; Image noteImage; Image restImage; /* * variables for double-buffering the graphics */ Image offscreen; Graphics offscreenGraphics; public void init() { System.out.println("Initializing applet Streaming...."); /* * set up the user interface components */ setupUI(); /* * initialize the audio objects */ setupAudio(); /* * initialize the buffers */ setupBuffers(); } // init() public void start() { System.out.println("starting applet Streaming...."); /* * connect the direct listener so we can hear the output */ if (directListener != null) directListener.connect(); /* * generate the sequence of notes to play */ generateSequence(); /* * play the sequence of notes */ playSequence(); } // start() public void stop() { System.out.println("stopping applet Streaming...."); if (directListener != null) directListener.disconnect(); } // stop() public void destroy() { /* * release all resources */ System.out.println("destroying applet Streaming...."); if (streamingSoundSource != null) { streamingSoundSource.release(); streamingSoundSource = null; } if (directListener != null) { directListener.release(); directListener = null; } if (env != null) { env.release(); env = null; } } public boolean handleEvent(Event evt) { switch(evt.id) { /* * when we get a buffer event, it means that the audio * renderer is finished with the buffer. so we call * playNextNote() with that buffer so that it can be * refilled with data and resubmitted. this is our * pacing mechanism. */ case SoundSource.BUFFER_EVENT: if (evt.arg == "event1") { playNextNote(buffer1); return true; } if (evt.arg == "event2") { playNextNote(buffer2); return true; } if (evt.arg == "event3") { playNextNote(buffer3); return true; } } // switch /* * if they hit the "New Tune" button, generate a new sequence * of notes and play them. */ if (evt.arg == "New Tune") { generateSequence(); playSequence(); return true; } /* * just play the existing sequence of notes. */ if (evt.arg == " Replay ") { playSequence(); return true; } return false; } public synchronized void playNextNote(BufferHolder b) { /* * we need to repaint every time we want to show a new note */ repaint(); /* * to start off our auto-buffer-feeding mechanism, we "trip" * the event loop with events we create and deliver. the data * variable in the buffers for which the events are tripped are * null in that case; we use that to detect that a not has not * really just been played. * * note that we're keeping separate counters for the number of * buffers submitted and the number of notes played. this is * because we want to paint the new note when it's actually * played, not when it's submitted. */ if (b.data != null) numPlayed++; /* * if we've submitted the total number, then quit */ if (numSubmitted >= NUM_TO_PLAY) return; /* * set the data variable of the buffer holder to the byte * buffer which matches the next note's data, and submit * the buffer. */ b.data = getCurrentNote(); if (streamingSoundSource != null) streamingSoundSource.submitBuffer(b); /* * increment the counter */ numSubmitted++; } public void paint(Graphics g) { /* * this is how much space we allot between notes when we draw them */ int offset = 25; /* * start drawing into the offscreen buffer -- first clear it and * draw the lines. */ offscreenGraphics.setColor(Color.white); offscreenGraphics.fillRect(0, 0, this.size().width, this.size().height); offscreenGraphics.drawImage(linesImage, 0, 55, this); /* * now we iterate through all the notes which have been played and * draw them at the y axis value that represents the note, or draw * a rest. */ for (int i = 0; i < numPlayed; i++) { if (noteList[i] == 0) { offscreenGraphics.drawImage(restImage, offset*(i+1), 73, this); } else { int yVal; switch(noteList[i]) { case 1: yVal = 40; break; // G case 2: yVal = 47; break; // F case 3: yVal = 54; break; // E case 4: yVal = 61; break; // D case 5: yVal = 68; break; // C case 6: yVal = 75; break; // B case 7: yVal = 92; break; // A default: System.out.println("uh-oh"); yVal = 0; break; } // switch offscreenGraphics.drawImage(noteImage, offset*(i+1), yVal, this); } // else } // for /* * finally, draw the whole offscreen image onto the screen. */ g.drawImage(offscreen, 0, 0, this); } // paint() /* * we override update() to reduce flashing when the background is cleared. */ public void update(Graphics g) { paint(g); } /* * Set up the user interface elements */ public void setupUI() { setBackground(Color.white); /* * Create the buttons */ newButton = new Button("New Tune"); replayButton = new Button(" Replay "); this.add(newButton); this.add(replayButton); /* * Create the images */ linesImage = getImage(getDocumentBase(), "lines.gif"); noteImage = getImage(getDocumentBase(), "note.gif"); restImage = getImage(getDocumentBase(), "rest.gif"); /* * Prepare variables for double-buffering */ offscreen = createImage(this.size().width, this.size().height); offscreenGraphics = offscreen.getGraphics(); } /* * Initialize the audio */ public void setupAudio() { /* * First we create the environment * Then we set the characteristics of the environment */ try { env = SpatialAudio.createEnvironment(); EnvironmentModel envModel = new EnvironmentModel(RenderOptions.USE_RIGHT_HAND, RenderOptions.DEFAULT, RenderOptions.MODERATE_BUDGET, null); env.setModel(envModel); System.out.println("Created the environment"); } catch (SpatialAudioException e) { System.out.println("Failed to create the environment"); env = null; return; } /* * Next we create the streaming listener * Then we set the streaming listener position and orientation. * This listener will be at the origin facing in the positive Z direction. */ try { directListener = env.createDirectListener(WaveFormat.PCM_22K_8BIT_STEREO, null); directListener.setOrientation(0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f); directListener.setPosition(0.0f, 0.0f, -60.0f); System.out.println("Created the direct listener..."); } catch (SpatialAudioException e) { System.out.println("Failed to create the direct listener"); directListener = null; return; } /* * Finally we create and position the streaming sound source. * We need to specify the format of the audio data we will be feeding it. */ try { streamingSoundSource = env.createStreamingSoundSource(WaveFormat.PCM_22K_8BIT_STEREO); System.out.println("Created streaming sound source"); streamingSoundSource.setModel(new SoundSourceModel(20.0f, 20.0f, 180.0f, 180.0f, 0.4f)); streamingSoundSource.setPosition(0.0f, 0.0f, 0.0f); streamingSoundSource.setOrientation(0.0f, 0.0f, 1.0f); } catch (SpatialAudioException e) { System.out.println("Failed to create the streaming sound source"); streamingSoundSource = null; } } /* * Create the data we'll send to the streaming sound source; * We create the seven notes of an octave and a rest */ public void setupBuffers() { /* * This is the list where we store the values for the note sequence */ noteList = new int[NUM_TO_PLAY]; /* * Generate the data for the notes */ dataRest = new byte[NUM_BYTES]; dataG = new byte[NUM_BYTES]; dataF = new byte[NUM_BYTES]; dataE = new byte[NUM_BYTES]; dataD = new byte[NUM_BYTES]; dataC = new byte[NUM_BYTES]; dataB = new byte[NUM_BYTES]; dataA = new byte[NUM_BYTES]; for(int i = 0; i < NUM_BYTES; i++) { dataRest[i] = (byte)0; dataG[i] = (byte)(10 * Math.sin(2 * 3.14159 * 196 / 22050.0* i)); // G dataF[i] = (byte)(10 * Math.sin(2 * 3.14159 * 174.61 / 22050.0* i)); // F dataE[i] = (byte)(10 * Math.sin(2 * 3.14159 * 164.81 / 22050.0* i)); // E dataD[i] = (byte)(10 * Math.sin(2 * 3.14159 * 146.83 / 22050.0* i)); // D dataC[i] = (byte)(10 * Math.sin(2 * 3.14159 * 130.81 / 22050.0* i)); // C dataB[i] = (byte)(10 * Math.sin(2 * 3.14159 * 123.47 / 22050.0* i)); // B dataA[i] = (byte)(10 * Math.sin(2 * 3.14159 * 110 / 22050.0* i)); // A } /* * Create the buffers * Initially, we set the data to null */ buffer1 = new BufferHolder(null, (Object)"event1", null, this); buffer2 = new BufferHolder(null, (Object)"event2", null, this); buffer3 = new BufferHolder(null, (Object)"event3", null, this); } /* * generate the sequence of notes to play */ public void generateSequence() { Random random = new Random(); for (int i = 0; i < NUM_TO_PLAY; i++) { noteList[i] = random.nextInt() % 8; if (noteList[i] < 0) noteList[i] = -1*noteList[i]; } } /* * play the sequence of notes */ public synchronized void playSequence() { /* * reset the counters */ numSubmitted = 0; numPlayed = 0; repaint(); /* * flush any data from the streaming sound source's queue */ if (streamingSoundSource != null) streamingSoundSource.flush(); /* * set the data variables back to null so that we will know * we haven't submitted real data yet. */ buffer1.data = null; buffer2.data = null; buffer3.data = null; /* * create and deliver events that match those generated by our * three buffers when they're ready to be reused. this has * the effect of "tripping" the event loop and initiating * streaming play. */ deliverEvent(new Event(this, SoundSource.BUFFER_EVENT, "event1")); deliverEvent(new Event(this, SoundSource.BUFFER_EVENT, "event2")); deliverEvent(new Event(this, SoundSource.BUFFER_EVENT, "event3")); } /* * translate from the numbers we use to represent notes or rests to * the actual byte buffers of data. */ public byte[] getCurrentNote() { switch(noteList[numSubmitted]) { case 0: return dataRest; case 1: return dataG; case 2: return dataF; case 3: return dataE; case 4: return dataD; case 5: return dataC; case 6: return dataB; case 7: return dataA; default: System.out.println("uh-oh"); return dataRest; } // switch } } // Streaming class