Home > Articles > Programming > Java

  • Print
  • + Share This
This chapter is from the book

This chapter is from the book

2.7 Source Code for Project GuitarTuner

Listing 2.1 and Listing 2.2 show the code for class GuitarString in two parts. Listing 2.1 includes the class declarations, functions, class-level variables, and properties for class GuitarString. Note that several variables are declared public-init. This JavaFX keyword means that users of the class can provide initial values with object literals, but otherwise these properties are read-only. The default accessibility for all variables is script-private, making the remaining declarations private.

Use def for read-only variables and var for modifiable variables. The GuitarString class also provides utility functions that play a note (playNote) or stop playing a note (stopNote). Along with the sound, guitar strings vibrate on and off with vibrateOn and vibrateOff. These functions implement the behavior of the GuitarString class.

Listing 2.1 Class GuitarString—Properties, Variables, and Functions

package guitartuner;

import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.scene.Cursor;
import javafx.scene.CustomNode;
import javafx.scene.Group;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.Node;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import noteplayer.SingleNote;

public class GuitarString extends CustomNode {

    // read-only variables
    def stringColor = Color.WHITESMOKE;
    // "Strings" are oriented sideways, so stringLength is the
    // Rectangle width and stringSize is the Rectangle height
    def stringLength = 300;
    def stringSize = 1;
    def stringMouseSize = 15;
    def timeline = Timeline {
        repeatCount: Timeline.INDEFINITE
        autoReverse: true
        keyFrames: [
            at (0s) { vibrateH => 1.0 }
            at (.01s) { vibrateH => 3.0 tween Interpolator.LINEAR }
            at (0s) { vibrateY => 5.0 }
            at (.01s) { vibrateY => 4.0 tween Interpolator.LINEAR }
        ]
    };

    // properties to be initialized
    public-init var synthNote: SingleNote;
    public-init var note: Integer;
    public-init var yOffset: Number;
    public-init var noteText: String;
    
    // class variables
    var vibrateH: Number;
    var vibrateY: Number;
    var play: Rectangle;
    function vibrateOn(): Void {
        play.visible = true; 
        timeline.play();
    }
    function vibrateOff(): Void {
        play.visible = false;
        timeline.stop();
    }
    function playNote(): Void {
        synthNote.noteOn(note);
        vibrateOn();
    }
    function stopNote(): Void {
        synthNote.noteOff(note);
        vibrateOff();
    }

Listing 2.2 shows the second part of the code for the GuitarString class.

Every class that extends CustomNode must define a function create that returns a Node object.[3] Often the node you return will be a Group, since Group is the most general Node type and can include subnodes. But, you can return other Node types, such as Rectangle (Shape) or HBox (horizontal box) layout node.

The scene graph for GuitarString is interesting because it actually consists of three Rectangle nodes and a Text node. The first Rectangle, used to detect mouse clicks, is completely translucent (its opacity is 0). This Rectangle is wider than the guitar string so the user can more easily select it with the mouse. Several properties implement its behavior: property cursor lets a user know the string is selected and property onMouseClicked provides the event handling code (play the note and vibrate the string).

The second Rectangle node defines the visible string. The third Rectangle node (assigned to variable play) “vibrates” by both moving and changing its height. This rectangle is only visible when a note is playing and provides the vibration effect of “plucking” a string. The movement and change in height are achieved with animation and binding. The Text node simply displays the letter (E, A, D, etc.) associated with the guitar string’s note.

Listing 2.2 Scene Graph for GuitarString

    protected override function create(): Node {
        return Group {
            content: [
                // Rectangle to detect mouse events for string plucking
                Rectangle {
                    x: 0
                    y: yOffset
                    width: stringLength
                    height: stringMouseSize
                    // Rectangle has to be "visible" or scene graph will
                    // ignore mouse events. Therefore, we make it fully
                    // translucent (opacity=0) so it is effectively invisible
                    fill: Color.web("#FFFFF", 0)   // translucent
                    cursor: Cursor.HAND
                    onMouseClicked: function(evt: MouseEvent): Void {
                        if (evt.button == MouseButton.PRIMARY){
                            Timeline {
                                keyFrames: [
                                    KeyFrame {
                                        time: 0s
                                        action: playNote
                                    }
                                    KeyFrame {
                                        time: 2.8s
                                        action: stopNote
                                    }
                                ]  // keyFrames

                            }.play();  // start Timeline
                        } // if
                    }
                }   // Rectangle
                // Rectangle to render the guitar string
                Rectangle {
                    x: 0.0
                    y: 5 + yOffset
                    width: stringLength
                    height: stringSize
                    fill: stringColor
                }
                // Special "string" that vibrates by changing its height
                // and location
                play = Rectangle {
                    x: 0.0
                    y: yOffset
                    width: stringLength
                    height: bind vibrateH
                    fill: stringColor
                    visible: false
                    translateY: bind vibrateY
                }
                Text {      // Display guitar string note name
                    x: stringLength + 8
                    y: 13 + yOffset
                    font: Font {
                        size: 20
                    }
                    content: noteText
                }
            ]
        }   // Group
    }
}	// GuitarString

Listing 2.3 shows the code for Main.fx, the main program for GuitarTuner.

Listing 2.3 Main.fx

package guitartuner;
import guitartuner.GuitarString;
import javafx.scene.effect.DropShadow;
import javafx.scene.Group;
import javafx.scene.paint.Color;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.Scene;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import noteplayer.SingleNote;

def noteValues = [ 40,45,50,55,59,64 ];		// numeric value required by midi
def guitarNotes = [ "E","A","D","G","B","E" ]; 																
// guitar note name
def numberFrets = 2;
def numberStrings = 6;
var singleNote =  SingleNote{};
singleNote.setInstrument(27);           	// "Clean Guitar"

var scene: Scene;
var group: Group;
Stage {
    title: "Guitar Tuner"
    visible: true
    scene: scene = Scene {
		fill: LinearGradient {
			startX: 0.0
			startY: 0.0
			endX: 0.0
			endY: 1.0
			proportional: true
			stops: [
				Stop {
					offset: 0.0
					color: Color.LIGHTGRAY
				},
				Stop {
					offset: 1.0
					color: Color.GRAY
				}
			]
        }
        width: 340
        height: 200
        content: [
            group = Group {
                // Center the whole group vertically within the scene
                layoutY: bind (scene.height - group.layoutBounds.height) / 
										2 - group.layoutBounds.minY
                content: [
                    Rectangle {					// guitar neck (fret board)
                        effect: DropShadow { }
                        x: 0
                        y: 0
                        width: 300
                        height: 121
                        fill: LinearGradient {
								startX: 0.0
								startY: 0.0
								endX: 0.0
								endY: 1.0
								proportional: true
								stops: [
									Stop {
										offset: 0.0
										color: Color.SADDLEBROWN 
									},
									Stop {
										offset: 1.0
										color: Color.BLACK
									}
								]
							}
						} // Rectangle
                    for (i in [0..<numberFrets])   // two frets
                        Line {
                            startX: 100 * (i + 1)
                            startY: 0
                            endX: 100 * (i + 1)
                            endY: 120
                            stroke: Color.GRAY
                        }
                    for (i in [0..<numberStrings])   // six guitar strings
                        GuitarString {
                            yOffset: i * 20 + 5
                            note: noteValues[i]
                            noteText: guitarNotes[i]
                            synthNote: singleNote
                        }
                ]
            }
        ]
    }
}
  • + Share This
  • 🔖 Save To Your Account