Wednesday, July 27, 2016

JFugue 33. J.S. Bach's Trias Harmonica (BWV 1072)

This is the only Java file used, as Mod functions are not needed.


Two different related set of patterns are used on different instruments. The first 4 midi channels get one instrument and next 4 midi channels get another instrument. Of course most DAWs will allow you to change the instruments and usually have better sound fonts, and you can install extra ones.


package jfugue33;

import java.io.File;
import java.io.IOException;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import org.jfugue.midi.MidiFileManager;
import org.jfugue.pattern.Pattern;
import org.jfugue.player.Player;

public class JFugue33 extends Application {
    
    public static void main(String[] args) {
        launch(args);
    }
    
    TextArea text;
    Button button;
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        
        button = new Button("Play J.S.\nBach's Trias\nHarmonica");
                
        button.setOnAction(e->example());
        button.setFont(Font.font("Verdana", 16));
        button.setPrefSize(200, 100);
        
        VBox examples = new VBox(10, button);
        examples.setPadding(new Insets(10));
        
        text = new TextArea();
        text.setPrefRowCount(20);
        text.setPrefColumnCount(32);
        text.setFont(Font.font("Verdana", 20));
        text.setEditable(false);
        text.setWrapText(true);
        
        HBox root = new HBox(50,examples,text);
        
        Scene scene = new Scene(root, 900, 600);
        primaryStage.setTitle("JFugue 33. J.S. Bach's"
                + " Trias Harmonica (BWV 1072)");
        
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    private void example() {
        
        Player player = new Player();
        
        Pattern voice0 = new Pattern();
        voice0.add("C5q. D5i E5q. F5i G5q. F5i E5q. D4i")
                .repeat(8).add("C5q.");
        
        Pattern voice1 = new Pattern(voice0);
        voice1.prepend("V1 I[CHOIR_AAHS] Rh"); // half note later
        
        Pattern voice2 = new Pattern(voice0);
        voice2.prepend("V2 I[CHOIR_AAHS] Rhh"); // 2 half note later
        
        Pattern voice3 = new Pattern(voice0);
        voice3.prepend("V3 I[CHOIR_AAHS] Rhhh"); // 3 half note later
        
        Pattern voice4 = new Pattern();
        voice4.add("G5q. F5i E5q. D5i C5q. D5i E5q. F5i")
                .repeat(8).add("G5q.");
        
        Pattern voice5 = new Pattern(voice4);
        voice5.prepend("V5 I[VOICE_OOHS] Rh"); // half note later
        
        Pattern voice6 = new Pattern(voice4);
        voice6.prepend("V6 I[VOICE_OOHS] Rhh"); // 2 half note later
        
        Pattern voice7 = new Pattern(voice4);
        voice7.prepend("V7 I[VOICE_OOHS] Rhhh"); // 3 half note later
        
        voice0.prepend("V0 I[CHOIR_AAHS]");
        
        Pattern choir0 = new Pattern(voice0, voice1, voice2, voice3);
        
        voice4.prepend("V4 I[VOICE_OOHS]");
        Pattern choir1 = new Pattern(voice4, voice5, voice6, voice7);
        
        Pattern pattern = new Pattern();
        pattern.add(choir0, choir1);
        pattern.setTempo(100);

        text.appendText("\n\nPattern:\n" + pattern + "\n");
        
        try {
            MidiFileManager.savePatternToMidi(pattern,
                    new File ("BWV1072.mid"));
        } catch (IOException ex) {
        }
        
        player.play(pattern);
    }
}

This is the output:



The saved midi file is opened in LMMS. This shows the 8 repeats for each channel as well as the different time offsets for the four voices in each of the two choirs.


Tuesday, July 26, 2016

JFugue 32. J.S. Bach's Canon No. 1 Goldberg Ground (BWV1087)

Below we have the new Mod class. The function of this class is to modify a pattern, as well as to analyze it.


Also note the names have been shortened. There are 3 functions which may be called:


1. Mod.transpose(pattern, nt) to get pattern with has been transposed in pitch by interval nt.


2. Mod.reverse(pattern) to get pattern with reverse order.


3. Mod.duration(pattern) to get total length in whole notes of a pattern.


package jfugue32;

import org.jfugue.pattern.Pattern;
import org.jfugue.pattern.Token;
import org.jfugue.theory.Note;

class Mod {
    
    public static Pattern transpose(Pattern pattern, int nt) {
        Pattern transpose = new Pattern();
        for (Token token: pattern.getTokens()) {
            if (token.getType() != Token.TokenType.NOTE) {
                transpose.add(token.getPattern());
            }
            else {
                String val = token.toString();
                if (val.substring(0,1).equalsIgnoreCase("r")) { // Rest 
                    transpose.add(token.toString());
                }
                else if (val.contains("+")) { // Chord (C+E+G format)
                    String newValue = "";
                    String[] notes = val.split("[+]");
                    int i;
                    for (i = 0; i<(notes.length-1); i++) {
                        newValue += (getNote(notes[i], 0, nt) + "+");
                    }
                    i = notes.length-1;
                    newValue += getNote(notes[i], 1, nt);
                    transpose.add(newValue);
                    
                }
                else { // Single note
                    transpose.add(getNote(token.toString(), 1, nt));
                }
            }
        }
        return transpose;
    }
    
    private static String getNote(String ntStr, int flg, int nt) {
        Note note = new Note(ntStr);
        byte value = note.changeValue(nt).getValue();
        String tone = Note.getToneString(value);
        if (flg == 0) return tone;
        else {
            String dur = Note.getDurationString(note.getDuration());
            String vel = note.getVelocityString();
            return tone + dur + vel;
        }
    }
    
    public static Pattern reverse(Pattern pattern) {
        Pattern transpose = new Pattern();
        for (Token token: pattern.getTokens()) 
            transpose.prepend(token);
        return transpose;
    }
    
    public static double duration(Pattern pattern) {
        double dur = 0.0;
        for (Token token: pattern.getTokens()) {
            String val = token.toString();
            if (token.getType() == Token.TokenType.NOTE) {
                if (val.contains("+")) {
                    Note n = new Note(val.replaceAll(".+[+]", ""));
                    dur += n.getDuration();
                } else {
                    Note n = new Note(val);
                    dur += n.getDuration();
                }
            }
        }
        return dur;
    }
}

In this canon, an 8-note pattern, of 2 whole note duration, is added to its reverse and then repeated for 5 more times. The duration of the reverse to total pattern is 1 to 12 as shown in the output.


package jfugue32;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import org.jfugue.pattern.Pattern;
import org.jfugue.player.Player;

public class JFugue32 extends Application {
    
    public static void main(String[] args) {
        launch(args);
    }
    
    TextArea text;
    Button button;
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        
        button = new Button("Play J.S.\nBach's Canon\nNo. 1");
                
        button.setOnAction(e->example());
        button.setFont(Font.font("Verdana", 16));
        button.setPrefSize(200, 100);
        
        VBox examples = new VBox(10, button);
        examples.setPadding(new Insets(10));
        
        text = new TextArea();
        text.setPrefRowCount(20);
        text.setPrefColumnCount(32);
        text.setFont(Font.font("Verdana", 20));
        text.setEditable(false);
        text.setWrapText(true);
        
        HBox root = new HBox(50,examples,text);
        
        Scene scene = new Scene(root, 900, 600);
        primaryStage.setTitle("JFugue 32. J.S. Bach's Canon No. 1"
                + " Goldberg Ground (BWV1087)");
        
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    private void example() {
        
        Player player = new Player();
        
        Pattern pattern = new Pattern();
        pattern.add("G4q F4q E4q D3q B3q C4q D4q G3q");
        
        Pattern patternReverse = new Pattern();
        patternReverse.add(Mod.reverse(pattern));
        
        pattern.add(patternReverse).repeat(6).setTempo(100);
 
        text.appendText("\n\nPattern:\n" + pattern + "\n");
        
        text.appendText("\nDuration of pattern: " + 
                Mod.duration(pattern));
        text.appendText("\nDuration of patternReverse: " + 
                Mod.duration(patternReverse));
        
        player.play(pattern);
        
    }
}

This is the output:


JFugue 31. Pattern Reverse and Pattern Duration in main class

This file demonstrates two more methods that will be later added to Mod class.


They are reverse (to get reverse of pattern) and duration (to get the total length of a pattern).


package jfugue31;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import org.jfugue.pattern.Pattern;
import org.jfugue.pattern.Token;
import org.jfugue.pattern.Token.TokenType;
import org.jfugue.player.Player;
import org.jfugue.theory.Note;

public class JFugue31 extends Application {
    
    public static void main(String[] args) {
        launch(args);
    }
    
    TextArea text;
    Button button;
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        
        button = new Button("Find Reverse\n and Duration");
                
        button.setOnAction(e->example());
        button.setFont(Font.font("Verdana", 16));
        button.setPrefSize(200, 100);
        
        VBox examples = new VBox(10, button);
        examples.setPadding(new Insets(10));
        
