如何使用Java解析midi的节奏? [英] How can I parse a tempo of midi using Java?

查看:533
本文介绍了如何使用Java解析midi的节奏?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不知道如何掌握节奏. 因此,仍请使用一些卑鄙的方式对我的输出使用速度.

I do not understand how to catch tempo. So still use some shitty way to use tempo to my output.

我真的认为仅使用NOTE_ON& NOTE_OFF时间会给我实时的信息. 但是此输出在C ++中的播放速度仍然太慢.

I really thinks that just using NOTE_ON & NOTE_OFF timings will give me real time. But this output still played too slow in C++.

P.S.
播放时,我们仅使用ONE VOID Midis. (这只是为了好玩,我们教室中的某些计算机正在同步播放2种以上的VOICE音乐).

P.S.
We use only ONE VOICE midis when playing that. (It's just for fun, some computers in our class room is playing some 2+ VOICE music synchronized).

这是我的代码:

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import javax.sound.midi.*;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import static java.lang.Math.*;

public class MidiReader {
    public static final float DEFAULT_TEMPO = 100.0f;
    public static final int NOTE_ON = 0x90;
    public static final int NOTE_OFF = 0x80;
    public static final float[] NOTES = {32.70f, 34.65f, 36.95f, 38.88f, 41.21f, 43.65f,
            46.25f, 49.00f, 51.90f, 55.00f, 58.26f, 61.74f};
    private JFrame frame = new JFrame();
    private JTextArea outText = new JTextArea();
    private JPanel panel = new JPanel();
    private File inputFile = null;
    private JButton button = new JButton("Choose file");
    private JTextField inputTempo = new JTextField(Integer.toString((int) DEFAULT_TEMPO));
    private float tempo = DEFAULT_TEMPO;

    public void init(){
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(300, 400);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        frame.setLayout(new BorderLayout());
        panel.setLayout(new BorderLayout());
        panel.add(outText, BorderLayout.CENTER);
        panel.add(inputTempo, BorderLayout.SOUTH);
        frame.add(panel, BorderLayout.CENTER);
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JFileChooser fileopen = new JFileChooser();
                FileNameExtensionFilter filter = new FileNameExtensionFilter(
                        "Midi files", "mid");
                fileopen.setFileFilter(filter);
                int ret = fileopen.showDialog(null, "Choose File");
                if (ret == JFileChooser.APPROVE_OPTION) {
                    inputFile = fileopen.getSelectedFile();
                }
                if (inputFile != null) {
                    setTempo(Float.parseFloat(inputTempo.getText()));
                    outText.setText("");
                    calculate(getTempo());
                }
            }
        });
        inputTempo.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                setTempo(Float.parseFloat(inputTempo.getText()));
                outText.setText("");
                calculate(getTempo());
            }
        });
        frame.add(button, BorderLayout.SOUTH);
        frame.setTitle("Midi to C++ Beep");

    }

    public void setTempo(float tempo) {
        this.tempo = tempo;
    }

    public float getTempo(){
        return this.tempo;
    }

    public float getPitch(int key){
        return NOTES[key % 12] * (float) (pow(2.0, (key / 12) - 2));
    }

    public MidiReader(){
        init();
    }


    public void calculate(float tempo){
        Sequence sequence = null;
        try {
            sequence = MidiSystem.getSequence(inputFile);
        } catch (InvalidMidiDataException e) {
            System.exit(0);
        } catch (IOException e) {
            e.printStackTrace();
        }

        for (Track track :  sequence.getTracks()) {
            int key;
            long startTime = 0;
            long stopTime;
            for (int i = 0; i < track.size(); i++) {
                MidiEvent event = track.get(i);
                MidiMessage message = event.getMessage();
                if (message instanceof ShortMessage) {
                    ShortMessage sm = (ShortMessage) message;
                    switch(sm.getCommand()){
                        case NOTE_ON:
                            startTime = event.getTick();
                            break;
                        case NOTE_OFF:
                            stopTime = event.getTick();
                            key = sm.getData1();
                            outText.append(
                                "\t" + "Beep(" + getPitch(key) + ", " +
                                (int)((stopTime - startTime) * (DEFAULT_TEMPO / tempo)) + ");" + "\n");
                            break;
                    }
                }
            }
        }
    }

    public static void main(String[] args){
    new MidiReader();
}
}

推荐答案

MIDI文件使用两个值来指定时间,即 resolution (以每四分音符的滴答度数为单位)和速度(以每四分音符微秒为单位).

MIDI files use two values to specify the timing, the resolution (measured in ticks per quarter note), and the tempo (measured in microseconds per quarter note).

可以从序列.

用MIDI信息指定速度.您必须在第一首曲目中搜索SET_TEMPO元事件,该事件会更改所有后续事件的速度,直到下一个速度事件为止.

The tempo is specified with MIDI messages; you have to search the first track for SET_TEMPO meta events, which change the tempo for all following events until the next tempo event.

另请参见如何获取MetaMessage.getData()返回的字节数组中的整数值?使用NAudio从MIDI文件中读取音符.

这篇关于如何使用Java解析midi的节奏?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