Interactive
Web Development Techniques for Music Learning
Steven G. Estrella, Ph.D.
http://www.stevenestrella.com
steve@stevenestrella.com
|
For the multimedia supplement to this
article, click here: Pocket Guide
The Beatnik plugin (www.beatnik.com) is required. |
One of the most common uses of multimedia applications in music has involved the performance of a sound file accompanied by narrative text and explanatory graphics appearing at designated moments in the music performance. This application was featured prominently as the "Close Reading" feature in all the Voyager CD Companion Series products such as "Beethovens Ninth Symphony," "Mozarts Dissonant Quartet," and "Stravinskys Rite of Spring." Users of these products were able to listen to a music performance while learning about the work through text and illustrations on screen. The "Close Reading" feature was also interactive in that the user could easily skip from one section of the piece to another. In the "Close Reading" the current time on the CD determined what text and graphics would be displayed.
Another feature created in the Voyager CD Companion series was the "Pocket Guide" which was an interactive chart of the major sections of the work being studied. Users could click on any section and the CD would begin playing from that point. The convenience of this section allowed a student studying sonata form, for example, to listen to the second theme of the exposition for a few measures and then quickly hear the same content played in a different key in the recapitulation. The "Pocket Guide" also served as a useful presentation tool in music classrooms.
The purpose of this presentation is to demonstrate a technique for implementing the "Pocket Guide" and "Close Reading" features on the world wide web using a MIDI file as the sound source instead of a CD. The technique uses the Beatnik plugin to allow for control of a MIDI file in real time through JavaScript. Properties of the MIDI file which can be controlled include:
Tempo (users can slow down the music to allow for detailed listening)
Timbre (users can experiment with different timbres)
Volume and Balance
Stereo Pan (users can isolate a track in the left or right speaker)
Reverb (users can mimic the sounds of different spaces)
Chorus (users can experiment with chorus effects)
Start time and end time (users can play the music from any point)
Transposition (users may transpose the piece)
Track muting (users may mute a track to focus on specific content)
This article will demonstrate the following steps for creating a "Pocket Guide" and "Close Reading" for a Mozart Piano Sonata.
To create this exercise, follow these procedures.

