The file was updated 2/1/13. Some parts are significantly more sophisticated than earlier versions.
The tutorial assumes that you have already understand KISA parts 1 - 5.
In this lesson we will replace the replace the non-standard looking CircleButtons and ToggleButtons used in the previous KISA sketches with HTML form buttons, checkboxes, and radio buttons. It is fairly easy to use HTML buttons because it is possible for Javascript to call Processing.js methods and Processing.js to call Javascript functions. The connection is done using getElementById function in Processing.js and getInstanceById in Javascript. One limitations is the Javascript cannot directly access Processing.js variables even though it can access its methods.
Using HTML5 does provide one potential advantage. We will show how the <meter> tag and <input type="range">tag can be used.
There are several alternatives. We will discus a couple of them later. But initially, we will use a rather straight forward method even though it uses more code than some of the alternatives. This technique was suggested by "Scott" in http://makesomethingeveryday.scottkobewka.com/draw/draw_code.html which might not be available any more.
Using HTML form buttons means that the .pde and .html files are much more interrelated. Arbitrarily we will begin by looking at the .pde file. Discussion of the proposed HTML5 meter tags and range inputs will be postponed until after completing the discussion of the buttons. .
The most significant change in the .pde file when replacing the Processing.js buttons with HTML buttons is that we will replace the lengthy mouseClicked methods with a number of simple methods that carry out the operations when a particular button is clicked. These new methods are found in lines 134 to 161. All the new method names are prefixed with a "p" to signify they are part of the Processing.js file. (Corresponding Javascript functions are prefixed with "js".)
The code previously in the mouseClicked methods for the "Play", "Pause", and "Stop" buttons was moved to new methods called pPlay, pPause, and pStop. pPlay will be called when the HTML "Play" button is clicked. It just invokes audio.play. Likewise clicking the "Pause" and "Stop" buttons will invoke the pPause and pStop methods. The pStop method just calls the existing stop method. Actually, the stop method could have been called directly by Javascript but using the pStop method fits the pattern set up for the other buttons.
149 void pPlay() { audio.play(); } 150 void pPause() { audio.pause(); } 151 void pStop() { stop(); }
The mouseClicked code for the "Volume down" and "Volume up" buttons was moved to the new pVolumeDown and pVolumeUp methods which will be called when corresponding HTML buttons are clicked. The "Time down" and Time up" buttons where handled in same way.
153 void pVolumeDown() { 154 double v = audio.volume - 0.1; 155 audio.volume = constrain(v, 0, 1); 156 } 157 void pVolumeUp() { 158 double v = audio.volume + 0.1; 159 audio.volume = constrain(v, 0, 1); 160 } ... 164 void pTimeDown() { 165 double t = audio.currentTime - 0.1 * audio.duration; 166 audio.currentTime = constrain(t, 0, audio.duration); 167 } 168 void pTimeUp() { 169 double t = audio.currentTime + 0.1 * audio.duration; 170 audio.currentTime = constrain(t, 0, audio.duration); 171 }
The mouseClicked code for the "Loop" and "Autoplay" buttons had to be modified (actually simplified) because the original code used button routines but the buttons have been eliminated. pLoop just complements the repeatLoop boolean variable. As in KISA 5, this version of KISA loops though all the audio files instead of using the audio API attribute loop. The pAutoplay directly compliments the audio API autoplay attribute.
176 void pLoop() { repeatLoop = !repeatLoop; } 177 void pAutoplay() { audio.autoplay = !audio.autoplay; }
When one of the files is selected by clicking one of the new radio buttons, the pChooseFile(int i) method is called. i is the number of the file selected. So it sets the currentFile variable to i and then uses setSource to set the source to that file.
179 void pChooseFile(int i) { 180 currentFile = i; 181 setSource(files[currentFile]); 182 }
There are few questions remaining: How do we make sure that the "Loop" and "Autoplay" checkboxes actually correspond to the corresponding values in the .pde file? This is a nontrivial question because in some browsers, reloading the web page doesn't reset the check boxes to the default state. The same question applies to the file selection radio buttons. Another question is how do we get the names of the files from the .pde file where they are declared into the HTML file. The answers are similar: we take advantage of Processing.js ability to make changes in the .html file using document.getElementById. When we look at the .html file, we will observe that the loop and autoplay buttons are identified by the id's "loopbox" and "autoplayBox". Thus it is easy to set those elements properly. A call to jsFileButtons creates the specified number of buttons for listing the file names. The file buttons have the form "file#" (where the "#" is replaced by a sequential number) while the name of the files are stored in spans with the ids of the form "file#Name". We can use a loop to set the file names and another statement to mark the current file. In the setup function we use the following code to initialize the HTML elements.
48 document.getElementById('loopBox').checked = audio.loop; 49 document.getElementById('autoplayBox').checked = audio.autoplay; 50 jsFileButtons(numFiles); 51 for (i = 0; i < numFiles; i++) 52 document.getElementById('file'+ i + 'Name').innerHTML = files[i]; 53 document.getElementById('file'+ currentFile).checked = true;
There is one more place where synchronizing is important. To keep the file selection radio buttons indicating the current source file while looping, a statement in the ended function makes sure that the current file is checked. Recall that the button names have the form file#.
137 document.getElementById('file'+currentFile).checked = true;
The values of the volume, current time, and duration of the audio are displayed in the HTML form. In the draw method, the current values are sent to the HTML display function using the following code
80 text("Volume (0 to 1): " + round(10 * audio.volume)/10.0, width/2, 120); 81 text("Loop: " + repeatLoop + " Autoplay: " + audio.autoplay, width/2, 145); 82 if (frameCount % 5 == 0) { 83 try { // designed to solve a timing problem - drawn before setup finished 84 setVolumeGraphics(audio.volume); 85 setCurrentTimeGraphics(audio.currentTime, audio.duration); 86 } catch (Exception e) { 87 } 88 }
Why is the try/catch structure needed? It turns out the draw methods may be called before the HTML code is completed. This would cause an exception. The try/catch structure just ignores the problem until the HTML code is complete. It didn't seem necessary to update the volume and time displays 20 times a second. To reduce the load on the computer, they are updated every 5th time the canvas is redrawn.
There are a couple of other minor changes. Largely for debugging purposes, the values of Loop (repeatLoop) and Autoplay were to the added sketch in line 81. The height of the sketch was reduced in line 7 because the space for the Processing.js buttons was no longer needed.
We will now turn our attention to the .html file which obviously becomes more complicated when HTML form buttons are used.
Lets first look at the section where the buttons are added. Remembering "KIS", we will not bother with nice formatting. The form is rather straight forward. In each case, the onclick event calls a js...() function. (We are using the "js" prefix to help make it clear that the function is a Javascript function included in this file.) The code for the buttons is shown below.
85 <form> 86 <input type="button" value="Play" onclick="jsPlay()"> 87 <input type="button" value="Pause" onclick="jsPause()"> 88 <input type="button" value="Stop" onclick="jsStop()"> 89 <br> 90 <input type="button" value="Volume down" onclick="jsVolumeDown()"> 91 <input type="button" value="Volume up" onclick="jsVolumeUp()"> 92 <br> 93 <input type="button" value="Time down" onclick="jsTimeDown()"> 94 <input type="button" value="Time up" onclick="jsTimeUp()"> 95 <br> 96 <input id="loopBox" type="checkbox" value="Loop" onclick="jsLoop()">Loop 97 <input id="autoplayBox" type="checkbox" value="Autoplay" onclick="jsAutoplay()">Autoplay
After these buttons, come the buttons for the file names. The problem is that the number of files and their names is specified in the .pde file. Even worse, we don't even know for sure that the .pde file has been loaded yet. So we just reserve room for these buttons with a section whose contents will be determined later.
99 <div id="putFileButtonsHere"><br>reserved for file buttons and names<br></div>
The form continues with the experimental section and alternatives which will be discussed later.
We now turn our attention to the js...() functions that respond to clicking a button. The first eight are all similar so only a few are shown below.
7 <script type="text/javascript"> 8 9 function jsPlay() { 10 var pjs = Processing.getInstanceById("KISA"); 11 pjs.pPlay(); 12 } 13 function jsPause() { 14 var pjs = Processing.getInstanceById("KISA"); 15 pjs.pPause(); 16 } 17 function jsStop() { 18 var pjs = Processing.getInstanceById("KISA"); 19 pjs.pStop(); 20 } ... 72 </script>
There is one important point to make. Consider the line
var pjs = Processing.getInstanceById("KISA");
which appears in each function. This statement connects the Javascript to the Processing.js code. It uses the Processing.getInstanceById function using the identifier, "KISA", specified in the canvas tag. Of course any other identifier could be used.
After setting up the pjs variable, the corresponding p...() in the .pde file is called to carry out the desired operation.
One might ask, why doesn't on just include the var statement at the beginning of the script and avoid having to do it in every function. That would really simplify things but it doesn't work unfortunately if the .pde file has not been loaded before the statement is executed.
The jsChooseFile(i) is slightly different as it services clicks on all the file selection buttons. The argument i specifies the particular file clicked on.
56 function jsChooseFile(i) { 57 var pjs = Processing.getInstanceById("KISA"); 58 pjs.pChooseFile(i); 59 }
The final js... function, jsFileButtons(numButtons) is use to create the file buttons. It is called by the setup function in the .pde file. This was required because only the .pde file knows how many files have been specified. Later, setup uses a for loop to insert the file names into the .html file. First the code in the .html file.
61 function jsFileButtons(numButtons) { // called by Processing.js 62 var i; 63 var s = ""; 64 for (i = 0; i < numButtons; i++) { 65 var theId = "file" + i; 66 s += '<input id="' + theId + '" type="radio" name="file" value="' + theId + '"' 67 + ' onclick="jsChooseFile(' + i + ')"><span id = "' + theId + 'Name">??</span><br>'; 68 } 69 document.getElementById("putFileButtonsHere").innerHTML = s; 70 }
When i = 0, the nasty looking lines 66 and 67 produce the following code:
<input id="file0" type="radio" name="file0" value="file0" onclick="JsChooseFile(0)"> <span id = "file0Name">??<span><br>
The function creates the specified number of buttons but still does not know the names of the files. Hence it just puts "??" where the filename is supposed to be.
In the setup in the .pde file, the following code first calls the function jsFileButtons, then replaces the "??" with the file names, and finally checks the currently selected file.
50 jsFileButtons(numFiles); 51 for (i = 0; i < numFiles; i++) 52 document.getElementById('file'+ i + 'Name').innerHTML = files[i]; 53 document.getElementById('file'+ currentFile).checked = true;
The draft version of HTML5 includes a <meter> tag that would be great for displaying the volume and current time. It also includes a range input (a slider) for forms that would a convenient way to adjust the volume and current time. Unfortunately as of 1/30/13 only Chrome and Opera implement both completely. Safari 5.1 does a nice job with the range input but does not implement the meter graphically. Firefox 18 does the opposite. Internet Explorer 9 does not implement either graphically. If your browser doesn't implement these items you may see nothing or may see a text box with the value. The image shown in this sections shows the meters and range inputs as they appear in Chrome 23. Lets see how these items can be used. First the lines in the .html file:
101 Volume: 102 <meter id="volumeMeter" min="0" max="1" value="1.00" ></meter> 103 <span id="volumeText">100%</span> 104 <br> 105 Current Time: 106 <meter id="currentTimeMeter" min="0" value="0" /></meter> 107 <span id="currentTimeText"> 0.0/1.0 sec</span> 108 <br> 109 Volume: 110 <input id="volumeRange" type="range" value = "100" min = "0" max = "100" 111 onchange="jsVolumeChange()"> 112 <br> 113 Time: 114 <input id="timeRange" type="range" value = "0" min = "0" max = "100" 115 onchange="jsTimeChange()">
Volume values are between 0 and 100 so the volume meter's and the range's min and max attributes are set accordingly. KISA uses the default value of 100 for the initial value of the volume. The minimum and initial value for the current time will be 0. The maximum value will be reset by the .pde file after it is known.
We will use the draw method in the .pde file to keep the meter and range graphics updated. In order to avoid having to call the getElementById every time the meter and range graphics are redrawn, some global variables are declared and then evaluated in the draw method.
25 Element volumeMeter, volumeValue, currentTimeMeter, currentTimeText, 26 volumeRange, timeRange; ... 55 // get meter and input range elements 56 volumeMeter = document.getElementById("volumeMeter"); 57 volumeValue = document.getElementById("volumeValue"); 58 currentTimeMeter = document.getElementById("currentTimeMeter"); 59 currentTimeText = document.getElementById("currentTimeText"); 60 volumeRange = document.getElementById("volumeRange"); 61 timeRange = document.getElementById("timeRange");
The draw method calls the setVolumeDisplay and setCurrentTimeValue methods which actually do the work. It didn't seem necessary to update the meter and range graphics 20 times a second so, in order to reduce overhead, they are called every 5 times the sketch is redrawn. setVolumeGraphics and setCurrentTimeGraphics methods send the appropriate values sent to the web document. The volume values were rounded to the nearest percent. The volume values were rounded to the nearest percent and the time values to the nearest 10th second. You might prefer to round the time values to the nearest second. The step is also set in case the audio file lasts less than 10 seconds. The following code includes a section from the draw method, and concludes with the two methods.
82 if (frameCount % 5 == 0) { 83 try { // designed to solve a timing problem - drawn before setup finished 84 setVolumeGraphics(audio.volume); 85 setCurrentTimeGraphics(audio.currentTime, audio.duration); 86 } catch (Exception e) { 87 } 88 } ... 185 // volumeMeter, volueValue, volumeRange are in .html file 186 try { 187 double v, v100; 188 v = round(100 * vol)/100; 189 v100 = 100 * v; 190 volumeMeter.value = vol; 191 volumeText.innerHTML = v100 + "%"; 192 volumeRange.value = v100; 193 } catch (Exception e) { 194 } 195 } // setVolumeGraphics 196 197 void setCurrentTimeGraphics(double cur, double dur) { 198 // currentTimeMeter and timeRange are in .html file 199 double c, d; 200 try { 201 c = round(10 * cur)/10; 202 d = round(10 * dur)/10; 203 currentTimeMeter.value = c; 204 currentTimeMeter.max = d; 205 currentTimeText.innerHTML = c + "/" + d + " sec"; 206 timeRange.value = c; 207 timeRange.max = d; 208 if (d < 10) 209 timeRange.step = .1; 210 else 211 timeRange.step = 1. 212 } catch (Exception e) { 213 } 214 } // setCurrentTimeGraphics
We need to explain the try/catch structures. In at least one browser (Chrome), if the code in the setCurrentTimeValue method is executed before the length of the file is known, then parameter dur is "NaN" and causes an error when the currentTimeMeter is set. The try/catch statements prevented this problem. In addition, temporarily adding printlns to print the Exception e values in the catch sections helped finding bugs during debugging.
The meter and range graphics have additional attributes that can be used. CSS can be used to improve the looks of these graphics but this was not done in keeping with "KIS".
Alternative 1: Getting access to the Processing.js audio object
Alternative 2: Sophisticated onclick commands
We now look at two alternative coding styles that can reduce the amount of coding needed.
While Javascript code cannot look at variable Processing.js variable "audio" directly, there is a simple way to provide access indirectly by means of a "get" function. This method might particularly appealing to Javascript programmers. In the .pde file:
217 // An alternative: 218 // The following method allows Javascript access to the audio object 219 Audio getAudio() { return audio; }
Javascript can now use this method to access the audio object. For example, here is an alternate way to handle the "Play" button. The pPlay method in the .pde file is no longer needed. In the .html file:
121 <input type="button" value="Play" onclick="jsPlay1()"> 122 <script type="text/javascript"> 123 function jsPlay1() { 124 var pjs = Processing.getInstanceById("KISA"); 125 pjs.getAudio().play(); 126 } 127 </script>
The second method can be used to eliminate all the js...() Javascript functions at the cost of somewhat complicating the "onclick' code. It works by essentially compacting the js...() function code into the onclick attribute. For example, the "Pause" button could be handled as follows:
132 <input type="button" value="Pause" 133 onclick="Processing.getInstanceById('KISA').pPause()">
< Previous (KISA 5)
Next (KISA 6) >
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>KISA 5 Form (Keep It Simple Audio)</title> 5 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 6 <script type="text/javascript" src="processing.min.js"></script> 7 <script type="text/javascript"> 8 9 function jsPlay() { 10 var pjs = Processing.getInstanceById("KISA"); 11 pjs.pPlay(); 12 } 13 function jsPause() { 14 var pjs = Processing.getInstanceById("KISA"); 15 pjs.pPause(); 16 } 17 function jsStop() { 18 var pjs = Processing.getInstanceById("KISA"); 19 pjs.pStop(); 20 } 21 function jsVolumeDown() { 22 var pjs = Processing.getInstanceById("KISA"); 23 pjs.pVolumeDown(); 24 } 25 function jsVolumeUp() { 26 var pjs = Processing.getInstanceById("KISA"); 27 pjs.pVolumeUp(); 28 } 29 function jsTimeDown() { 30 var pjs = Processing.getInstanceById("KISA"); 31 pjs.pTimeDown(); 32 } 33 function jsTimeUp() { 34 var pjs = Processing.getInstanceById("KISA"); 35 pjs.pTimeUp(); 36 } 37 function jsLoop() { 38 var pjs = Processing.getInstanceById("KISA"); 39 pjs.pLoop(); 40 } 41 function jsVolumeChange() { 42 var x = document.getElementById("volumeRange").value; 43 var pjs = Processing.getInstanceById("KISA"); 44 pjs.pVolumeChange(x); 45 } 46 function jsTimeChange() { 47 var x = document.getElementById("timeRange").value; 48 var pjs = Processing.getInstanceById("KISA"); 49 pjs.pTimeChange(x); 50 } 51 52 function jsAutoplay() { 53 var pjs = Processing.getInstanceById("KISA"); 54 pjs.pAutoplay(); 55 } 56 function jsChooseFile(i) { 57 var pjs = Processing.getInstanceById("KISA"); 58 pjs.pChooseFile(i); 59 } 60 61 function jsFileButtons(numButtons) { // called by Processing.js 62 var i; 63 var s = ""; 64 for (i = 0; i < numButtons; i++) { 65 var theId = "file" + i; 66 s += '<input id="' + theId + '" type="radio" name="file" value="' + theId + '"' 67 + ' onclick="jsChooseFile(' + i + ')"><span id = "' + theId + 'Name">??</span><br>'; 68 } 69 document.getElementById("putFileButtonsHere").innerHTML = s; 70 } 71 72 </script> 73 </head> 74 <body> 75 <h1>Keep It Simple Audio 5 Form</h1> 76 <audio id="audioTag" class="border" preload="auto" width="420"> 77 Your browser does not support HTML5 audio. 78 </audio> 79 80 <canvas id="KISA" data-processing-sources="KISA5F.pde"> 81 Your browser does not support HTML 5. You may want to update your 82 browser. 83 </canvas> 84 85 <form> 86 <input type="button" value="Play" onclick="jsPlay()"> 87 <input type="button" value="Pause" onclick="jsPause()"> 88 <input type="button" value="Stop" onclick="jsStop()"> 89 <br> 90 <input type="button" value="Volume down" onclick="jsVolumeDown()"> 91 <input type="button" value="Volume up" onclick="jsVolumeUp()"> 92 <br> 93 <input type="button" value="Time down" onclick="jsTimeDown()"> 94 <input type="button" value="Time up" onclick="jsTimeUp()"> 95 <br> 96 <input id="loopBox" type="checkbox" value="Loop" onclick="jsLoop()">Loop 97 <input id="autoplayBox" type="checkbox" value="Autoplay" onclick="jsAutoplay()">Autoplay 98 <br> 99 <div id="putFileButtonsHere"><br>reserved for file buttons and names<br></div> 100 <br> 101 Volume: 102 <meter id="volumeMeter" min="0" max="1" value="1.00" ></meter> 103 <span id="volumeText">100%</span> 104 <br> 105 Current Time: 106 <meter id="currentTimeMeter" min="0" value="0" /></meter> 107 <span id="currentTimeText"> 0.0/1.0 sec</span> 108 <br> 109 Volume: 110 <input id="volumeRange" type="range" value = "100" min = "0" max = "100" 111 onchange="jsVolumeChange()"> 112 <br> 113 Time: 114 <input id="timeRange" type="range" value = "0" min = "0" max = "100" 115 onchange="jsTimeChange()"> 116 <br> 117 <!-- ******************************************************* --> 118 <div style = "background-color: #FFEEEE; width: 500px"> 119 <p> Alternative 1: Getting access to the Processing.js audio object 120 </p> 121 <input type="button" value="Play" onclick="jsPlay1()"> 122 <script type="text/javascript"> 123 function jsPlay1() { 124 var pjs = Processing.getInstanceById("KISA"); 125 pjs.getAudio().play(); 126 } 127 </script> 128 </div> 129 <div style = "background-color: #EEEEFF; width: 500px"> 130 <p>Alternative 2: Sophisticated onClick commands 131 </p> 132 <input type="button" value="Pause" 133 onclick="Processing.getInstanceById('KISA').pPause()"> 134 </div> 135 <!-- ******************************************************* --> 136 137 </form> 138 139 </body> 140 </html>
1 // KISA 2 // Keep It Simple Audio version 5 Form 3 // James Brink brinkje@plu.edu 4 // 3/15/12 4/12/12 1/29/13 5 // The constants can be modified. 6 int width = 500; // Width of canvas 7 int height = 165; // Height of canvas 8 // It is assumed that both .ogg and .mp3 versions of the files are available 9 // in the same folder. The file names must be complete URLs except for the 10 // ending .ogg or .mp3 which must be omitted. It will be added as needed for 11 // the browser. Use the prefix "file:///" or "http:// as appropriate. 12 String[] files = {"http://brinkje.com/KISA_Sounds/groove", 13 "http://brinkje.com/KISA_Sounds/jingle", 14 "http://brinkje.com/KISA_Sounds/marcus_kellis_theme"}; 15 int numFiles = files.length(); 16 int currentFile = 0; 17 18 // Global variables 19 Audio audio = document.getElementById("audioTag"); 20 21 int time; 22 String audioStatus; 23 boolean repeatLoop = false; // Should the player loop? 24 25 Element volumeMeter, volumeValue, currentTimeMeter, currentTimeText, 26 volumeRange, timeRange; 27 28 void setup() { 29 int i, y; 30 // Setup the sketch 31 size(width, height); 32 smooth(); 33 if (audio == null) { 34 noLoop(); 35 return; 36 } 37 frameRate(20); 38 39 audio.addEventListener("loadstart", loadStart, false); 40 audio.addEventListener("playing", playing, false); 41 audio.addEventListener("pause", pause, false); 42 audio.addEventListener("ended", ended, false); 43 audio.addEventListener("error", error, false); 44 45 audioStatus = ""; 46 setSource(files[currentFile]); // pick the initial audio file 47 // set HTML form items 48 document.getElementById('loopBox').checked = audio.loop; 49 document.getElementById('autoplayBox').checked = audio.autoplay; 50 jsFileButtons(numFiles); 51 for (i = 0; i < numFiles; i++) 52 document.getElementById('file'+ i + 'Name').innerHTML = files[i]; 53 document.getElementById('file'+ currentFile).checked = true; 54 55 // get meter and input range elements 56 volumeMeter = document.getElementById("volumeMeter"); 57 volumeValue = document.getElementById("volumeValue"); 58 currentTimeMeter = document.getElementById("currentTimeMeter"); 59 currentTimeText = document.getElementById("currentTimeText"); 60 volumeRange = document.getElementById("volumeRange"); 61 timeRange = document.getElementById("timeRange"); 62 } // setup 63 64 void draw() { 65 // Draws the sketch on the canvas 66 background(#FFFFAA); 67 fill(#000000); 68 if (audio == null) { 69 text("Your browser does not handle the HTML 5 audio tag. You ", 20, 30); 70 text("may want to upgrade your browser to the current version.", 20, 60); 71 return; 72 } 73 74 textAlign(CENTER); 75 text("KISA 5F Using HTML Form Input", width/2, 30); 76 text("Source file: " + audio.src, width/2, 60); 77 text("Status: " + audioStatus, width/2, 80); 78 text("Current time: " + round(audio.currentTime) 79 + " sec. Length: " + round(audio.duration)+ " sec.", width/2, 100); 80 text("Volume (0 to 1): " + round(10 * audio.volume)/10.0, width/2, 120); 81 text("Loop: " + repeatLoop + " Autoplay: " + audio.autoplay, width/2, 145); 82 if (frameCount % 5 == 0) { 83 try { // designed to solve a timing problem - drawn before setup finished 84 setVolumeGraphics(audio.volume); 85 setCurrentTimeGraphics(audio.currentTime, audio.duration); 86 } catch (Exception e) { 87 } 88 } 89 } // draw 90 91 void setSource(String url) { 92 // Called to set the source file. Do not include the file extension 93 // in the url. It will be added here. 94 if (audio.canPlayType && audio.canPlayType("audio/ogg")) { 95 audio.setAttribute("src", url + ".ogg"); 96 } else { 97 audio.setAttribute("src", url + ".mp3"); 98 } 99 audioStatus = "File selected"; 100 } // setSource 101 102 void stop() { 103 // Called stop playing the file by pausing it and setting the 104 // time back to 0; 105 audio.pause(); 106 audio.currentTime = 0; 107 audioStatus = "Stopped"; 108 } // stop 109 110 void loadStart() { 111 // LoadStart event processing 112 audioStatus = "Loading"; 113 } // loadStart 114 115 void playing() { 116 // Playing event processing 117 audioStatus = "Playing"; 118 } // playing 119 120 void pause() { 121 // Pause event processing. 122 // There is no stop event but a "stop" causes a pause. This 123 // method checks to see if the current time = 0. If so it assumes 124 // stopped 125 if (audio.currentTime == 0) 126 audioStatus = "Stopped"; 127 else 128 audioStatus = "Paused"; 129 } // pause 130 131 void ended() { 132 int j; 133 // Ending event processing 134 if (repeatLoop) { 135 currentFile = (currentFile+1) % numFiles; 136 setSource(files[currentFile]); 137 document.getElementById('file'+currentFile).checked = true; 138 audio.play(); 139 } else { 140 audioStatus = "Finished playing"; 141 } 142 } // ended 143 144 void error() { 145 // error event processing 146 audioStatus = "Error"; 147 } // error 148 149 void pPlay() { audio.play(); } 150 void pPause() { audio.pause(); } 151 void pStop() { stop(); } 152 153 void pVolumeDown() { 154 double v = audio.volume - 0.1; 155 audio.volume = constrain(v, 0, 1); 156 } 157 void pVolumeUp() { 158 double v = audio.volume + 0.1; 159 audio.volume = constrain(v, 0, 1); 160 } 161 void pVolumeChange(double v) { 162 audio.volume = v/100; 163 } 164 void pTimeDown() { 165 double t = audio.currentTime - 0.1 * audio.duration; 166 audio.currentTime = constrain(t, 0, audio.duration); 167 } 168 void pTimeUp() { 169 double t = audio.currentTime + 0.1 * audio.duration; 170 audio.currentTime = constrain(t, 0, audio.duration); 171 } 172 173 void pTimeChange(double t) { 174 audio.currentTime = t; 175 } 176 void pLoop() { repeatLoop = !repeatLoop; } 177 void pAutoplay() { audio.autoplay = !audio.autoplay; } 178 179 void pChooseFile(int i) { 180 currentFile = i; 181 setSource(files[currentFile]); 182 } 183 184 void setVolumeGraphics(double vol) { 185 // volumeMeter, volueValue, volumeRange are in .html file 186 try { 187 double v, v100; 188 v = round(100 * vol)/100; 189 v100 = 100 * v; 190 volumeMeter.value = vol; 191 volumeText.innerHTML = v100 + "%"; 192 volumeRange.value = v100; 193 } catch (Exception e) { 194 } 195 } // setVolumeGraphics 196 197 void setCurrentTimeGraphics(double cur, double dur) { 198 // currentTimeMeter and timeRange are in .html file 199 double c, d; 200 try { 201 c = round(10 * cur)/10; 202 d = round(10 * dur)/10; 203 currentTimeMeter.value = c; 204 currentTimeMeter.max = d; 205 currentTimeText.innerHTML = c + "/" + d + " sec"; 206 timeRange.value = c; 207 timeRange.max = d; 208 if (d < 10) 209 timeRange.step = .1; 210 else 211 timeRange.step = 1. 212 } catch (Exception e) { 213 } 214 } // setCurrentTimeGraphics 215 216 217 // An alternative: 218 // The following method allows Javascript access to the audio object 219 Audio getAudio() { return audio; }
Updated 11/6/14