        text = new TextArea();
        text.setPrefRowCount(20);
        text.setPrefColumnCount(32);
        text.setFont(Font.font("Verdana", 20));
        text.setEditable(false);
        text.setWrapText(true);
        
        HBox root = new HBox(50,examples,text);
        
        Scene scene = new Scene(root, 900, 600);
        primaryStage.setTitle("JFugue 31. Pattern Reverse and Pattern"
                + " Duration in main class.");
        
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    private void example() {
        
        Player player = new Player();
        
        Pattern pattern = new Pattern("C4q Rq C4+D4+G5ha50 Rh G4h.");
        
        Pattern reversePattern = reverse(pattern);
        
        text.appendText("\n\nPattern:\n" + pattern + "\n");
        text.appendText("\nReverse:\n" + reversePattern + "\n");
        
        text.appendText("\nDuration of pattern: " + 
                duration(pattern));
        text.appendText("\nDuration of reverse pattern: " + 
                duration(reversePattern));
        player.play(pattern, reversePattern);
        
    }
    
    public Pattern reverse(Pattern pattern) {
        Pattern transpose = new Pattern();
        for (Token token: pattern.getTokens()) 
            transpose.prepend(token);
        return transpose;
    }
    
    public double duration(Pattern pattern) {
        double duration = 0.0;
        for (Token token: pattern.getTokens()) {
            String val = token.toString();
            if (token.getType() == TokenType.NOTE) {
                if (val.contains("+")) {
                    Note n = new Note(val.replaceAll(".+[+]", ""));
                    duration += n.getDuration();
                } else {
                    Note n = new Note(val);
                    duration += n.getDuration();
                }
            }
        }
        return duration;
    }
}

This is the output:


JFugue30. Row Your Boat

This plays Row Your Boat.


It uses the Mod class shown earlier to transpose the trumpet and clarinet parts above octave or below octave of the flute part.


package jfugue30;

import java.io.File;
import java.io.IOException;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import org.jfugue.midi.MidiFileManager;
import org.jfugue.pattern.Pattern;
import org.jfugue.player.Player;

public class JFugue30 extends Application {
    
    public static void main(String[] args) {
        launch(args);
    }
    
    TextArea text;
    Button button;
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        
        button = new Button("Play Row\n\tYour\n"
                + "\tBoat");
                
        button.setOnAction(e->example());
        button.setFont(Font.font("Verdana", 16));
        button.setPrefSize(200, 100);
        
        VBox examples = new VBox(10, button);
        examples.setPadding(new Insets(10));
        
        text = new TextArea();
        text.setPrefRowCount(20);
        text.setPrefColumnCount(32);
        text.setFont(Font.font("Verdana", 20));
        text.setEditable(false);
        text.setWrapText(true);
        
        HBox root = new HBox(50,examples,text);
        
        Scene scene = new Scene(root, 900, 600);
        primaryStage.setTitle("JFugue 30. Row Your Boat");
        
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    private void example() {
        
        Player player = new Player();
        
        Pattern flute = new Pattern();
        flute.add("C5q C5q C5i. D5s E5q E5i. D5s E5i. F5s G5h")
             .add("C6i C6i C6i G5i G5i G5i E5i E5i E5i C5i")
             .add("C5i C5i")
             .add("G5i. F5s E5i. D5s C5h")
             .repeat(2);
        
        Pattern trumpet = 
                new Pattern(Mod.transposeFromPattern(flute, 12));
        
        Pattern clarinet = 
                new Pattern(Mod.transposeFromPattern(flute, -12));
        
        flute.prepend("V0 I[FLUTE]");
        trumpet.prepend("V1 I[TRUMPET] R/1.0");
        clarinet.prepend("V2 I[CLARINET] R/2.0");
        
        Pattern pattern = new Pattern(flute, trumpet, clarinet);
        pattern.setTempo(108);
 
        text.appendText("\n\nPattern:\n" + pattern + "\n");
        
        try {
            MidiFileManager.savePatternToMidi(pattern,
                    new File("RowYourBoat.mid"));
        } catch (IOException ex) {
        }
       
        player.play(pattern); 
    }
}

This is the output:



You can open the saved midi file in a DAW. Here I use LMMS:


JFugue29. Mod class

We have two java files.


Below is Mod, where transposeFromPattern must be public to be called. However getNote can be private since only this class can call it. Again we have the limit that chords must be in the + format. This can be overcome but code will become more complex. The methods are static so they may be simply called without creating an instance.


package jfugue29;

import org.jfugue.pattern.Pattern;
import org.jfugue.pattern.Token;
import org.jfugue.theory.Note;

class Mod {
    
    public static Pattern transposeFromPattern(Pattern pattern, int nt) {
        Pattern transpose = new Pattern();
        for (Token token: pattern.getTokens()) {
            if (token.getType() != Token.TokenType.NOTE) {
                transpose.add(token.getPattern());
            }
            else {
                String val = token.toString();
                if (val.substring(0,1).equalsIgnoreCase("r")) { // Rest 
                    transpose.add(token.toString());
                }
                else if (val.contains("+")) { // Chord (C+E+G format)
                    String newValue = "";
                    String[] notes = val.split("[+]");
                    int i;
                    for (i = 0; i<(notes.length-1); i++) {
                        newValue += (getNote(notes[i], 0, nt) + "+");
                    }
                    i = notes.length-1;
                    newValue += getNote(notes[i], 1, nt);
                    transpose.add(newValue);
                    
                }
                else { // Single note
                    transpose.add(getNote(token.toString(), 1, nt));
                }
            }
        }
        return transpose;
    }
    
    private static String getNote(String noteString, int flag, int nt) {
        Note note = new Note(noteString);
        byte value = note.changeValue(nt).getValue();
        String tone = Note.getToneString(value);
        if (flag==0) return tone;
        else {
            String dur = Note.getDurationString(note.getDuration());
            String vel = note.getVelocityString();
            return tone + dur + vel;
        }
    }
}

The main class plays Deep Purple Smoke On the Water using the new Mod class. The sound is same as example 28, since the pattern is identical.


package jfugue29;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import org.jfugue.pattern.Pattern;
import org.jfugue.player.Player;

public class JFugue29 extends Application {
    
    public static void main(String[] args) {
        launch(args);
    }
    
    TextArea text;
    Button button;
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        
        button = new Button("Play Deep Purple\nSmoke On the\n\tWater");
                
        button.setOnAction(e->example());
        button.setFont(Font.font("Verdana", 16));
        button.setPrefSize(200, 100);
        
        VBox examples = new VBox(10, button);
        examples.setPadding(new Insets(10));
        
        text = new TextArea();
        text.setPrefRowCount(20);
        text.setPrefColumnCount(32);
        text.setFont(Font.font("Verdana", 20));
        text.setEditable(false);
        text.setWrapText(true);
        
        HBox root = new HBox(50,examples,text);
        
        Scene scene = new Scene(root, 900, 600);
        primaryStage.setTitle("JFugue 29. Smoke On the Water" +
                " by Deep Purple using Mod class.");
        
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    private void example() {
        
        Player player = new Player();
        
        String g1 = "G3q A#3q C4q. G3q A#3q C#4i C4h G3q A#3q C4q. A#3q "
                + "G3ih.";
        Pattern guitar1 = new Pattern(g1);
        
        Pattern guitar2 = Mod.transposeFromPattern(guitar1, -12);
        
        guitar1.repeat(8);
        guitar1.setInstrument("OVERDRIVEN_GUITAR").setVoice(0);
        
        guitar2.repeat(6);
        guitar2.prepend("R/8.0"); // start at 8 beat
        guitar2.setInstrument("OVERDRIVEN_GUITAR").setVoice(1);
        
        Pattern bass1 = new Pattern("G3i").repeat(10);
        
        Pattern bass2 = new Pattern("G3i").repeat(8); 
        Pattern bass3 =
                new Pattern("A#3i A#3i C4i C4i C4i A#3i A#3i G3i G3i");
        Pattern bass4 = new Pattern("G3i").repeat(5);
        
        Pattern bass = new Pattern(bass1, bass2, bass3, bass4);
        bass.repeat(4);
        bass.prepend("R/16.0"); // start at 16 beat
        bass.setInstrument("ELECTRIC_BASS_FINGER").setVoice(2);
        
        Pattern drum = new Pattern("Rq [ACOUSTIC_SNARE]q").repeat(16);
        drum.prepend("R/24.0"); // start at beat 24
        drum.setVoice(9); // percussion
        
        Pattern pattern = 
                new Pattern(guitar1, guitar2, bass, drum);
        pattern.setTempo(110);
 
        text.appendText("\n\nPattern:\n" + pattern + "\n\n");
        
        player.play(pattern);
        
    }
}

This is the output:


JFugue28. Transpose in main class

The Transpose code has been cleaned up (not requiring a listener as well) and put in the main code so this is the only file.


We send a pattern, as well as interval, to a method called transposeFromPattern. It converts each token using String and Note function into individual notes, and calls getNote as needed to get the String output.


