Laboratory 10
Laboratory 10
Xylophone
Getting Started
Connect to the App Inventor website and start a new project. Name it “Xylophone”,
and also set the screen’s title to “Xylophone”. Connect your app to your device or
emulator.
HorizontalArrange Layout HorizontalArrange Place the Play and Reset buttons next to each other.
ment ment1
1. From the User Interface category, drag a Button onto the screen. Leave its name
as Button1. We want it to be a long magenta bar, like that on a xylophone, so set
its properties as follows:
2. Repeat for a second Button, named Button2, placing it below Button1. Use the
same Width and Height property values, but set its BackgroundColor property to
Red and its Text property to “D”.
The display on your phone should look similar, although there will not be any empty
space between the two colored buttons.
134 Chapter 9: Xylophone
We could do the same for Button2, as shown in Figure 9-4 (just changing the text
value), but the code would be awfully repetitive.
Repeated code is a good sign that you should create a procedure, which you’ve
already done in the MoleMash game in Chapter 3 and the Ladybug Chase game in
Chapter 5. Specifically, we’ll create a procedure that takes a number as a parameter,
sets Sound1’s Source to the appropriate file, and plays the sound. This is another exam-
ple of refactoring—improving a program’s implementation without changing its
behavior, a concept introduced in the MoleMash tutorial. We can use the Text draw-
er’s join block to combine the number (e.g., 1) and the text “.wav” to create the
proper filename (e.g., “1.wav”). Here are the steps for creating the procedure we need:
1. Under the Built-In tab, go to the Procedures drawer and drag out the to proce
dure do block. (Unless otherwise specified, you should choose the version with
“do”, not “result”.)
2. Add the parameter by clicking on the little blue icon on the to procedure do
block, dragging over an input, and changing its name from “x” to “number”. You
might want to review Figure 5-6 from Chapter 5.
3. Click the name of the procedure, which by default is “procedure” and set it to
“PlayNote”.
4. Drag the Sound1.Source block from Button1.Click into PlayNote to the right of
the word “do”. Move the Sound1.Play block into PlayNote as well.
5. Drag the 1.wav block into the trash can.
6. From the Text drawer, drag the join block into Sound1.Source’s socket.
7. Type “number” and move it to the top socket of the join block (if it is not already
there).
8. From the Text drawer, drag the text block into the second socket of the join
block.
9. Change the text value to “.wav”. (Remember not to type the quotation marks.)
10. From the Procedures drawer, drag out a call PlayNote block and place into the
empty body of Button1.Click.
11. Type “1” and put it in the “number” socket.
Now, when Button1 is clicked, the procedure PlayNote will be called, with its number
parameter having the value 1. It should set Sound1.Source to “1.wav” and play the
sound.
Create a similar Button2.Click block with a call to PlayNote with a parameter of 2.
(You can copy the existing call PlayNote block and move it into the body of But
ton2.Click, making sure to change the parameter.) Your program should look like
Figure 9-5.
136 Chapter 9: Xylophone
Test your app Touch the buttons and check if the notes play without
delay. (If you don’t hear anything, make sure that the media volume
on your phone is not set to mute.)
• http://appinventor.org/bookFiles/Xylophone/3.wav
• http://appinventor.org/bookFiles/Xylophone/4.wav
• http://appinventor.org/bookFiles/Xylophone/5.wav
Creating the Keyboard 137
• http://appinventor.org/bookFiles/Xylophone/6.wav
• http://appinventor.org/bookFiles/Xylophone/7.wav
• http://appinventor.org/bookFiles/Xylophone/8.wav
Then, create six new buttons, following the same steps as you did for the previous
two but setting their Text and BackgroundColor properties as follows:
You might also want to change Button8’s TextColor property to White, as shown in
Figure 9-7, so it is more legible.
Figure 9-7. Putting the remaining buttons and sounds in the Component Designer
Back in the Blocks Editor, create Click blocks for each of the new buttons with appro-
priate calls to PlayNote. Similarly, add each new sound file to Screen.Initialize, as
shown in Figure 9-8.
138 Chapter 9: Xylophone
Figure 9-8. Programming the button click events to correspond to all the keyboard keys
Test your app You should now have all the buttons, and each one
will play a different note when you click it.
• notes, which will contain the names of the sound files in the order in which they
were played.
• times, which will record the points in time at which the notes were played.
Recording and Playing Back Notes 139
Note Before continuing, you might want to review lists, which are
covered in the Presidents Quiz in Chapter 8 and in Chapter 19.
We can get the timing information from a Clock component, which we will also use to
properly time the notes for playback.
1. From the Sensors drawer, drag in a Clock component. It will appear in the “Non-
visible components” section. Uncheck its TimerEnabled property because we
don’t want its timer to go off until we tell it to during playback.
2. Go to the Layout drawer and drag a HorizontalArrangement component beneath
the existing button. Set its Width property to “Fill parent.”
3. From the User Interface drawer, drag in a Button. Rename it "PlayButton" and set
its Text property to “Play”.
4. Drag in another Button, placing it to the right of PlayButton. Rename the new
Button "ResetButton" and set its Text property to “Reset”.
Figure 9-9. Adding components for recording and playing back sounds
140 Chapter 9: Xylophone
1. Create a new variable by going to the Variables drawer and dragging out an
initialize global to block from the Definition drawer.
2. Change the name of the variable to “notes”.
3. Open the Lists drawer and drag a create empty list block out, placing it in the
socket of the initialize global to block.
This defines a new variable named “notes” to be an empty list. Repeat the steps for
another variable, which you should name “times”. These new blocks should look like
those in Figure 9-10.
Figure 9-10. Initialize two variables to store the notes and the timing information
For example, if you play “Row, Row, Row Your Boat” [C C C D E], your lists would end
up having five entries, which might appear as follows:
When the user presses the Reset button, we want the two lists to go back to their
original, empty states. Because the user won’t see any change, it’s nice to add a small
Sound1.Vibrate block to indicate that the key click was registered. Figure 9-12 shows
the blocks for this behavior.
Figure 9-12. Providing feedback when the user resets the app
Although the recursion is correct, there is a different problem with the preceding
example: almost no time passes between one call to Sound1.Play and the next, so
each note is interrupted by the next note, except for the last one. No note (except for
the last) is allowed to complete before Sound1’s source is changed and Sound1.Play is
called again. To achieve the correct behavior, we need to implement a delay between
calls to PlayBackNote.
Nothing else happens for 1 second, at which time Clock1.Timer runs, temporarily
disabling the timer and calling PlayBackNote.
2. The second time PlayBackNote is called, count = 2:
Nothing else happens for 3 seconds, at which time Clock1.Timer runs, temporar-
ily disabling the timer and calling PlayBackNote.
3. The third time PlayBackNote is called, count = 3:
Variations
Here are some alternative scenarios to explore:
• Currently, there’s nothing to stop a user from clicking ResetButton during play-
back, which will cause the program to crash. (Can you figure out why?) Modify
PlayButton.Click so it disables ResetButton. To re-enable it when the song is
complete, change the if block in PlayButton.Click into an if else block, and
re-enable ResetButton in the else portion.
• Similarly, the user can currently click PlayButton while a song is already playing.
(Can you figure out what will happen?) Make it so PlayButton.Click disables
PlayButton and changes its text to “Playing...” You can re-enable it and reset the
text in an ifelse block, as described in the previous bullet.
146 Chapter 9: Xylophone
• Add a button with the name of a song, such as “Für Elise”. If the user clicks it, pop-
ulate the notes and times lists with the corresponding values, set count to 1, and
call PlayBackNote. To set the appropriate times, you’ll find the Clock1.MakeIn
stantFromMillis block useful.
• If the user presses a note, goes away and does something else, and then comes
back hours later and presses an additional note, the notes will be part of the same
song, which is probably not what the user intended. Improve the program by 1)
stopping recording after some reasonable interval of time, such as a minute; or, 2)
putting a limit on the amount of time used for Clock1.TimerInterval by using
the max block from the Math drawer.
• Visually indicate which note is playing by changing the appearance of the button
—for example, by changing its Text, BackgroundColor, or ForegroundColor.
Summary
Here are some of the ideas we covered in this tutorial:
• You can play different audio files from a single Sound component by changing its
Source property. This enabled us to have one Sound component instead of eight.
Just be sure to load the sounds at initialization to prevent delays (Figure 9-6).
• Lists can provide a program with memory, with a record of user actions stored in
the list and later retrieved and reprocessed. We used this functionality to record
and play back a song.
• You can use the Clock component to determine the current time. Subtracting
two time values gives us the amount of time between two events.
• You can set the TimerInterval property for Clock within the program, such as
how we set it to the duration of time between the starts of two notes.
• It is not only possible but sometimes desirable for a procedure to make a call to
itself. This is a powerful technique called recursion. When writing a recursive pro-
cedure, make sure that there is a base case in which the procedure ends, rather
than calling itself, or the program will loop infinitely.