Package org.jfugue

Source Code of org.jfugue.TimeFactor

/*
* JFugue - API for Music Programming
* Copyright (C) 2003-2008  David Koelle
*
* http://www.jfugue.org
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

package org.jfugue;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.sound.midi.MetaMessage;
import javax.sound.midi.MidiEvent;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.Sequence;

public final class TimeFactor
{
    public static double DEFAULT_BPM = 120.0d;
    public static int QUARTER_DURATIONS_IN_WHOLE = 4;
   
    public static final double getTimeFactor(Sequence sequence, double bpm)
    {
        double divisionType = sequence.getDivisionType();
        double resolution = sequence.getResolution();

        // If division type is PPQ, resolution is ticks per beat.
        // Since a beat is the length of time given to a one quarter note, this essentially
        // means that ticks per beat == pulses per quarter note (PPQ or PPQN)
        if (divisionType == Sequence.PPQ)
        {
//            System.out.println("DivisionType is PPQ");
//            System.out.println("Resolution is "+resolution);
        } else {
//            System.out.println("DivisionType is SMPTE");
        }
        // Useful resources: http://www.borg.com/~jglatt/tech/midifile/tempo.htm and http://www.borg.com/~jglatt/tech/midifile/ppqn.htm
       
//        If bit 15 of division is a zero, the bits 14 thru 0 represent the number of delta-time ticks which make up a
//        quarter-note. For instance, if division is 96, then a time interval of an eighth-note between two events
//        in the file would be 48.
//
//        If bit 15 of division is a one, delta-times in a file correspond to subdivisions of a second, in a way consistent
//        with SMPTE and MIDI time code. Bits 14 thru 8 contain one of the four values -24, -25, -29, or -30, corresponding
//        to the four standard SMPTE and MIDI time code formats (-29 corresponds to 30 drop frame), and represents the number
//        of frames per second. These negative numbers are stored in two's complement form. The second byte (stored positive)
//        is the resolution within a frame: typical values may be 4 (MIDI time code resolution), 8, 10, 80 (bit resolution),
//        or 100. This system allows exact specification of time- code-based tracks, but also allows millisecond-based tracks
//        by specifying 25 frames/sec and a resolution of 40 units per frame. If the events in a file are stored with bit
//        resolution of thirty-frame time code, the division word would be E250 hex.        

        if (bpm == 0.0)
        {
            bpm = DEFAULT_BPM;
        }
       
        return 60000.0 / (resolution * bpm);
    }
   
    public static final byte[] convertToThreeTempoBytes(int tempo)
    {
        double tempoInMsPerBeat = TimeFactor.convertBPMToMicrosecondsPerBeat(tempo);
        double d1 = Math.floor(tempoInMsPerBeat / 16384.0);
        double d2 = Math.floor((tempoInMsPerBeat % 16384.0) / 128.0);
        double d3 = Math.floor((tempoInMsPerBeat % 16384.0) % 128.0);
        return new byte[] { (byte)d1, (byte)d2, (byte)d3 };
    }
   
    public static final int parseMicrosecondsPerBeat(MetaMessage message, long timestamp)
    {
        int tempo = message.getData()[0]*16384 + message.getData()[1]*128 + message.getData()[2];
        int beatsPerMinute = (int)convertMicrosecondsPerBeatToBPM(tempo);
        return beatsPerMinute;
    }
   
    /** Converts microseconds per beat to BPM -- and vice versa */
    public static final double convertMicrosecondsPerBeatToBPM(double value) {
        double microsecondsPerMinute = 60000000.0D;
        if (value == 0.0d) {
            return 0.0d;
        }
        return microsecondsPerMinute / value;
    }
   
    /** Converts microseconds per beat to BPM -- and vice versa */
    public static final double convertBPMToMicrosecondsPerBeat(int bpm) {
        double microsecondsPerMinute = 60000000.0D;
        if (bpm == 0) {
            return 0;
        }
        return microsecondsPerMinute / bpm;
    }
   
    /**
     * Takes all of the MIDI events in the given Sequence, sorts them according to
     * when they are to be played, and sends the events to the MidiMessageRecipient
     * when the each event is ready to be played.
     *
     * @param sequence The Sequence with messages to sort and deliver
     * @param recipient the handler of the delivered message
     */
    public static final void sortAndDeliverMidiMessages(Sequence sequence, MidiMessageRecipient recipient)
    {
        double timeFactor = 1.0;
       
        Map<Long, List<MidiEvent>> timeMap = new HashMap<Long, List<MidiEvent>>();
        long longestTime = TimeEventManager.sortSequenceByTimestamp(sequence, timeMap);
       
        long lastTime = 0;
        for (long time=0; time < longestTime; time++)
        {
            List<MidiEvent> midiEventList = (List<MidiEvent>)timeMap.get(time);
            if (midiEventList != null)
            {
                for (MidiEvent event : midiEventList)
                {
                    MidiMessage message = event.getMessage();
                    if ((message.getMessage().length >= 2) && (message.getMessage()[1] == 0x51) && (message instanceof MetaMessage))
                    {
                        int bpm = parseMicrosecondsPerBeat((MetaMessage)message, time);
                        timeFactor = TimeFactor.getTimeFactor(sequence, bpm);
                        System.out.println("TimeFactor is "+timeFactor);
                    }
                    recipient.messageReady(message, time);
                }

                try
                {
                    long sleepTime = (int)(((time - lastTime) * (TimeFactor.QUARTER_DURATIONS_IN_WHOLE+0.20)));
                    Thread.sleep(sleepTime); // (int) (1 * timeFactor));
                    lastTime = time;
                } catch (Exception ex)
                {
                    throw new JFugueException(JFugueException.ERROR_SLEEP);
                }
            }
        }
    }
}
TOP

Related Classes of org.jfugue.TimeFactor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.