Later we will put these two methods into their own class (Mod) and turn them static so we can use them as, transposePattern = Mod.transposeFromPattern(pattern,-12) to get pattern octave below.


In the split function for the chord case, we put '+' is [ ], so it is treated as a literal, and not as part of a regular expression.


package jfugue28;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import org.jfugue.pattern.Pattern;
import org.jfugue.pattern.Token;
import org.jfugue.pattern.Token.TokenType;
import org.jfugue.player.Player;
import org.jfugue.theory.Note;

public class JFugue28 extends Application {
    
    public static void main(String[] args) {
        launch(args);
    }
    
    TextArea text;
    Button button;
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        
        button = new Button("Find Transpose");
                
        button.setOnAction(e->example());
        button.setFont(Font.font("Verdana", 16));
        button.setPrefSize(200, 100);
        
        VBox examples = new VBox(10, button);
        examples.setPadding(new Insets(10));
        
        text = new TextArea();
        text.setPrefRowCount(20);
        text.setPrefColumnCount(32);
        text.setFont(Font.font("Verdana", 20));
        text.setEditable(false);
        text.setWrapText(true);
        
        HBox root = new HBox(50,examples,text);
        
        Scene scene = new Scene(root, 900, 600);
        primaryStage.setTitle("JFugue 28. Transpose in main class.");
        
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    private void example() {
        
        Player player = new Player();
        
        Pattern pattern = new Pattern("T85 C4q Rq C4+D4ha50 Rh "
                + "E5q.a100 Rs. C5+D5+E5q Ri C3+E4+F3+G4 rh.");
        
        Pattern transpose1 = transposeFromPattern(pattern, 1);
        Pattern transpose2 = transposeFromPattern(pattern, 2);
        Pattern transpose3 = transposeFromPattern(pattern, 3);
        Pattern transpose4 = transposeFromPattern(pattern, 4);
        
        text.appendText("\n\nPattern:\n" + pattern + "\n");
        text.appendText("\nTranspose by 1:\n" + transpose1 + "\n");
        text.appendText("\nTranspose by 2:\n" + transpose2 + "\n");
        text.appendText("\nTranspose by 3:\n" + transpose3 + "\n");
        text.appendText("\nTranspose by 4:\n" + transpose4 + "\n");
        
        player.play(pattern, 
                transpose1, transpose2, transpose3, transpose4);
        
    }
    
    public Pattern transposeFromPattern(Pattern pattern, int nt) {
        Pattern transpose = new Pattern();
        for (Token token: pattern.getTokens()) {
            if (token.getType() != TokenType.NOTE) {
                transpose.add(token.getPattern());
            }
            else {
                String val = token.toString();
                if (val.substring(0,1).equalsIgnoreCase("r")) { // Rest 
                    transpose.add(token.toString());
                }
                else if (val.contains("+")) { // Chord (C+E+G format)
                    String newValue = "";
                    String[] notes = val.split("[+]");
                    int i;
                    for (i = 0; i<(notes.length-1); i++) {
                        newValue += (getNote(notes[i], 0, nt) + "+");
                    }
                    i = notes.length-1;
                    newValue += getNote(notes[i], 1, nt);
                    transpose.add(newValue);
                    
                }
                else { // Single note
                    transpose.add(getNote(token.toString(), 1, nt));
                }
            }
        }
        return transpose;
    }
    
    public String getNote(String noteString, int flag, int interval) {
        Note note = new Note(noteString);
        byte value = note.changeValue(interval).getValue();
        String tone = Note.getToneString(value);
        if (flag==0) return tone;
        else {
            String dur = Note.getDurationString(note.getDuration());
            String vel = note.getVelocityString();
            return tone + dur + vel;
        }
    }
}

This is the output:


Monday, July 25, 2016

JFugue27. Smoke On the Water by Deep Purple.

This program uses the Transpose class and the Parse Listener.


This plays Smoke On the Water by Deep Purple. For voice 1, the pattern of voice0 is lowered by octave and we start at beat 8. One beat is a quarter note. Both voice 0 and voice 1 are the same instument (OVERDRIVEN_GUITAR). We also have ELECTRIC_BASS_FINGER and percussion (ACOUSTIC_SNARE).


package jfugue27;

import java.io.File;
import java.io.IOException;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import org.jfugue.midi.MidiFileManager;
import org.jfugue.pattern.Pattern;
import org.jfugue.player.Player;

public class JFugue27 extends Application {
    
    public static void main(String[] args) {
        launch(args);
    }
    
    TextArea text;
    Button button;
    int interval = 2;
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        
        button = new Button("Play Deep Purple\nSmoke On the\n\tWater");
                
        button.setOnAction(e->example());
        button.setFont(Font.font("Verdana", 16));
        button.setPrefSize(200, 100);
        
        VBox examples = new VBox(10, button);
        examples.setPadding(new Insets(10));
        
        text = new TextArea();
        text.setPrefRowCount(20);
        text.setPrefColumnCount(32);
        text.setFont(Font.font("Verdana", 20));
        text.setEditable(false);
        text.setWrapText(true);
        
        HBox root = new HBox(50,examples,text);
        
        Scene scene = new Scene(root, 900, 600);
        primaryStage.setTitle("JFugue 27. Smoke On the Water" +
                " by Deep Purple.");
        
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    private void example() {
        
        Player player = new Player();
        
        String g1 = "G3q A#3q C4q. G3q A#3q C#4i C4h G3q A#3q C4q. A#3q "
                + "G3ih.";
        Pattern guitar1 = new Pattern(g1);
        
        Transpose transpose = new Transpose(-12); // octave lower
        Pattern guitar2 = transpose.transposeFromPattern(guitar1);
        
        guitar1.repeat(8);
        guitar1.setInstrument("OVERDRIVEN_GUITAR").setVoice(0);
        
        guitar2.repeat(6);
        guitar2.prepend("R/8.0"); // start at 8 beat
        guitar2.setInstrument("OVERDRIVEN_GUITAR").setVoice(1);
        
        Pattern bass1 =
                new Pattern("G3i G3i G3i G3i G3i G3i G3i G3i G3i G3i"); 
        Pattern bass2 =
                new Pattern("G3i G3i G3i G3i G3i G3i G3i G3i");
        Pattern bass3 =
                new Pattern("A#3i A#3i C4i C4i C4i A#3i A#3i G3i G3i");
        Pattern bass4 =
                new Pattern("G3i G3i G3i G3i G3i");
        Pattern bass = new Pattern(bass1, bass2, bass3, bass4);
        bass.repeat(4);
        bass.prepend("R/16.0"); // start at 16 beat
        bass.setInstrument("ELECTRIC_BASS_FINGER").setVoice(2);
        
        Pattern drum = new Pattern("Rq [ACOUSTIC_SNARE]q").repeat(16);
        drum.prepend("R/24.0"); // start at beat 24
        drum.setVoice(9); // percussion
        
        Pattern pattern = 
                new Pattern(guitar1, guitar2, bass, drum);
        pattern.setTempo(110);
 
        text.appendText("\n\nPattern:\n" + pattern + "\n\n");
        
        try {
            MidiFileManager.savePatternToMidi(pattern,
                    new File("DeepPurple.mid"));
        } catch (IOException ex) {
        }
        player.play(pattern);
        
    }
}

This is the output. You have to scroll to see the entire pattern. The midi file is 70 seconds.


JFugue26. Transpose using a Transpose Class

Now we have 3 classes in 3 different Java files. We have a Transpose class which acts to encapsulate code so the main file can concentrate on creating the pattern, and have minimum number of statements for transposing.


When a Transpose object is created we have to pass in the interval to the constructor. The main work is done in transposeFromPattern(Pattern pattern) method. The initial pattern is read, and the pattern tokens are parsed and the proper interval added to each note. Finally the method returns the transposed pattern.


This Transpose method can not handle tokens such as C3maj, instead the notes have to be written as C3+E3+G3. With a little more work it is possible to find a proper solution.


package jfugue26;

import org.jfugue.pattern.Pattern;
import org.staccato.StaccatoParser;

public class Transpose {
    Pattern pattern;
    Pattern transpose;
    int interval;
    
    public Transpose(int interval) {
        this.interval = interval;
    }
    
    public Pattern transposeFromPattern(Pattern pattern) {
        this.pattern = new Pattern(pattern);
        StaccatoParser parser = new StaccatoParser();
        JFugue26a listener = new JFugue26a();
        parser.addParserListener(listener);
        listener.setInterval(interval);
        transpose = new Pattern();
        pattern.getTokens().stream().map((token) -> {
            listener.clearSB();
            parser.parse(token.getPattern());
            return token;
        }).forEach((_item) -> {
            transpose.add(listener.getString());
        });
        return transpose;
    }
}

The parse listener is defined in Ex26a, and which the Transpose class calls.


Because the Transpose class and the Parse Listener class will be almost identical except for names, they will not be shown from now on, whenever they are needed.



package jfugue26;

import org.jfugue.parser.ParserListenerAdapter;
import org.jfugue.theory.Note;

