Package miniseq

Source Code of miniseq.Miniseq

package miniseq;

import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.util.Iterator;
import java.util.TreeSet;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.Timer;
import java.util.TimerTask;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MidiChannel;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Receiver;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.Synthesizer;
import static javax.sound.midi.ShortMessage.*;


public class Miniseq {


    private Synthesizer synthesizer;
    private Receiver receiver;
    private List<PlayTask> playTasks;
    private List<Integer> unusedChannels; /// for deciding on which midi channel new lines are played on
    private boolean play = false;   // is sequencer playing?
   
    public void setSynthesizer(Synthesizer sy)
    {
        this.synthesizer = sy;
    }

    public Synthesizer getSynthesizer()
    {
        return synthesizer;
    }

    public void setReceiver(Receiver receiver)
    {
        this.receiver = receiver;
    }

    public Receiver getReceiver()
    {
        return receiver;
    }

    public boolean isPlaying()
    {
        return play;
    }
   
   
    public synchronized void init() throws MidiUnavailableException
    {
        if (unusedChannels != null)
            return;
        if (synthesizer == null)
            synthesizer = MidiSystem.getSynthesizer();
        if (!synthesizer.isOpen())
            synthesizer.open();
        MidiChannel[] chn = synthesizer.getChannels();
        playTasks = new ArrayList<PlayTask>();
        if (receiver == null)
            receiver = synthesizer.getReceiver();
        unusedChannels = new ArrayList<Integer>(chn.length);
        for(int i = 0; i < chn.length; i++)
            if (i != 9)
                unusedChannels.add(i);
    }

    public void play() throws MidiUnavailableException
    {
        if (play)
            return;
        play = true;
        if (playTasks == null)
            init();
        synchronized(playTasks)
        {
            for(PlayTask th : playTasks)
                th.timer.schedule(th.newTask(), 10L);
        }
    }

    public void pause()
    {
        if (!play)
            return;
        play = false;
        for(MidiChannel c : synthesizer.getChannels())
            if (c != null)
                c.allNotesOff();
    }

    public void stop()
    {
        play = false;
        synchronized(playTasks)
        {
            for(PlayTask th : playTasks)
                th.playQueue.clear();
        }
        for(MidiChannel c : synthesizer.getChannels())
            if (c != null)
                c.allNotesOff();
    }

    public boolean isPlayingNotes()
    {
        return play && playTasks.size() > 0 && playTasks.get(0).playQueue.size() > 0;
    }

    public void fadeout(int quarterNotes)
    {
        for(PlayTask th : playTasks)
            fadeout(th, quarterNotes);
    }
   
    private void fadeout(PlayTask th, int quarterNotes)
    {
        int off = 0;
        float subst = 1f / (quarterNotes * 32f);
        synchronized(th.playQueue)
        {
            Iterator<MidimsgHolder> i = th.playQueue.iterator();
            MidimsgHolder last = null;
            for(float factor = 1f; i.hasNext(); )
            {
                MidimsgHolder d = i.next();
                if (d.msg != null)
                {
                    byte[] m = d.msg.getMessage();
                    if ((m[0] & 0xFF) >> 4 == 0x09)
                    {
                        float fv = (float)(m[2] & 0xFF) * factor;
                        m[2] = (byte)fv;
                        ShortMessage m2 = new ShortMessage();
                        try {
                            m2.setMessage(NOTE_ON, m[0] & 0x0F, (int)(m[1] & 0xFF), (int)(m[2] & 0xFF));
                            d.msg = m2;
                        } catch(InvalidMidiDataException ex)
                        {
                            ex.printStackTrace();
                        }

                    }
                }
                if (last != null)
                    factor -= (d.offset - last.offset) * subst;
                if (factor < 0f)
                    factor = 0f;
                last = d;
            }
        }
    }

    private static class PlayNode extends TimerTask
    {
        private PlayNode(Runnable r)
        {
            this.run = r;
        }
        private Runnable run;
        @Override
        public void run() {
            run.run();
        }

    }
    /** holder for state information about a timer playing at a specific tempo.
     *  This is an instance class to access Miniseq members without ado.
     *  A PlayNode, a lightweight object, is constructed during each step of the
     * sequencer.
     */
    private class PlayTask implements Runnable
    {
        private long playOffset = 0L;
        // bpm
        private float tempo = 160.0f;
        // queue of events to play
        private ArrayDeque<MidimsgHolder> playQueue = new ArrayDeque<MidimsgHolder>();;
        // for fragments defined in the input sequence
        private Map<Integer, TreeSet<MidimsgHolder>> seqmem = new HashMap<Integer, TreeSet<MidimsgHolder>>();
        private Timer timer = new Timer();

        private TimerTask newTask()
        {
            return new PlayNode(this);
        }

