マイペースなプログラミング日記

DTMやプログラミングにお熱なd-kamiがマイペースに書くブログ

エントリ数を稼ぐためにソースコードを貼り付けるd-kami

まぁ、何か作ってますよ程度のもの。Java FXとJava Sound APIを使って遊んでるだけです

import javafx.stage.Stage;
import javafx.application.Application;

import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.animation.Animation;

import javafx.util.Duration;

import java.io.File;
import java.util.List;

import javax.xml.bind.JAXB;

import javax.sound.midi.Receiver;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.ShortMessage;

import net.d_kami.midi.data.MidiNote;
import net.d_kami.midi.data.MidiTrack;
import net.d_kami.midi.data.MidiMusic;

import javax.sound.midi.InvalidMidiDataException;
 
public class MidiTest extends Application {
    private Receiver receiver;
    private Timeline timer;

    private int timerCount = 0;
    
    private int bassIndex = 0;
    private int drumIndex = 0;

    private MidiMusic music;
    
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) throws Exception {
        receiver = MidiSystem.getReceiver();

        music = JAXB.unmarshal(new File("midi.xml"), MidiMusic.class);
        
        Button button = createMidiButton("8ビート!");
        BorderPane pane = new BorderPane();
        pane.setCenter(button);
        Scene scene = new Scene(pane, 320, 120);
        stage.setScene(scene);
        stage.show();
    }
    
    private Button createMidiButton(String text){
        loadProgram();
        setTempo(120);

        return createButton(text);
    }
    
    private Button createButton(String text){
        Button button = new Button(text);
        
        button.setOnAction(e -> {
            if(timer.getStatus() == Animation.Status.STOPPED){
                timer.setCycleCount(160);
                timer.play();
            }
        });
        
        return button;
    }
    
    private void setTempo(int bpm){
        int duration = 60000 / bpm * 4 / 128;
        
        setTimer(duration);
    }
    
    private void setTimer(int duration){
        List<MidiTrack> tracks = music.getTracks();
        int[] indices = new int[tracks.size()];
        
        timer = new Timeline(new KeyFrame(Duration.millis(duration), event -> {
            try{
                for(int i = 0; i < tracks.size(); i++){
                    MidiTrack track = tracks.get(i);
                    midiEvent(track, duration, indices, i);
                }
            }catch(InvalidMidiDataException ex){
                ex.printStackTrace();
            }
            
            timerCount++;
        }));
    }
    
    private void loadProgram(){
        music.getTracks().forEach(track -> {
            try{
                programChange(track.getNumber(), track.getProgram());
            }catch(InvalidMidiDataException e){
                e.printStackTrace();
            }
        });
    }
    
    private void programChange(int trackNumber, int program) throws InvalidMidiDataException{
        ShortMessage message = new ShortMessage();
        message.setMessage(ShortMessage.PROGRAM_CHANGE, trackNumber, program, 0);
        receiver.send(message, -1);
    }
    
    private void midiEvent(MidiTrack track, int duration, int[] indices, int i) throws InvalidMidiDataException{
        List<MidiNote> notes = track.getNotes();
        
        if(indices[i] < notes.size()){
            MidiNote note = notes.get(indices[i]);
            
            if(duration * timerCount > note.getPosition()){
                note(track.getNumber(), note.getType(), note.getNote(), note.getVelocity());
                indices[i]++;
            }
        }
    }
    
    private void note(int trackNumber, int type, int note, int velocity) throws InvalidMidiDataException{
        ShortMessage message = new ShortMessage();
        message.setMessage(type, trackNumber, note, velocity);
        receiver.send(message, -1);
    }
}