class JFugue26a extends ParserListenerAdapter {
    private final StringBuilder sb = new StringBuilder();
    private byte interval;
    private final Note n = new Note();
    
    @Override
    public void onNoteParsed(Note note) {
        if (note.isRest()) {
            sb.append("R/");
            sb.append(note.getDuration());
        }
        else if (note.isFirstNote()) {
            n.setValue((byte) (note.getValue() + interval));
            sb.append(n.getToneString());
            sb.append(n.getOctave());
            sb.append("/");
            sb.append(note.getDuration());
        }
        else if (note.isHarmonicNote()) {
            n.setValue((byte) (note.getValue() + interval));
            sb.append("+");
            sb.append(n.getToneString());
            sb.append(n.getOctave());
            sb.append("/");
            sb.append(note.getDuration());
        }
    }
    
    public String getString() {
        String val = sb.toString();
        String[] ret = val.split("/");
        String valOut = "";
        for (int i = 0; i< ret.length-1; i++) {
            valOut += ret[i].replaceAll(".*[+]", "+");
        }
        valOut = valOut + "/" + ret[ret.length-1];
        return valOut;
    }
    
    public void setInterval(int interval) {
        this.interval = (byte) interval;
    }
    
    public void clearSB() {
        sb.delete(0, sb.length());
    }
}


Finally the main class will only have 2 statements to deal with the Transpose class and Parse Listener.


The constructor will set the interval. Next the pattern is passed in via the method transposeFromPattern, and the transposed pattern is returned.



package jfugue26;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import org.jfugue.pattern.Pattern;
import org.jfugue.player.Player;

public class JFugue26 extends Application {
    
    public static void main(String[] args) {
        launch(args);
    }
    
    TextArea text;
    Button button;
    int interval = 2;
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        
        button = new Button("Transpose notes\n\tusing\n"
                + "Transpose class");
                
        button.setOnAction(e->example());
        button.setFont(Font.font("Verdana", 16));
        button.setPrefSize(200, 100);
        
        VBox examples = new VBox(10, button);
        examples.setPadding(new Insets(10));
        
        text = new TextArea();
        text.setPrefRowCount(20);
        text.setPrefColumnCount(32);
        text.setFont(Font.font("Verdana", 20));
        text.setEditable(false);
        text.setWrapText(true);
        
        HBox root = new HBox(50,examples,text);
        
        Scene scene = new Scene(root, 900, 600);
        primaryStage.setTitle("JFugue 26. Transpose Class");
        
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    private void example() {
        
        Player player = new Player();
        
        Pattern p1=new Pattern("F5q Rq Ab5q Ri F5q F5i Bb5q F5q E#5q");
        Pattern p2=new Pattern("F5q Rq C6q Ri F5q F5i Db6q C6q Ab5q");
        Pattern p3=new Pattern("F5q C6q F6q F5i Eb5q Eb5i C5q G5q F5q.");
        Pattern pattern = new Pattern(p1,p2,p3);
        text.appendText("\n\nPattern:\n" + pattern + "\n\n");
        
        Transpose transpose = new Transpose(interval);
        Pattern transposePattern=transpose.transposeFromPattern(pattern);
        
        text.appendText(
                String.format("Transposing by %d semitones" 
                        + " (%.2f octave):\n",
                        interval, interval/12.0));
        
        text.appendText("\nTranspose Pattern:\n" + transposePattern);
        pattern.setInstrument("SYNTH_BASS_2").setTempo(220);
        transposePattern.setInstrument("SYNTH_BASS_1").setTempo(180);
        Pattern comp = new Pattern(pattern, new Pattern("Rh"),
                transposePattern);
        player.play(comp);
    }
}

This is the output after calling for a 2 semitone transposition:


JFugue25. Transposing of notes and chords

The listener object is based on JFugue25a class. In the override to onNoteParsed, a string is built up with all duration in /dur format, where dur is a double.


We also have function to set interval, for transposition, way to clear the StringBuilder, as well as a way to get the output pattern string which will be transposed. Each chord gets multiple /dur values and we remove extra using replaceAll. You can print the string val to see extra characters that must be removed.


package jfugue25;

import org.jfugue.parser.ParserListenerAdapter;
import org.jfugue.theory.Note;

class JFugue25a extends ParserListenerAdapter {
    private final StringBuilder sb = new StringBuilder();
    private byte interval;
    private final Note n = new Note();
    
    @Override
    public void onNoteParsed(Note note) {
        if (note.isRest()) {
            sb.append("R/");
            sb.append(note.getDuration());
        }
        else if (note.isFirstNote()) {
            n.setValue((byte) (note.getValue() + interval));
            sb.append(n.getToneString());
            sb.append(n.getOctave());
            sb.append("/");
            sb.append(note.getDuration());
        }
        else if (note.isHarmonicNote()) {
            n.setValue((byte) (note.getValue() + interval));
            sb.append("+");
            sb.append(n.getToneString());
            sb.append(n.getOctave());
            sb.append("/");
            sb.append(note.getDuration());
        }
    }
    
    public String getString() {
        String val = sb.toString();
        String[] ret = val.split("/");
        String valOut = "";
        for (int i = 0; i< ret.length-1; i++) {
            valOut += ret[i].replaceAll(".*[+]", "+");
        }
        valOut = valOut + "/" + ret[ret.length-1];
        return valOut;
    }
    
    public void setInterval(int interval) {
        this.interval = (byte) interval;
    }
    
    public void clearSB() {
        sb.delete(0, sb.length());
    }
}

The main class parses all individual tokens, which call onNoteParsed for each note and chord. After getting the output pattern corresponding to a token, we clear the StringBuilder so the next token can be parsed and so on.


The Axel theme from Beverly Hills Cop is played and then its transpose (slightly different instrument and tempo).


package jfugue25;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import org.jfugue.pattern.Pattern;
import org.jfugue.pattern.Token;
import org.jfugue.player.Player;
import org.staccato.StaccatoParser;

public class JFugue25 extends Application {
    
    public static void main(String[] args) {
        launch(args);
    }
    
    TextArea text;
    Button button;
    int interval = 7;
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        
        button = new Button("Transpose notes");
                
        button.setOnAction(e->example());
        button.setFont(Font.font("Verdana", 16));
        button.setPrefSize(200, 100);
        
        VBox examples = new VBox(10, button);
        examples.setPadding(new Insets(10));
        
        text = new TextArea();
        text.setPrefRowCount(20);
        text.setPrefColumnCount(32);
        text.setFont(Font.font("Verdana", 20));
        text.setEditable(false);
        text.setWrapText(true);
        
        HBox root = new HBox(50,examples,text);
        
        Scene scene = new Scene(root, 900, 600);
        primaryStage.setTitle("JFugue 25. Transposing of notes");
        
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    private void example() {
        
        Player player = new Player();
        
        Pattern p1=new Pattern("F5q Rq Ab5q Ri F5q F5i Bb5q F5q E#5q");
        Pattern p2=new Pattern("F5q Rq C6q Ri F5q F5i Db6q C6q Ab5q");
        Pattern p3=new Pattern("F5q C6q F6q F5i Eb5q Eb5i C5q G5q F5q.");
        Pattern pattern = new Pattern(p1,p2,p3);
        text.appendText("\n\nPattern:\n" + pattern + "\n\n");
        
        StaccatoParser parser = new StaccatoParser();
        JFugue25a listener = new JFugue25a();
        parser.addParserListener(listener);
        listener.setInterval(interval);
        text.appendText(
                String.format("Transposing by %d semitones" 
                        + " (%.2f octave):\n",
                        interval, interval/12.0));
        
        Pattern transpose = new Pattern();
        for (Token token: pattern.getTokens()) {
            listener.clearSB();
            parser.parse(token.getPattern());
            transpose.add(listener.getString());
        }
        
        text.appendText("\nTranspose Pattern:\n" + transpose);
        pattern.setInstrument("SYNTH_BASS_2").setTempo(220);
        transpose.setInstrument("SYNTH_BASS_1").setTempo(180);
        Pattern comp = new Pattern(pattern, new Pattern("Rh"), transpose);
        player.play(comp);
    }
}

This is the output:


JFugue 24. First Note, Rest or Harmonic

One of the important actions, on patterns, is transposition up and down semitones. There are 12 semitones to an octave.


JFugue parsers allows us modifications to a pattern. There are two classes in the jfugue24 package. Below is for the JFugue24a class in JFugue24a.java file. Here each note is queried, whether it is rest, a lone note (only first note), start of chord (first note) or part of chord (harmonic with the first note). Since this will be used later (for the actual transposition) a very simple case is presented here.


package jfugue24;

import org.jfugue.parser.ParserListenerAdapter;
import org.jfugue.theory.Note;

class JFugue24a extends ParserListenerAdapter {
    private StringBuilder sb = new StringBuilder();
    
