Monday, July 25, 2016

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:


No comments:

Post a Comment