        @Override
        public void run()
        {
            MidimsgHolder msg = null;
            if (playQueue.size() > 0)
            {
                synchronized(playQueue)
                {
                    msg = playQueue.getFirst();
                }
            }
            while(msg != null && msg.offset <= playOffset)
            {
                if (msg.newTempo != null)
                {
                    tempo = msg.newTempo;
                }
                else
                {
                    if (msg.offset == playOffset)
                        receiver.send(msg.msg, -1);
                }
                synchronized(playQueue)
                {
                    playQueue.removeFirst();
                    if (playQueue.size() > 0)
                        msg = playQueue.getFirst();
                    else
                        msg = null;
                }
            }
            playOffset++;
            synchronized(playQueue)
            {
                if (playQueue.size() == 0)
                {
                    playTasks.remove(this);
                    return;
                }
            }
            if (play) // continue to play if not paused meanwhile
            {
                timer.schedule(newTask(), (long)(60000f / tempo / 32f));
            }
        }
    }
   
    private static int[] baseNote = new int[] // from middle C = 60
        { 60, 62, 64, 65, 67, 69, 70, 71, 73, 75};

    public void readFrom(FeatureInputStream in) throws IOException, InvalidMidiDataException
    {
        readFrom(in, 0L);
    }
   
    public void readFrom(FeatureInputStream in, long startOffset) throws IOException, InvalidMidiDataException
    {
        PlayTask th = new PlayTask();
        float tempo = 160f;
        char ch = in.readChar();
        TreeSet<MidimsgHolder> lst = new TreeSet<MidimsgHolder>();
        while (ch != 0xFFFF)
        {
            long dstoff = 0L;
            int channel;
            synchronized(unusedChannels)
            {
                channel = unusedChannels.remove(0); // channel for new notes
                unusedChannels.add(channel);
            }
            int vol = 64;   // base volume for new notes (0..127)
            TreeSet<MidimsgHolder> memslot = null;
            long memOnly = -1;
            while (ch != '\n' && ch != '\\' && ch != 0xFFFF)
            {
                boolean incOffset = true;
                switch(ch)
                {
                    case '/':
                        if (in.peekChar() == '*') {  /* checks and reads for this kind of comment block */
                            in.readChar();
                            for(;;)
                            {
                                while ((ch = in.readChar()) != '*' && ch != 0xFFFF);
                                if ((ch = in.readChar()) == '/' || ch == 0xFFFF)
                                    break;
                            }
                        }
                        break;
                    case 'x':
                        synchronized(unusedChannels)
                        {
                            unusedChannels.remove(channel);
                            unusedChannels.add(0, channel);
                        }
                        channel = in.readInt(1, 16);
                        break;
                    case 't':
                        MidimsgHolder t = MidimsgHolder.tempo(dstoff, in.readFloat());
                        if (tempo == 160f)
                            tempo = t.newTempo;
                        if (memOnly == -1)
                            lst.add(t);
                        if (memslot != null)
                            memslot.add(t);
                        break;
                    case 'V': vol = Math.min(in.readInt(2,16), 127)break;
                    case 'R':
                        if (memOnly == -1)
                        {
                            memOnly = dstoff;
                            th.seqmem.put(in.readInt(), memslot = new TreeSet<MidimsgHolder>());
                        }
                        else
                        {
                            memslot.add(MidimsgHolder.text(dstoff, "REPEAT"));
                            memslot = null;
                            dstoff = memOnly;
                            memOnly = -1;
                        }
                        break;
                    case 'M':
                        if (memslot == null)
                            th.seqmem.put(in.readInt(), memslot = new TreeSet<MidimsgHolder>());
                        else {
                            memslot.add(MidimsgHolder.text(dstoff, "REPEAT"));
                            memslot = null;
                        }
                        break;
                    case 'p':
                        MidimsgHolder pc = new MidimsgHolder(dstoff, PROGRAM_CHANGE, channel, in.readInt(), 0);
                        if (memOnly == -1)
                            lst.add(pc);
                        if (memslot != null)
                            memslot.add(pc);
                        break;
                    case '!': // generic midi event
                        int tmp = in.peekChar();
                        if (tmp == '*') // fade
                            in.readChar();
                        Integer a = null, b = 1, step = 1, delta = 128;
                        int eventType = in.readInt(1, 16) << 4;
                        int param1 = in.readInt(2, 16);
                        if (tmp == '*') // fade
                        {
                            in.readChar()// =
                            a = in.readInt(2, 16);
                            in.readChar()// >
                            b = in.readInt(2, 16);
                            try {
                                step = in.readInt();
                            } catch(IOException e) { };
                            try {
                                delta = in.readInt();
                            } catch(IOException e) { };
                        } else
                        {
                            a = in.readInt(2, 16);
                            b = a;
                        }
                        int d = 0;
                        for (int x = a; step < 0 ? x >= b : x <= b; x += step, d += delta)
                        {
                            MidimsgHolder hh = new MidimsgHolder(dstoff + d, eventType, channel, param1, x);
                            if (memOnly == -1)
                                lst.add(hh);
                            if (memslot != null)
                                memslot.add(hh);
                        }
                        break;
                    case '.'// stop
                        if (memslot != null && memslot.size() == 0)
                            memslot.add(MidimsgHolder.text(dstoff, ".")); // record correct start moment
                        int length = readLength(in);
                        if (in.peekChar() == '*')
                        {
                            in.readChar();
                            int qty2 = in.readInt();
                            for(int i = 0; i < qty2; i++)
                                dstoff += length;
                        } else
                            dstoff += length;
                        break;
                    case '<':
                        incOffset = false;
                        ch = in.readChar();
                        if (ch < 'c' || ch > 'h')
                            break;
                    case 'c'case 'd'case 'e'case 'f'case 'g'case 'a'case 'b'case 'h':
                        int add = (ch=='a'||ch=='b') ? 'c' - 7 : 'c';
                        int base = baseNote[ch - add];
                        int dur = 0;
                        int velo = vol;
                        int qty = 1;
                        boolean ok = true;
                        while (ok) {
                            int mdf = in.peekChar();
                            switch(mdf) {
                                case '#': base++;  in.readChar()break;
                                case '-': base--;  in.readChar()break;
                                case 'v': in.readChar();  velo = Math.min(in.readInt(2,16), 127)break;
                                case '0'case '1'case '2'case '3'case '4'case '5'case '6'case '7'case '8'case '9':
                                    base += (mdf - '5') * 12
                                    in.readChar();
                                    break;
                                case 'I'case 'H'case 'G'case 'F'case 'E'case 'D'case 'C'case 'B'case 'A':
                                    dur += readLength(in);
                                    break;
                                case '*': in.readChar();  qty = in.readInt()break;
                                default:
                                    ok = false;
                                    break;
                            }
                        }
                        if (dur == 0)
                            dur = 32; // 32 ticks for 4/4
                        for(int i = 0; i < qty; i++)
                        {
                            if (memOnly == -1)
                            {
                                lst.add(new MidimsgHolder(dstoff, NOTE_ON, channel, base, velo));
                                lst.add(new MidimsgHolder(dstoff + dur, NOTE_OFF, channel, base, 0));
                            }
                            if (memslot != null)
                            {
                                memslot.add(new MidimsgHolder(dstoff, NOTE_ON, channel, base, velo));
                                memslot.add(new MidimsgHolder(dstoff + dur, NOTE_OFF, channel, base, 0));
                            }
                            dstoff += dur;
                        }
                        if (!incOffset)
                            dstoff -= dur * qty;
                        break;
                    case 'N':
                        TreeSet<MidimsgHolder> stored = th.seqmem.get(in.readInt());
                        int n = 1;
                        if (in.peekChar() == '*')
                        {
                            in.readChar();
                            n = in.readInt();
                        }
                        if (stored != null)
                        {
                            for(int i = 0; i < n; i++)
                            {
                                long diff = dstoff - stored.first().offset;
                                for(MidimsgHolder h : stored)
                                {
                                    MidimsgHolder h2 = new MidimsgHolder();
                                    h2.offset = h.offset + diff;
                                    h2.msg = h.msg;
                                    h2.newTempo = h.newTempo;
                                    if (memOnly == -1)
                                        lst.add(h2);
                                    if (memslot != null)
                                        memslot.add(h2);
                                }
                                dstoff += stored.last().offset - stored.first().offset;
                            }
                        }
                        break;
                }
                ch = in.readChar();
            }
            ch = in.readChar();
        }
        PlayTask target = th;
        synchronized(playTasks)
        {
            for(PlayTask test : playTasks)
                if (test.tempo == tempo)
                {
                    target = test;
                    target.seqmem.putAll(th.seqmem);
                }
        }
        if (target == th) // must start a new timer
        {
            synchronized(playTasks)
            {
                playTasks.add(target);
            }
            target.timer.schedule(target.newTask(), 10L);
        }
        long offset = target.playOffset + startOffset;
        for(MidimsgHolder d : lst)
            d.offset = offset + d.offset + 32L;
        if (target.playQueue.isEmpty())
        {
            synchronized(target.playQueue)
            {
                target.playQueue.addAll(lst);
            }
        }
        else
        {
            synchronized(target.playQueue)
            {
                lst.addAll(target.playQueue);
                target.playQueue.clear();
                target.playQueue.addAll(lst);
            }
        }
    }

    private int readLength(FeatureInputStream in) throws IOException {
        int ret = 32;   // 32 ticks for quarter note
        int tmp = in.peekChar();
        switch((char)tmp)
        {
            case 'I':  ret >>= 4;  in.readChar()break; // 1/64
            case 'H':  ret >>= 3;  in.readChar()break; // 1/32
            case 'G':  ret >>= 2;  in.readChar()break; // 1/16
            case 'F':  ret >>= 1;  in.readChar()break; // 1/8
            case 'E':  in.readChar(); break;              // 1/4
            case 'D':  ret <<= 1;  in.readChar()break; // 1/2
            case 'C':  ret <<= 2;  in.readChar()break; // 1/1
            case 'B':  ret <<= 3;  in.readChar()break; // 2/1
            case 'A':  ret <<= 4;  in.readChar()break; // 4/1
        }
        return ret;
    }

    private long cc = 0L;




}
TOP

Related Classes of miniseq.Miniseq

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.