    @Override
    public void onNoteParsed(Note note) {
        sb.append("\nNote ");
        sb.append(note);
        String str;
        if (note.isRest()) str = " is Rest";
        else if (note.isFirstNote()) str =" is First Note";
        else if (note.isHarmonicNote()) str = " is Harmonic Note";
        else str = "It is not rest, first note or harmonic note";
        sb.append(str);
    }
    
    public String getString() {
        return sb.toString();
    }
}

In the main class for the JFugue24 class, we create a listener object of JFugue24a that parses a pattern string, with the different conditionals.


In the text output, we can find the status of the various notes.



package jfugue24;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import org.jfugue.pattern.Pattern;
import org.jfugue.realtime.RealtimePlayer;
import org.staccato.StaccatoParser;

public class JFugue24 extends Application {
    
    public static void main(String[] args) {
        launch(args);
    }
    
    TextArea text;
    Button button;
    RealtimePlayer rp;
    
    @Override
    public void stop() {
        rp.close();
    }
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        
        rp = new RealtimePlayer();
        
        button = new Button("Kind of notes");
                
        button.setOnAction(e->example());
        button.setFont(Font.font("Verdana", 16));
        button.setPrefSize(200, 100);
        
        VBox examples = new VBox(10, button);
        examples.setPadding(new Insets(10));
        
        text = new TextArea();
        text.setPrefRowCount(20);
        text.setPrefColumnCount(32);
        text.setFont(Font.font("Verdana", 20));
        text.setEditable(false);
        text.setWrapText(true);
        
        HBox root = new HBox(50,examples,text);
        
        Scene scene = new Scene(root, 900, 600);
        primaryStage.setTitle("JFugue 24. First Note or Harmonic");
        
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    private void example() {
        
        Pattern pattern = new Pattern("T85 C4q Rq C4+D4q Rh "
                + "E5q. Rs. C5+D5+E5q");
        text.appendText("\nPattern:\n" + pattern + "\n\n");
        
        StaccatoParser parser = new StaccatoParser();
        JFugue24a listener = new JFugue24a();
        parser.addParserListener(listener);
        parser.parse(pattern);
        
        text.appendText("Types of notes:\n");
        text.appendText(listener.getString());
        
        
        rp.play(pattern);
    }
}

This is the output:


JFugue 23. Drum Pattern over 4 measures

Instead of using Strings or the more efficient StringBuilder, we can use simple Patterns to create more complicated Patterns. Patterns use StringBuilder behind the scene.


We added 4 measures of bass, snare, and hihat patterns. They were put in separate layers to sound in harmony and finally added to MIDI channel 10 (or voice 9) - the Percussion channel.


The different Strings inside [ ] for dictionary lookup, can be found in the reference mentioned earlier. It is possible to use the numbers instead since MIDI format standards can be found on different MIDI sites.


package jfugue23;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import org.jfugue.pattern.Pattern;
import org.jfugue.player.Player;

public class JFugue23 extends Application {
    
    public static void main(String[] args) {
        launch(args);
    }
    
    TextArea text;
    Button button;
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        
        button = new Button("Play Drum\nPattern");
                
        button.setOnAction(e->example());
        button.setFont(Font.font("Verdana", 16));
        button.setPrefSize(200, 100);
        
        VBox examples = new VBox(10, button);
        examples.setPadding(new Insets(10));
        
        text = new TextArea();
        text.setPrefRowCount(20);
        text.setPrefColumnCount(32);
        text.setFont(Font.font("Verdana", 20));
        text.setEditable(false);
        text.setWrapText(true);
        
        HBox root = new HBox(50,examples,text);
        
        Scene scene = new Scene(root, 900, 600);
        primaryStage.setTitle("JFugue 23. Drum Pattern over 4 measures");
        
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    private void example() {
        
        Player player = new Player();
        
        text.appendText("Played Drum Pattern\n");
        
        // 2 measures of bass, snare, hihat
        
        Pattern bassDrum = new Pattern("[BASS_DRUM]q Rq");
        bassDrum.repeat(4);
        bassDrum.prepend("L0");
        
        Pattern snareDrum = new Pattern("Rq [ACOUSTIC_SNARE]q");
        snareDrum.repeat(4);
        snareDrum.prepend("L1");
        
        Pattern hihatDrum = new Pattern("[CLOSED_HI_HAT]i");
        hihatDrum.repeat(15);
        hihatDrum.add("[OPEN_HI_HAT]i");
        hihatDrum.prepend("L2");
        

        Pattern pattern = new Pattern(bassDrum, snareDrum, hihatDrum);
        pattern.repeat(2); // 4 measures in total
        pattern.setVoice(9); // Percussion channel
        pattern.setTempo(125);
        text.appendText("Pattern:\n" + pattern + "\n\n");
        player.play(pattern);
    }
}

This is the output after playing once:


Sunday, July 24, 2016

JFugue22. Harold Faltermeyer's electronic instrumental theme from the film Beverly Hills Cop (1984)

We use the instrument SYNTH_BASS_2 (instrument 39) for the instrumental of Beverly Hills Cop.


The instrument list, with the defined constants for dictionary lookup, is in section 2.4 of The Complete Guide to JFugue 5.0.


The standard instrument numbers 0-127 or 1-128 (for 0-based or 1-based counting) can also be found online. Java, JFugue and most languages use 0-based counting.


package jfugue22;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import org.jfugue.pattern.Pattern;
import org.jfugue.player.Player;

public class JFugue22 extends Application {
    
    public static void main(String[] args) {
        launch(args);
    }
    
    TextArea text;
    Button button;
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        
        button = new Button("Play Harold Faltermeyer's\nelectronic "
                + "instrumental\ntheme");
                
        button.setOnAction(e->example());
        button.setFont(Font.font("Verdana", 12));
        button.setPrefSize(200, 100);
        
        VBox examples = new VBox(10, button);
        examples.setPadding(new Insets(10));
        
        text = new TextArea();
        text.setPrefRowCount(20);
        text.setPrefColumnCount(32);
        text.setFont(Font.font("Verdana", 20));
        text.setEditable(false);
        text.setWrapText(true);
        
        HBox root = new HBox(50,examples,text);
        
        Scene scene = new Scene(root, 900, 600);
        primaryStage.setTitle("JFugue 22. Harold Faltermeyer's "
                + " electronic instrumental theme");
        
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    private void example() {
        
        Player player = new Player();
        
        text.appendText("Played Harold Faltermeyer's electronic "
                + "instrumental theme from the film Beverly Hills "
                + "Cop (1984)\n\n");
        
        StringBuilder sb = new StringBuilder();
        sb.append(" F5q Rq Ab5q Ri F5q F5i Bb5q F5q E#5q ");
        sb.append(" F5q Rq C6q Ri F5q F5i Db6q C6q Ab5q ");
        sb.append(" F5q C6q F6q F5i Eb5q Eb5i C5q G5q F5q. ");

        Pattern pattern = new Pattern(sb.toString());
        pattern.setTempo(220);
        pattern.setInstrument("SYNTH_BASS_2");
        text.appendText("Pattern:\n" + pattern + "\n\n");
        player.play(pattern);
    }
}

This is the output after 2 plays:


JFugue21. Autumn Leaves in a Jazz trio

Three MIDI channels (V0, V1, V2) have Trumpet, Vibraphone, and Acoustic Bass instruments used in this trio.


We again use StringBuilder class to create our string pattern.


package jfugue21;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import org.jfugue.pattern.Pattern;
import org.jfugue.player.Player;

public class JFugue21 extends Application {
    
    public static void main(String[] args) {
        launch(args);
    }
    
    TextArea text;
    Button button;
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        
        button = new Button("Play Autumn \n\tLeaves\n in a Jazz trio");
                
        button.setOnAction(e->example());
        button.setFont(Font.font("Verdana", 16));
        button.setPrefSize(200, 100);
        
        VBox examples = new VBox(10, button);
        examples.setPadding(new Insets(10));
        
        text = new TextArea();
        text.setPrefRowCount(20);
        text.setPrefColumnCount(32);
        text.setFont(Font.font("Verdana", 20));
        text.setEditable(false);
        text.setWrapText(true);
        
        HBox root = new HBox(50,examples,text);
        
        Scene scene = new Scene(root, 900, 600);
        primaryStage.setTitle("JFugue 21. Autumn Leaves in a Jazz trio");
        
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    private void example() {
        
        Player player = new Player();
        
        StringBuilder sb = new StringBuilder();
        sb.append(" V0 I[TRUMPET] Rq E6q F#6q G5q C6w Ri D5q. ");
        sb.append(" E5q F#5q B5q.  B5hi ");
        sb.append(" Rq C5q D5q E5q A5w Ri B4q. A5q G5q E5w. ");
        sb.append(" V1 I[VIBRAPHONE] Rw E4+G4+A4+C5h E4+G4+A4+C5q Rq ");
        sb.append(" E4+F#4+A4+C4q ");
        sb.append(" Rh. D4+F#4+G4+B4h D4+F#4+G4+B4q ");
        sb.append(" Rq C4+E4+G4+B4q Rh. E4+F#4+A4+C5h ");
        sb.append(" E4+F#4+A4+C5q ");
        sb.append(" Rq D#4+F#4+A4+B4q Rh. E4+G4+B4h ");
        sb.append(" D#4+F#4+A4+B4q ");
        sb.append(" Rq E4+G4+B4h Rh ");
        sb.append(" V2 I[ACOUSTIC_BASS] Rw A3q Ri A3i E3h D3q Ri ");
        sb.append(" D3i A3h G3q Ri ");
        sb.append(" G3i D3h C3q ");
        sb.append(" Ri C3i G3h F#3q Ri F#3i C3h B2q Ri B2i F#3h E3q "); 
        sb.append(" Ri E3i E3q B2q E3h Rh ");
 
        Pattern pattern = new Pattern(sb.toString());
        pattern.setTempo(140);
        text.appendText("Playing pattern:\n" + pattern + "\n\n");
        player.play(pattern);
    }
}

