Chapter 5 – Making Music: An On-Screen Piano (Part 1 – Using Loops)
topics: sound concepts: abstraction, loops, arrays, OO structure Start on a new scenario: a piano that can play with our computer Opening a scenario from the book scenarios: piano-1. Need (the images and the sound files)
Creating a Music Simulation:
The Piano Scenario: piano1 On the world classes, Right click on the World and add wood.jpg as your background. Then compile.
Animating the Key
The Piano World Code Click on the Open editor Take a look at the Piano Specifies the size and resolution of the World Square (800,340, 1)
Open editor on the Key actor. The Key Code Open editor on the Key actor. The Code is only Stubs
Creating Multiple Keys Hold the shift key on the key Actor and bring in a few keys
Methods that do nothing The object of class Key and placing it into the world that we see a simple white key, and it does nothing anything when we run the scenario import greenfoot.*; // (World, Actor, GreenfootImage, and Greenfoot) public class Key extends Actor { /* * Create a new key. */ public Key() } * Do the action for this key. public void act()
Chapter05 under book-scenarios-piano1-there are images folder
Animate the piano key white-key.png and white-key-down.png Animate the piano key: When we press a piano key on the keyboard, change color The scenario as it is already contains two image files white-key.png and white-key-down.png (which it shows two states) black-key.png and blackkey-down.png (which we shall use later for the black keys.)
Act method: changing image Note: Both “g” and “white-key-down.png” are parameters of type String. A String is simply a sequence of chars. The sequence can be of any length, including 1.
Animating the Keys
Improving the Code private boolean isDown; We add a boolean field to our class to remember whether the key is currently down or not. We call this field isDown, and its declaration looks as follows: private boolean isDown; We will store true in this field while the piano key is down, and false while it isn’t.
Handling Repetition Issues There is a repetition problem because the act() method is called repeatedly while we hold down the "g" key. To fix this problem we need a variable to tell us if we already reacted to the key being down, so we do not play the note more than once: private boolean isDown = false; Once the input has been handled, we set isDown is true. If the key is released we set isDown to false, so the next time the key is pressed we can react again. How can we now use this variable?
Comparison Operators < less than <= less than or equal == equal ex: if (a < b) { <= less than or equal ex: if (a <= b) { == equal ex: if (a == b) { != not equal ex: if (a != b) { >= greater than or equal ex: if (a >= b) { > greater than ex: if (a > b) { (all are used with numerical values)
Logical Operators && AND || OR ! NOT Ex: if (a < b && b < c) { Note: both a < b and b < c must be true || OR Ex: if (a < b || b < c) { Note: either a < b or b < c must be true ! NOT Ex: if ( ! a ) { Note: if a is false, then !a is true, if a is true, then !a is false (all are used with boolean values)
Logical Operators (Truth Table) The logical NOT is a unary operator, meaning it is applied to one boolean value, not two. This changes its Truth Table. Inputs Results NOT a !a true false
Add Boolean field
This isDown boolean logic: public void act () { if ( !isDown && Greenfoot.isKeyDown("g")) setImage("white-key-down.png"); play(); isDown = true; } if ( isDown && !Greenfoot.isKeyDown("g")) setImage("white-key.png"); isDown = false;
Logical Operations AND and NOT if (isDown is set to false AND “g” is down ) if ( !isDown && Greenfoot.isKeyDown("g")) { setImage("white-key-down.png"); play(); isDown = true; } if ( isDown && !Greenfoot.isKeyDown("g")) setImage("white-key.png"); isDown = false; if ( isDown is set to true AND “g” is not down)
Animation + Sound Now the Key is animated and plays a note. But there is a Repetition Problem
Producing the Sound The sounds folder has a collection of sound files each of which contains the sounds for a single piano key with an octave number followed by the note name for the file’s name.
Producing the Sound /** * Play the note of this key. */ public void play() { Greenfoot.playSound("3a.wav"); }
Producing the Sound Right Click on the Object Click play
Producing the Sound The Keys all play the same note!
Abstraction: Coding Classes, not Objects The current iteration of our Key class ultimately only implements ONE specific Key object (a Key that plays the “3a.wav” file and reacts to the “g” key). What we actually want is a “generic” Key class that can be used to implement any Key object. Abstraction (for our purposes): Solving an entire range of problems, instead of one specific problem. This requires the use of Constructors with Parameters
Using Parameters in Constructors public class Key extends Actor { private boolean isDown = false; private String key; private String sound; /** * Create a new key linked to a given keyboard key, and * with a given sound. */ public Key(String keyName, String soundFile) key = keyName; sound = soundFile; } // methods omitted. Two new Instance Variables of type String A Constructor that receives and handles Parameters
The new act() method Change the “g” to key Change the “3a.wav” to sound
The new play() method /** * Play the note of this key. */ public void play() { Greenfoot.playSound(sound); }
The new act() method public void act() { if ( !isDown && Greenfoot.isKeyDown(key)) setImage("white-key-down.png"); isDown = true; } if ( isDown && !Greenfoot.isKeyDown(key)) setImage("white-key.png"); isDown = false;
Creating two different Keys: the sounds Folder We Will Use Sound Files 3a.wav and 3b.wav
Abstraction: Creating multiple keys
Creating two different Keys Right Click Key Select new Key Add the First Key
Creating two different Keys Add the Second Key
Creating two different Keys We now Have a Piano with Two Keys
Building the piano: Using addObject() Reminder: the expression new Key( "a", "3a.wav" ) creates a new Key object with a specific key and a sound file. We can use this for the addObject() method: addObject( new Key( "a", "3a.wav" ), 300, 180 );
Using the Piano Constructor import greenfoot.*; // (World, Actor, GreenfootImage, and Greenfoot) /** * A piano that can be played with the computer keyboard. * * @author: M. Kolling * @version: 1.0 */ public class Piano extends World { * Make the piano. public Piano() super(800, 340, 1); addObject( new Key( "a", "3a.wav" ), 300, 180 ); } Call addObject to Create a Key
Using the Piano Constructor The image is 280*63 pixels. The (x, y) parameters given to the addObject() method refer to the MIDDLE of the image. If we want the image to be align with the upper border, we have to place it at (x, 140)
Placing keys next to each other import greenfoot.*; // (World, Actor, GreenfootImage, and Greenfoot) public class Piano extends World { /** * Make the piano. */ public Piano() super(800, 340, 1); addObject( new Key( "g", "3g.wav" ), 300, 140 ); addObject( new Key( "f", "3f.wav" ), 237, 140 ); }
A Method for Making the keys public class Piano extends World { /** * Make the piano. */ public Piano() super(800, 340, 1); makeKeys(); } * Create the Piano Keys. public void makeKeys() addObject (new Key ("g", "3g.wav"), 300, 140); addObject (new Key ("f", "3f.wav"), 237, 140);
Handling Repetition: The while-loop while ( condition ) { statement1; statement2; statement3; } The while-loop in words: Execute statement1, statement2 and statement3 as long as the condition evaluates to true.
A while-loop using a counter Counter variable is initialized int i = 0; while ( i < 5) { statement1; statement2; . . . i = i + 1; } Counter variables is used as a condition Counter variable is incremented How often are the statements inside the loop executed? Or: How often does the loop iterate?
Another while-loop int a = 20; while ( a => 10) { statement1; . . . a = a - 1; } How often does the loop iterate?
Another while-loop int a = 3; while ( a < 31) { statement1; . . . a = a; } How often does the loop iterate?
Another while-loop int i = 0; while ( i > 30 && i < 100 ) { statement1; statement2; . . . i = i + 1; } How often does the loop iterate?
Handling Repetition: The for-loop Counter variable is incremented (happens AFTER an iteration) Counter variable is initialized Counter variables is used as a condition for ( int i = 0; i < 100; i++/i = i + 1) { statement1; statement2; . . . }
Comparison: for- and while-loops Counter variable is initialized int i = 0; while ( i < 100 ) { statement1; statement2; . . . i = i + 1; } for ( int i = 0; i < 100; i++) { statement1; statement2; . . . }
Counter variables is used as a condition Comparison: for- and while-loops Counter variables is used as a condition int i = 0; while ( i < 100 ) { statement1; statement2 . . . i = i + 1; } for ( int i = 0; i < 100; i++) { statement1; statement2; . . . }
Comparison: for- and while-loops Counter variable is incremented int i = 0; while ( i < 100 ) { statement1; statement2; . . . i = i + 1; } for ( int i = 0; i < 100; i++) { statement1; statement2; . . . }
Creating Multiple keys using a while-loop /** * Create the Piano Keys */ public void makeKeys() { int i = 0; while ( i < 12 ) addObject (new Key ("g", "3g.wav"), 300, 140); i = i + 1; } Note how the Counter variable is created INSIDE the method. It is a “local” variable.
Creating Multiple keys using a for-loop /** * Create the Piano Keys */ public void makeKeys() { for (int i = 0; i<12; i++) { addObject (new Key ("g", "3g.wav"), 300, 140); } Object to be placed y-coord x-coord
Creating Multiple keys using a while-loop It Appears That There is Only One Key
Creating Multiple keys using a while-loop But the keys are actually stacked. They are all crated in the same position!
Creating Multiple keys using a for-loop The x-position has to increase by 63 with every iteration. This is due to the image being 63 pixels wide. /** * Create the Piano Keys */ public void makeKeys() { for (int i = 0; i<12; i++) { addObject (new Key ("g", "3g.wav"), i*63, 140); }
Creating Multiple keys using a for-loop These keys are not centered
Creating Multiple keys using a for-loop (0, 0) Y X (800, 0) Each Key is 280 * 63 pixels. The world is 800 pixels wide. When placing 12 keys, we have 44 pixels of “wiggle room” (22 to each side). This means that, for ideal placement, the first key should be placed at (31.5 + 22, 140) (0, 340)
Creating Multiple keys using a for-loop(with offset) for (int i = 0; i<12; i++) { addObject (new Key ("g", "3g.wav"), i*63 + 54, 140); } This value will move every key slightly to the right The for-loop Will Execute 12 Times. The y-position will remain fixed. The x-position are dependent on the value of i (0, 1, . . ., 11) plus an offset value of 54.
Creating Multiple keys using a for-loop(with offset) /** * Create the Piano Keys */ public void makeKeys() { for (int i = 0; i<12; i++) { addObject (new Key ("g", "3g.wav"), i*63 + 54, 140); }
Creating Multiple keys using a for-loop(with offset)
Advanced: Generic Centering with with calculated (x, y)-coord and offSet public void makeKeys() { int keyWidth, keyHeight, worldWidth; int offSet; Key key = new Key(" ", " "); keyWidth = key.getImage().getWidth(); keyHeight = key.getImage().getHeight(); worldWidth = this.getWidth(); offSet = (worldWidth - keyWidth*12) / 2 + keyWidth/2; for (int i=0; i<12; i++) { addObject (new Key ("g", "3g.wav"), keyWidth*i + offSet, keyHeight / 2); }
Advanced: Generic Centering with with calculated (x, y)-coord and offSet
Arrays