The completed project will look like the graphic below. The completed version
can be found online at
http://www.stevenestrella.com/IWP.
(click the picture to run the multimedia supplement)
This article is an extension of the presentation "A Web Toolkit for Music Educators" given at the 2000 TDML. This presentation is posted on the presenters web site at (url). Those attending the presentation were also encouraged to attend the hands-on Javascript session scheduled for Saturday evening of the conference where they were given an opportunity to complete the tutorial and learn the concepts of JavaScript.
The complete code for the pocket guide is printed below and commented.
<HTML><HEAD><TITLE>Embedded Beatnik with Pocket Guide and Close Reading Features</TITLE>
<!--The next two lines allow this page to access the beatnik source code--> <!--These files are free on the beatnik web site.--> <SCRIPT SRC="javascript/music-object.js"></SCRIPT> <SCRIPT SRC="javascript/music-object-prefs.js"></SCRIPT>
<SCRIPT LANGUAGE="JavaScript"> /*First set up global variables for the Music object, tempo, images, and captions.*/ var myMusicObject = new Music (); var thetempo = 120; /*The measure number of the last notation graphic to be displayed.*/ var lastsegment = 71; /*The measure number of the last measure of the piece.*/ var lastmeasure = 73;
/*All the notation graphics will be stored as image objects in an array.*/ var myimage = new Array(); myimage[1] = new Image(); myimage[5] = new Image(); myimage[9] = new Image(); myimage[13] = new Image(); myimage[16] = new Image(); myimage[19] = new Image(); myimage[22] = new Image(); myimage[26] = new Image(); myimage[29] = new Image(); myimage[32] = new Image(); myimage[35] = new Image(); myimage[38] = new Image(); myimage[42] = new Image(); myimage[46] = new Image(); myimage[49] = new Image(); myimage[52] = new Image(); myimage[55] = new Image(); myimage[58] = new Image(); myimage[61] = new Image(); myimage[64] = new Image(); myimage[67] = new Image(); myimage[71] = new Image();
/*Sets the src property of each of the images. Fill these in with valid paths to graphic files.*/ myimage[1].src = "images/K545_mm01-4.gif"; myimage[5].src = "images/K545_mm05-8.gif"; myimage[9].src = "images/K545_mm09-12.gif"; myimage[13].src = "images/K545_mm13-15.gif"; myimage[16].src = "images/K545_mm16-18.gif"; myimage[19].src = "images/K545_mm19-21.gif"; myimage[22].src = "images/K545_mm22-25.gif"; myimage[26].src = "images/K545_mm26-28.gif"; myimage[29].src = "images/K545_mm29-31.gif"; myimage[32].src = "images/K545_mm32-34.gif"; myimage[35].src = "images/K545_mm35-37.gif"; myimage[38].src = "images/K545_mm38-41.gif"; myimage[42].src = "images/K545_mm42-45.gif"; myimage[46].src = "images/K545_mm46-48.gif"; myimage[49].src = "images/K545_mm49-51.gif"; myimage[52].src = "images/K545_mm52-54.gif"; myimage[55].src = "images/K545_mm55-57.gif"; myimage[58].src = "images/K545_mm58-60.gif"; myimage[61].src = "images/K545_mm61-63.gif"; myimage[64].src = "images/K545_mm64-66.gif"; myimage[67].src = "images/K545_mm67-70.gif"; myimage[71].src = "images/K545_mm71-73.gif";
/*This array holds your comments to go with each notation graphic.*/ var mycomments = new Array(); mycomments[1] = "Exposition: The principal theme is a typical classical antecedent-consequent phrase."; mycomments[5] = "An episode follows of contrasting rhythmic character."; mycomments[9] = "The ii chord leads to a V/V and resolves on V."; mycomments[13] = "Exposition: The secondary theme opens with a decorative trill."; mycomments[16] = "The second theme, like the first, is mostly arpeggios."; mycomments[19] = "The second theme also has a contrasting episode."; mycomments[22] = "A modulation to dominant is accomplished."; mycomments[26] = "A short restatement of the chord progression solidifies the modulation."; mycomments[29] = "The development begins in G minor, the parallel minor of the dominant key."; mycomments[32] = "Sequences are used to move the key to the subdominant. Gm, A7, Dm."; mycomments[35] = "Dm, E7, Am."; mycomments[38] = "G7-C, modulation to V of IV"; mycomments[42] = "Recapitulation: Principal theme, now in the subdominant key."; mycomments[46] = "An episode follows of contrasting rhythmic character."; mycomments[49] = "The episode is extended to modulate back to tonic."; mycomments[52] = "The placement of the motive is changed to the left hand for variety."; mycomments[55] = "V of V moving to a half cadence on V once again."; mycomments[58] = "Recapitulation: The secondary theme is restated in the tonic."; mycomments[61] = "Contrasting episode"; mycomments[64] = "Extension to effect the resolution to tonic."; mycomments[67] = "I6/4 to V7 to I."; mycomments[71] = "German composers always move from Dominant Teutonic.";
/*This function determines the duration of each measure in milliseconds.*/
function calculateTempo(){
themeasureduration = (4/(thetempo/60))*1000;
return themeasureduration;
}
/*This function finds the appropriate graphic to display and also displays the caption.*/
function goMeasure(measurenumber){
if (document.images){
if (myimage[measurenumber]){
document.controlform.notation.src =
myimage[measurenumber].src;
document.controlform.commentary.value =
mycomments[measurenumber];
}else{
for (i=1;i<4;i++){
if (myimage[measurenumber-i]){
document.controlform.notation.src = myimage[measurenumber-i].src;
}
}
}
}
}
/*This function resets the tempo and begins to play a specific measure.*/
function goToMeasure(measurenumber){
playMIDI();
myMusicObject.setPosition(1);
stopMIDI();
var tempomenu = window.document.controlform.tempo;
tempomenu.selectedIndex = 2;
setthetempo();
measurelength = calculateTempo();
timestamp = measurenumber * measurelength;
playMIDI();
myMusicObject.setPosition(timestamp-measurelength);
}
/*This function sends program change messages to the various tracks.*/
function settimbres(){
var mpatchmenu = window.document.controlform.keyboardpatch;
var mpatchmenuItemNumber = mpatchmenu.selectedIndex;
var mpatch = parseFloat(mpatchmenu.options[mpatchmenuItemNumber].value);
myMusicObject.setProgram(2,mpatch);
myMusicObject.setProgram(3,mpatch);
myMusicObject.setProgram(4,mpatch);
myMusicObject.setProgram(5,mpatch);
myMusicObject.setProgram(6,mpatch);
setcontrollers();
}
/*This function begins playing the file and sends the program changes after a 100 millisecond delay. It takes a fraction of a second after playback begins before the beatnik player can accept midi messages.*/
function playMIDI(){
myMusicObject.play ();
timerID = setTimeout("settimbres()",100);
}
/*This function stops playing the file with a fade out specified in milliseconds.*/
function stopMIDI(fadevalue){
myMusicObject.stop (fadevalue);
}
/*This function sets reverb and chorus effects. You will fill in the MIDI channel numbers and controller values. This piece uses channels 2-6.*/
function setcontrollers(){
myMusicObject.setController(2,91,120); myMusicObject.setController(3,91,120); myMusicObject.setController(4,91,120); myMusicObject.setController(5,91,120); myMusicObject.setController(6,91,120); myMusicObject.setController(2,93,10); myMusicObject.setController(3,93,10); myMusicObject.setController(4,93,10); myMusicObject.setController(5,93,10); myMusicObject.setController(6,93,10); }
/*This function gets the value from the tempo popup menu and sets the tempo.*/
function setthetempo(){
var tempomenu = window.document.controlform.tempo;
var tempomenuItemNumber = tempomenu.selectedIndex;
thetempo = parseFloat(tempomenu.options[tempomenuItemNumber].value);
myMusicObject.setTempo(thetempo);
}
/*This function stops playback and sets the tempo for the next playback.*/
function changethetempo(){
stopMIDI();
setthetempo();
}
// Clock script here --
var timerID = null;
var timerRunning = false;
var theseconds = 0;
var thepause = false;
function stopclock(){
if(timerRunning){
clearTimeout(timerID);
timerRunning = false;
document.controlform.clockface.value = theseconds;
}
}
function resetandstopclock(){
stopclock();
}
function resetandstartclock(){
if (thepause == "false"){
theseconds = 0;
}
startclock();
thepause == "false";
}
function pauseclock(){
stopclock();
thepause == "true";
}
function startclock(){
stopclock();
showtime();
}
function showtime(){
theseconds = myMusicObject.getPosition();
document.controlform.clockface.value = theseconds;
timerID = setTimeout("showtime()",100);
timerRunning = true;
setthetempo();
setcontrollers();
measurelength = calculateTempo();
theMeasureTarget = Math.floor(theseconds/measurelength)+1;
goMeasure(theMeasureTarget);
if (theseconds > measurelength*lastsegment) {
goMeasure(lastsegment);
}
if (theseconds < measurelength*(lastmeasure+1)){
measurecalc = Math.floor(theseconds/measurelength) + 1;
}else{
measurecalc = lastmeasure;
}
document.controlform.measurestatus.value = measurecalc;
}
</SCRIPT>
<LINK REL="stylesheet" HREF="../iwpstyle.css"> </HEAD> <BODY BGCOLOR="WHITE"> <P ALIGN="LEFT"> </P>
<DIV ALIGN="CENTER"> <H1 ALIGN="CENTER">Pocket Guide and Close Reading Technique</H1> <H2 ALIGN="CENTER">with an Embedded Beatnik MIDI File</H2>
<FORM NAME="controlform"> <TABLE BORDER=1 CELLSPACING="1" CELLPADDING="2" BGCOLOR="aqua" width=560> <TR> <TD align="center"> <DIV ALIGN="CENTER"> <H3>Set Tempo then Play</H3> <SELECT NAME="tempo" onChange="changethetempo();"> <OPTION VALUE="80">80 BPM</OPTION> <OPTION VALUE="100">100 BPM</OPTION> <OPTION VALUE="120" SELECTED>120 BPM</OPTION> <OPTION VALUE="140" >140 BPM</OPTION> <OPTION VALUE="160">160 BPM</OPTION> <OPTION VALUE="180">180 BPM</OPTION> <OPTION VALUE="200">200 BPM</OPTION> </SELECT>
</DIV> </TD> <TD align="CENTER"> <INPUT TYPE=button VALUE="PLAY" onClick="playMIDI();"><BR> <INPUT TYPE=button VALUE="PAUSE" onClick="myMusicObject.pause ();"><BR> <INPUT TYPE=button VALUE="STOP" onClick="stopMIDI(2000);"> </TD> <TD> <DIV ALIGN="CENTER"> <H3>Set Timbre</H3> <SELECT NAME="keyboardpatch" onChange="settimbres();"> <OPTION VALUE="0" SELECTED>Piano</OPTION> <OPTION VALUE="6">Harpsichord</OPTION> <OPTION VALUE="11">Vibraphone</OPTION> <OPTION VALUE="82">Calliope</OPTION> <OPTION VALUE="88">New Age</OPTION> <OPTION VALUE="21">Accordion</OPTION> <OPTION VALUE="4">E. Piano</OPTION> <OPTION VALUE="24">Guitar</OPTION> <OPTION VALUE="26">Jazz Guitar</OPTION> </SELECT> </DIV> </TD> </TR> </TABLE>
<P> </P> <TABLE BORDER=1 CELLSPACING="1" CELLPADDING="2" BGCOLOR="aqua"> <TR> <TD align="CENTER"><INPUT TYPE=button VALUE="Expo 1st Theme - Tonic" onClick="goToMeasure(1);"></TD> <TD align="CENTER"><INPUT TYPE=button VALUE="Episode" onClick="goToMeasure(5);"></TD> <TD align="CENTER"><INPUT TYPE=button VALUE="Expo 2nd Theme - Dominant" onClick="goToMeasure(13);"></TD> <TD align="CENTER"><INPUT TYPE=button VALUE="Episode" onClick="goToMeasure(18);"></TD> </TR> <TR>
<TD align="CENTER" colspan=4><INPUT TYPE=button VALUE="<---------------Development-------------->" onClick="goToMeasure(29);"></TD>
</TR> <TR> <TD align="CENTER"><INPUT TYPE=button VALUE="Recap 1st Theme - SubDominant" onClick="goToMeasure(42);"></TD> <TD align="CENTER"><INPUT TYPE=button VALUE="Episode" onClick="goToMeasure(46);"></TD> <TD align="CENTER"><INPUT TYPE=button VALUE="Recap 2nd Theme - Tonic" onClick="goToMeasure(58);"></TD> <TD align="CENTER"><INPUT TYPE=button VALUE="Episode" onClick="goToMeasure(63);"></TD> </TR>
<TR> <TD colspan=4> <IMG SRC="images/K545_mm01-4.gif" ALT="" WIDTH="555" HEIGHT="190" NAME="notation"> </TD> </TR> <TR> <TD> <DIV ALIGN="CENTER"> <H4>Measure <input NAME="measurestatus" SIZE="5"><BR></H4> <H4>Time <input NAME="clockface" SIZE="7"></H4> </DIV> </TD> <TD colspan=3><DIV ALIGN="CENTER"> <H4>Commentary</H4> <TEXTAREA NAME="commentary" cols=40 rows=4></TEXTAREA> </DIV> </TD> </TR> </TABLE> <P> </P> </FORM>
<HR> </DIV>
<DIV ALIGN="CENTER"> <P ALIGN="center"><img src="images/folder.gif" width="244" height="252"></P> <SCRIPT LANGUAGE=JavaScript> <!-- //
myMusicObject.magicEmbed ( 'SRC="sounds/mozartK545_CMA.mid" ' + 'TYPE="audio/rmf" ' + 'WIDTH=144 ' + 'HEIGHT=15 ' + 'DISPLAY=SYSTEM ' + 'AUTOSTART=FALSE ' + 'LOOP=FALSE' );
with (myMusicObject) {
onStop ('resetandstopclock ()');
onPlay ('resetandstartclock ()');
onPause ('pauseclock ()');
}
// --> </SCRIPT> </DIV> </BODY> </HTML>