This is the output after 2 plays:


JFugue20. Haydn Opus 64 no 5

We use the string instrument with Haydn Opus 64 no 5.


We use absolute timing. You could also use different voices and set appropriate rest at start. The StringBuilder class is used to build our pattern string.


package jfugue20;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import org.jfugue.pattern.Pattern;
import org.jfugue.player.Player;

public class JFugue20 extends Application {
    
    public static void main(String[] args) {
        launch(args);
    }
    
    TextArea text;
    Button button;
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        
        button = new Button("Play \nHaydn, Opus 64\nno 5");
                
        button.setOnAction(e->example());
        button.setFont(Font.font("Verdana", 16));
        button.setPrefSize(200, 100);
        
        VBox examples = new VBox(10, button);
        examples.setPadding(new Insets(10));
        
        text = new TextArea();
        text.setPrefRowCount(20);
        text.setPrefColumnCount(32);
        text.setFont(Font.font("Verdana", 20));
        text.setEditable(false);
        text.setWrapText(true);
        
        HBox root = new HBox(50,examples,text);
        
        Scene scene = new Scene(root, 900, 600);
        primaryStage.setTitle("JFugue 20. Haydn Opus 64 no 5");
        
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    private void example() {
        
        Player player = new Player();
        
        StringBuilder sb = new StringBuilder();
        sb.append(" A6i Ri A6i Ri A6i Ri A6w A7h. E7i D7i D7h ");
        sb.append(" C#7i. D7s D7i. C#7t D7t E7q ");
        sb.append(" @3.0 F#5q G5q F#5q E5q D5q Rh. G5q A5q G5q ");
        sb.append(" F#5q E5q ");
        sb.append(" @3.0 D5q E5q D5q A4q F#4q Rh. E5q F#5q E5q ");
        sb.append(" D5q C#5q ");
        sb.append(" @7.0 D3q F#3q A3q D4q A3q Rh. A3q ");

        Pattern pattern = new Pattern(sb.toString());
        pattern.setTempo(104);
        pattern.setInstrument("SYNTH_STRINGS_1");
        text.appendText("Playing pattern:\n" + pattern + "\n\n");
        player.play(pattern);
    }
}

This is the output after playing 2 times:


JFugue19. Bruce Hornsby's The Way It Is (1986)

These are the beginning notes from Bruce Hornsby's The Way It Is.


It is possible to write dotted eighth as i. or is for eighth and sixteenth, or as /0.1875.


package jfugue19;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import org.jfugue.pattern.Pattern;
import org.jfugue.realtime.RealtimePlayer;

public class JFugue19 extends Application {
    
    public static void main(String[] args) {
        launch(args);
    }
    
    TextArea text;
    Button button;
    RealtimePlayer rp;
    
    @Override
    public void stop() {
        rp.close();
    }
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        
        
        rp = new RealtimePlayer();
        
        button = new Button("Play \nBruce Hornsby's"
                + "\nThe Way It Is");
                
        button.setOnAction(e->example());
        button.setFont(Font.font("Verdana", 16));
        button.setPrefSize(200, 100);
        
        VBox examples = new VBox(10, button);
        examples.setPadding(new Insets(10));
        
        text = new TextArea();
        text.setPrefRowCount(20);
        text.setPrefColumnCount(32);
        text.setFont(Font.font("Verdana", 20));
        text.setEditable(false);
        text.setWrapText(true);
        
        HBox root = new HBox(50,examples,text);
        
        Scene scene = new Scene(root, 900, 600);
        primaryStage.setTitle("JFugue 19. Bruce Hornsby's "
                + "\"The Way It Is” (1986)");
        
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    private void example() {
        
        String patt1 = " A5s B5s A4+E5+G5+C6i. A4+E5+G5+C6i. ";
        String patt2 = " E4+D5+G5+B5h D5+A5s G5s D4+D5+F#5+A5i. ";
        String patt3 = " D4+D5+G5+B5i. C4+C5+D5+G5q. C4+E5i D5s ";
        String patt4 = " C5s G3+B5+D5q. G3+D5+G5+B5i D4+E5+A5+C6i. ";
        String patt5 = " D4+D5+G5+B5i A5s G5i C4+C5+D5+G5i. ";
        String patt6 = " C4+C5+D5+G5i. G4+B4+D5+G5h ";
        
        String patt = patt1 +  patt2 + patt3 
                + patt4 + patt5 + patt6;
        Pattern pattern = new Pattern(patt);
        pattern.setTempo(105);
        text.appendText("Playing pattern:\n" + pattern + "\n\n");
        rp.play(pattern);
    }
}

This is the output after 3 plays:


Saturday, July 23, 2016

JFugue18. Beethoven's Fur Elise

We play notes from Beethoven's Fur Elise.


The tokens, such as notes, should have space between them, and it does not matter how much. Thus it is better to put space before and after token since here some patterns are repeated.


package jfugue18;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import org.jfugue.realtime.RealtimePlayer;

public class JFugue18 extends Application {
    
    public static void main(String[] args) {
        launch(args);
    }
    
    TextArea text;
    Button button;
    RealtimePlayer rp;
    
    @Override
    public void stop() {
        rp.close();
    }
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        
        
        rp = new RealtimePlayer();
        
        button = new Button("Play \nBeethoven's \nFur Elise");
                
        button.setOnAction(e->example());
        button.setFont(Font.font("Verdana", 16));
        button.setPrefSize(200, 100);
        
        VBox examples = new VBox(10, button);
        examples.setPadding(new Insets(10));
        
        text = new TextArea();
        text.setPrefRowCount(20);
        text.setPrefColumnCount(32);
        text.setFont(Font.font("Verdana", 20));
        text.setEditable(false);
        text.setWrapText(true);
        
        HBox root = new HBox(50,examples,text);
        
        Scene scene = new Scene(root, 900, 600);
        primaryStage.setTitle("JFugue 18. Beethoven's Fur Elise");
        
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    private void example() {
        
        String patt1 =" E6s D#6s E6s D#6s E6s B5s D6s C6s ";
        String patt2 = " A5i Rs C5s E5s A5s B5i Rs E5s ";
        String patt3 = " G#5s B5s C6i Rs E5s ";
        String patt4 = " C6s B5s A5i ";
 
        String patt = " T60 " + patt1 +  patt2 + patt3 
                + patt1 + patt2 + patt4;
        text.appendText("Playing pattern:\n" + patt + "\n\n");
        rp.play(patt);
    }
}

This is the output after playing it twice:


JFugue17. Chord progression from 2Pac's Changes (1998)

We play chord progression from 2Pac's Changes.


The pattern in patt1 is repeated twice. The tempo is set at 105. The dotted notes are indicated by putting a dot(.) after the duration such as q. for dotted quarter note. We have to be careful to put spacing between all tokens so they are properly parsed.


package jfugue17;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import org.jfugue.player.Player;

public class JFugue17 extends Application {
    
    public static void main(String[] args) {
        launch(args);
    }
    
    TextArea text;
    Button button;
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        
        
        button = new Button("Play 2pac");
                
        button.setOnAction(e->example());
        
        VBox examples = new VBox(10, button);
        examples.setPadding(new Insets(10));
        
        text = new TextArea();
        text.setPrefRowCount(20);
        text.setPrefColumnCount(32);
        text.setFont(Font.font("Verdana", 20));
        text.setEditable(false);
        text.setWrapText(true);
        
        HBox root = new HBox(50,examples,text);
        
        Scene scene = new Scene(root, 900, 600);
        primaryStage.setTitle("JFugue 17. Play 2Pac");
        
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    private void example() {
        
        Player player = new Player();
        
        String patt1 = "E5+G5+C5i. E5+G5+C6i. D5+G5+B5h A5s G5s "
                + "D5+F#5+A5i. D5+G5+B5i. C5+E5+G5q. E5i D5s C5s "
                + "G4+B5+D5q.";
        String patt2 = "A5s B5s";
        String patt = "T105 " + patt1 + " " + patt2 + " " + patt1;
        text.appendText("Playing pattern:\n" + patt + "\n\n");
        player.play(patt);
    }
}

This is the output after playing it twice:


Friday, July 15, 2016

JFugue 16. Inversions

Chord inversions are indicated by ^ for 1st inversion, ^^ for second inversion, etc. after chord name.


We can also use use Cmaj^E for example, for Cmaj inversion with E as bass, etc.


package jfugue16;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import org.jfugue.pattern.Pattern;
import org.jfugue.realtime.RealtimePlayer;

public class JFugue16 extends Application {
    
    public static void main(String[] args) {
        launch(args);
    }
    
    RealtimePlayer realtimePlayer;
    TextArea text;
    Button button1, button2;
    
    @Override
    public void stop() {
        realtimePlayer.close();
    }
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        
        int w = 250, h = 100;
        realtimePlayer = new RealtimePlayer();
        button1 = new Button("Play Inversions 1");
        button1.setOnAction( e-> example1());
        button1.setPrefSize(w,h);
        button1.setFont(Font.font("Verdana", 16));
        
        button2 = new Button("Play Inversions 2");
        button2.setOnAction( e-> example2());
        button2.setPrefSize(w,h);
        button2.setFont(Font.font("Verdana", 16));
        
        
        VBox examples = new VBox(10, button1, button2);
        examples.setPadding(new Insets(10));
        
        text = new TextArea();
        text.setPrefRowCount(20);
        text.setPrefColumnCount(32);
        text.setFont(Font.font("Verdana", 20));
        text.setEditable(false);
        text.setWrapText(true);
        
        HBox root = new HBox(50,examples,text);
        
        Scene scene = new Scene(root, 900, 600);
        primaryStage.setTitle("JFugue 16. Inversions");
        
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    private void example1() {
        
        String patt = "Cmaj Ri Cmaj^ Rs Cmaj^^ Ri Cmaj";
        Pattern pattern = new Pattern(patt);
        text.appendText("\nPattern:\t" + pattern);
        realtimePlayer.play(pattern);
    }
    
    private void example2() {
        
        String patt = "Cmaj Ri Cmaj^E Rs Cmaj^G Ri Cmaj";
        Pattern pattern = new Pattern(patt);
        text.appendText("\nPattern:\t" + pattern);
        realtimePlayer.play(pattern);
    }
}

This is the output:


Wednesday, July 13, 2016

JFugue 15. Absolute Time 2

The same 3 notes are played by 3 different patterns. The notes are C (whole), E (3 quarters), and G (3 quarters). C starts at beginning of measure 1, E at middle of 1, and G 3/4 inside measure 1. The three notes are overlapping in time.


In the first pattern the durations are written numerically, h = /0.5, q = /0.25, etc.


In the second patterns, the durations are written using letters, /0.75 = qqq = hq.


In the third pattern, we use three channels to play each note individually. We also save the files as midi, to check visually, in the piano roll, the notes are identical in a DAW.


package jfugue15;

import java.io.File;
import java.io.IOException;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import org.jfugue.midi.MidiFileManager;
import org.jfugue.pattern.Pattern;
import org.jfugue.player.Player;

public class JFugue15 extends Application {
    
    public static void main(String[] args) {
        launch(args);
    }
    
    TextArea text;
    String[] patts = 
        {"C/1.0 @0.5 E/0.75 @0.75 G/0.75",
         "Cw @0.5 Ehq @0.75 Ghq",
         "V0 Cw V1 Rh Ehq V2 Rhq Ghq"};
    Button[] buttons = new Button[patts.length];
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        
        int w = 250, h = 100;
        for (int i = 0; i<buttons.length; i++){
            String str = String.valueOf(i);
            buttons[i] = new Button("Pattern " + i);
            buttons[i].setOnAction( e-> example(str));
            buttons[i].setPrefSize(w,h);
            buttons[i].setFont(Font.font("Verdana", 16));
        }
        
        VBox examples = new VBox(10, buttons);
        examples.setPadding(new Insets(10));
        
        text = new TextArea();
        text.setPrefRowCount(20);
        text.setPrefColumnCount(32);
        text.setFont(Font.font("Verdana", 20));
        text.setEditable(false);
        text.setWrapText(true);
        
        HBox root = new HBox(50,examples,text);
        
        Scene scene = new Scene(root, 900, 600);
        primaryStage.setTitle("JFugue 15. Absolute Time 2");
        
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    private void example(String string) {
        
        int i = Integer.valueOf(string);
        Player player = new Player();
        Pattern pattern = new Pattern(patts[i]);
        text.appendText("\n" + (i+1) +". Pattern:\t" + pattern);
        try {
            MidiFileManager
                    .savePatternToMidi(pattern,
                            new File("JFugue15_" + string + ".mid"));
        } catch (IOException ex) {
        }
        player.play(pattern);
    }
}

This is the output after the 3 patterns are played:


Tuesday, July 12, 2016

JFugue 14. Absolute Time 1

In JFugue we can refer to absolute time such as 1.5, which indicates middle of measure 2. measure 1 starts at 0. We have to use @ to indicate next value is absolute time.


Here we use @0.0 to indicate next note starts at measure 1 start. We do not have to indicate where rests are, as the note properties are enough to provide all details for the timing.


package jfugue14;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import org.jfugue.pattern.Pattern;
import org.jfugue.player.Player;

public class JFugue14 extends Application {
    
    public static void main(String[] args) {
        launch(args);
    }
    
    TextArea text;
    String[] bText = 
        {"Using +",
         "Using @",
         "Using @, root\nlargest velocity",
         "Using @, root\nsmallest velocity",
         "Using @, root\nlargest duration",
         "Using @, root\nsmallest duration"};
    Button[] buttons = new Button[bText.length];
    String[] patts = 
        {"C+E+G",
         "@0.0 C @0.0 E @0.0 G",
         "@0.0 Ca74 @0.0 E @0.0 Ga54",
         "@0.0 Ca54 @0.0 E @0.0 Ga74",
         "@0.0 Ch @0.0 E @0.0 G",
         "@0.0 C @0.0 E @0.0 Gh"};
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        
        int w = 250, h = 100;
        for (int i = 0; i<buttons.length; i++){
            String str = String.valueOf(i);
            buttons[i] = new Button(bText[i]);
            buttons[i].setOnAction( e-> example(str));
            buttons[i].setPrefSize(w,h);
            buttons[i].setFont(Font.font("Verdana", 16));
        }
        
        VBox examples = new VBox(10, buttons);
        examples.setPadding(new Insets(10));
        
        text = new TextArea();
        text.setPrefRowCount(20);
        text.setPrefColumnCount(32);
        text.setFont(Font.font("Verdana", 20));
        text.setEditable(false);
        text.setWrapText(true);
        
        HBox root = new HBox(50,examples,text);
        
        Scene scene = new Scene(root, 900, 600);
        primaryStage.setTitle("JFugue 14. Absolute Time");
        
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    private void example(String string) {
        
        int i = Integer.valueOf(string);
        Player player = new Player();
        Pattern pattern = new Pattern(patts[i]);
        text.appendText("\n" + (i+1) +". Pattern:\t" + pattern);
        player.play(pattern);
    }
}

This is the output after the 6 buttons have been clicked:


JFugue 13. Saving and Loading Pattern

We can save a pattern to a text file or read a pattern from a text file.


The file JFugue13.staccato is saved to the Project folder. If you open it, it will have the comment indicated below.


Because it is a text file it is easy to view and modify in text editor. Further it is possible to use another language such as Python to create the text file.


package jfugue13;

import java.io.File;
import java.io.IOException;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import org.jfugue.pattern.Pattern;
import org.jfugue.player.Player;

public class JFugue13 extends Application {
    
    public static void main(String[] args) {
        launch(args);
    }
    
    TextArea text;
    Button button;
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        
        button = new Button("Save and Load\n   Pattern");
        button.setOnAction(e->example());
        button.setPrefWidth(250);
        button.setFont(Font.font("Verdana", 20));
        
        VBox examples = new VBox(10, button);
        examples.setPadding(new Insets(10));
        
        text = new TextArea();
        text.setPrefRowCount(20);
        text.setPrefColumnCount(32);
        text.setFont(Font.font("Verdana", 20));
        text.setEditable(false);
        text.setWrapText(true);
        
        HBox root = new HBox(50,examples,text);
        
        Scene scene = new Scene(root, 900, 600);
        primaryStage.setTitle("JFugue 13. Saving and Loading Pattern");
        
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    private void example() {
        
        Player player = new Player();
        String patt = "C5q Rq D5q Rq E5h Rq C4h";
        Pattern pattern1 = new Pattern(patt);
        try {
            pattern1.save(new File("JFugue13.staccato"), "Test pattern");
        } catch (IOException ex) {
        }
        
        text.setText("\nSaved Pattern:\n\n" + pattern1);
        
        Pattern pattern2 = null;
        try {
            pattern2 = Pattern.load(new File("JFugue13.staccato"));
        } catch (IOException ex) {
        }
        
        text.appendText("\n\nLoaded Pattern:\n\n" + pattern2);
        
        Pattern pattern = new Pattern();
        pattern.add(pattern1.setVoice(0));
        pattern.add(pattern2.setVoice(1).setInstrument("TRUMPET"));
        text.appendText("\n\nCombined Pattern:\n\n" + pattern);
        
        player.play(pattern);
    }
}

This is the output. The loaded file, containing same notes, as the saved one, is played as Trumpet alongside the default Piano.


Monday, July 11, 2016

JFugue 12. Parser

We can parse a MIDI file, a Staccato String, etc.


Here we parse a Staccato String (converted to Pattern). Thus we use a StaccatoListener. We can always use a StaccatoListener as we can always convert a MIDI file (or another format) into a Pattern.


This program has two Java files. This is JFugue12a, which is a subclass of ParserListenerAdapter. We only override its onNoteParsed which gets individual notes. Here we count the number of notes belonging to a family such as "C", any octave. To find instrument information, etc. we can override another method. All the methods have empty implementations in the superclass.


package jfugue12;

import org.jfugue.parser.ParserListenerAdapter;
import org.jfugue.theory.Note;

class JFugue12a extends ParserListenerAdapter {
    private int[] counter = new int[12];
    
    @Override
    public void onNoteParsed(Note note) {
        if (note.isRest()) return;
        counter[note.getPositionInOctave()]++;
    }
    
    String getCounter() {
        StringBuilder sb = new StringBuilder();
        for (byte i = 0; i<counter.length; i++) {
            sb.append("\n");
            sb.append(Note.getToneStringWithoutOctave(i));
            sb.append(":\t");
            sb.append(counter[i]);
        }
        return sb.toString();
    }
}

This is the main file which parses the Pattern.


Once the Pattern is parsed, we call getCounter() method of JFugue12a, to get String to display listing the counts.


package jfugue12;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import org.jfugue.pattern.Pattern;
import org.jfugue.realtime.RealtimePlayer;
import org.staccato.StaccatoParser;

public class JFugue12 extends Application {
    
    public static void main(String[] args) {
        launch(args);
    }
    
    RealtimePlayer player;
    TextArea text;
    Button button;
    
    @Override
    public void stop() {
        player.close();
    }
    @Override
    public void start(Stage primaryStage) throws Exception {
        
        button = new Button("Parse");
        button.setPrefSize(150, 100);
        button.setOnAction(e->example());
        button.setFont(Font.font("Verdana", 20));
        
        player = new RealtimePlayer();
        
        VBox examples = new VBox(10, button);
        examples.setPadding(new Insets(10));
        
        text = new TextArea();
        text.setPrefRowCount(20);
        text.setPrefColumnCount(32);
        text.setFont(Font.font("Verdana", 20));
        text.setEditable(false);
        text.setWrapText(true);
        
        HBox root = new HBox(50,examples,text);
        
        Scene scene = new Scene(root, 900, 600);
        primaryStage.setTitle("JFugue 12. Parser");
        
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    private void example() {
        String patt = "C5q Rq D5q Rq E5h Rq C4h";
        Pattern pattern = new Pattern(patt);
        
        StaccatoParser parser = new StaccatoParser();
        JFugue12a listener = new JFugue12a();
        parser.addParserListener(listener);
        parser.parse(pattern);
        
        text.setText("\nWe have these Notes:\n\n");
        text.appendText(listener.getCounter());
        
        player.play(pattern);
    }
}

This is the output:


JFugue 11. Chord Progression

We can use ChordProgression class to indicate scale degree.


The roman numeral and the key will get the chords which we can get using getPattern() method. We do not have to explicitly call the getPattern in the play() method.


You can quickly play the chords moving the mouse, from one button to another. We don't click as those events are not monitored via callbacks.


package jfugue11;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import org.jfugue.realtime.RealtimePlayer;
import org.jfugue.theory.ChordProgression;

public class JFugue11 extends Application {
    
    public static void main(String[] args) {
        launch(args);
    }
    
    RealtimePlayer player;
    TextArea text;
    Button button1, button2;
    
    @Override
    public void stop() {
        player.close();
    }
    @Override
    public void start(Stage primaryStage) throws Exception {
        
        button1 = new Button("I IV V");
        button2 = new Button("ii iii vi");
        
        button1.setPrefSize(150, 100);
        button2.setPrefSize(150, 100);
        
                
        button1.setOnMouseEntered(e->example1());
        button2.setOnMouseEntered(e->example2());
        
        player = new RealtimePlayer();
        
        VBox examples = new VBox(10, button1, button2);
        examples.setPadding(new Insets(10));
        
        text = new TextArea();
        text.setPrefRowCount(20);
        text.setPrefColumnCount(32);
        text.setFont(Font.font("Verdana", 20));
        text.setEditable(false);
        text.setWrapText(true);
        
        HBox root = new HBox(50,examples,text);
        
        Scene scene = new Scene(root, 900, 600);
        primaryStage.setTitle("JFugue 11. Chord Progression");
        
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    private void example1() {
        ChordProgression cp = new ChordProgression(button1.getText())
                .setKey("D");
        text.appendText("\n" + cp.getPattern());
        player.play(cp);
    }
    
    private void example2() {
        ChordProgression cp = new ChordProgression(button2.getText())
                .setKey("D");
        text.appendText("\n" + cp.getPattern());
        player.play(cp);
    }
}

This is the output:


Saturday, July 9, 2016

JFugue 10. MIDI Messages

The reason we use JFugue, rather than dealing with MIDI messages, is so we can let JFugue deal with the low-level details of MIDI file structure. We create a pattern of four notes: C (quarter note), D (quarter note), E (half note), F (half note) each with a Rest of quarter-note between them.


The resulting MIDI file (JFugue10.mid) is 65 bytes. This is broken down as:


1. header (14 bytes). 'MThd' (4 bytes of ASCII), length of 00 00 00 06 (4 bytes), format (2 byte), ntrks (2 byte), division (2 bytes). The format = 00 00 = 0, ntrks = 00 01 = 1 track, division = 00 80 = 128 Pulses per Quarter Note


2. Track 1 header (8 bytes) 'MTrk' (4 bytes of ASCII code), and length of 00 00 00 2b (4 bytes). The length indicates that there will be 2b (hex) = 43 (dec) bytes of data to follow.


3. 43 bytes of Channel 1. The MIDI messages are printed by program (27 bytes). The other (16 bytes) are delta time signals. There are distinct values here, besides 0-time, 81 00 (VLQ) = 128, and 82 00 (VLQ) = 256. VLQ stands for Variable Length Quantity. Here 128 will denote quarter-note duration and 256 denotes half-note duration.


This is shown in this hex dump (I used Notepad++ with the Hex plug in). The annotations refer to the MIDI messages of Note on and Note off. The note off velocity is usually ignored. The last 3 bytes are end of track message FF 2F 00 or 255, 47, 0 (decimal).



package jfugue10;

import java.io.File;
import java.io.IOException;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javax.sound.midi.Sequence;
import javax.sound.midi.Track;
import org.jfugue.midi.MidiFileManager;
import org.jfugue.pattern.Pattern;
import org.jfugue.player.Player;

public class JFugue10 extends Application {
    
    public static void main(String[] args) {
        launch(args);
    }
    
    TextArea text;
    Button button;
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        
        button = new Button("MIDI Messages");
                
        button.setOnAction(e->example());
                
        VBox examples = new VBox(10, button);
        examples.setPadding(new Insets(10));
        
        text = new TextArea();
        text.setPrefRowCount(20);
        text.setPrefColumnCount(32);
        text.setFont(Font.font("Verdana", 20));
        text.setEditable(false);
        text.setWrapText(true);
        
        HBox root = new HBox(50,examples,text);
        
        Scene scene = new Scene(root, 900, 600);
        primaryStage.setTitle("JFugue 10. MIDI Messages");
        
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    private void example() {
        Player player = new Player();
        Pattern pattern = 
                new Pattern("Cqa80 R Dqa90 R Eha100 R Fha110d99");
        text.setText("\n\npattern: " + pattern +
                "\n\n\tChannel 1 MIDI Messages");
        Sequence sequence = player.getSequence(pattern);
        Track[] track = sequence.getTracks();
        String status;
        int c = 0;
        int value;
        for (int i = 0; i<track[0].size(); i++) {
            byte[] bArr = track[0].get(i).getMessage().getMessage();
            for (int j = 0; j<bArr.length; j++) {
                value = 0xff & (int) bArr[j];
                switch (value) {
                    case 128: 
                        status = "\tNone Off, "
                                + "Pitch and Velocity Off Follow";
                        break;
                    case 144: 
                        status = "\tNote On, "
                                + "Pitch and Velocity On Follow"; 
                        break;
                    default:
                        status = "";
                }
                text.appendText("\n " + c + ". " + value + status);
                c++;
            }
        }
        try {
            MidiFileManager
                    .savePatternToMidi(pattern,
                            new File("JFugue10.mid"));
        } catch (IOException ex) {
        }
        player.play(pattern);
    }
}

This is the output: