©  Jan Newmarch 2017

Jan Newmarch, Linux Sound Programming, 10.1007/978-1-4842-2496-0_18

18. MIDI Java Sound

Jan Newmarch

(1)Oakleigh, Victoria, Australia

Java Sound has a well-developed MIDI system, with cleanly separated components such as sequencers and synthesizers, and it allows hooks for meta events as well as ordinary MIDI events. This chapter considers programming using the MIDI API.

Resources

Many resources are available for Java Sound.

Key Java Sound MIDI Classes

Java Sound relies on a set of classes for its MIDI support. These are standard in Java. The following are the principal classes:

Device Information

Device information is found by querying MidiSystem for its list of DeviceInfo objects. Each information object contains fields such as Name and Vendor. You can find the actual device using this information object with MidiSystem.getMidiDevice(info). The device can then be queried for its receivers and transmitters and its type as sequencer or synthesizer.

One annoying part is that you cannot get a list of all the device’s transmitters and receivers, only those that are open. You can ask for the default transmitter and receiver, which will implicitly open them. So, you can see that the list may be empty before asking for the default, but it will be nonempty afterward if there is a default! If there are no defaults, a MidiUnavailableExceptionexception will be thrown.

The program is as follows:

import javax.sound.midi.*;
import java.util.*;


public class DeviceInfo {

    public static void main(String[] args) throws Exception {
        MidiDevice.Info[] devices;


        /*
        MidiDevice.Info[] info = p.getDeviceInfo();
        for (int m = 0; m < info.length; m++) {
            System.out.println(info[m].toString());
        }
        */


        System.out.println("MIDI devices:");
        devices = MidiSystem.getMidiDeviceInfo();
        for (MidiDevice.Info info: devices) {
            System.out.println("    Name: " + info.toString() +
                               ", Decription: " +
                               info.getDescription() +
                               ", Vendor: " +
                               info.getVendor());
            MidiDevice device = MidiSystem.getMidiDevice(info);
            if (! device.isOpen()) {
                device.open();
            }
            if (device instanceof Sequencer) {
                System.out.println("        Device is a sequencer");
            }
            if (device instanceof Synthesizer) {
                System.out.println("        Device is a synthesizer");
            }
            System.out.println("        Open receivers:");
            List<Receiver> receivers = device.getReceivers();
            for (Receiver r: receivers) {
                System.out.println("            " + r.toString());
            }
            try {
                System.out.println("         Default receiver: " +
                                   device.getReceiver().toString());


                System.out.println("         Open receivers now:");
                receivers = device.getReceivers();
                for (Receiver r: receivers) {
                    System.out.println("            " + r.toString());
                }
            } catch(MidiUnavailableException e) {
                System.out.println("        No default receiver");
            }


            System.out.println("         Open transmitters:");
            List<Transmitter> transmitters = device.getTransmitters();
            for (Transmitter t: transmitters) {
                System.out.println("            " + t.toString());
            }
            try {
                System.out.println("         Default transmitter: " +
                                   device.getTransmitter().toString());


                System.out.println("         Open transmitters now:");
                transmitters = device.getTransmitters();
                for (Transmitter t: transmitters) {
                    System.out.println("            " + t.toString());
                }
            } catch(MidiUnavailableException e) {
                System.out.println("        No default transmitter");
            }
            device.close();
        }


        Sequencer sequencer = MidiSystem.getSequencer();
        System.out.println("Default system sequencer is " +
                           sequencer.getDeviceInfo().toString() +
                           " (" + sequencer.getClass() + ")");


        Synthesizer synthesizer = MidiSystem.getSynthesizer();
        System.out.println("Default system synthesizer is " +
                           synthesizer.getDeviceInfo().toString() +
                           " (" + synthesizer.getClass() + ")");


    }
}

The output on my system is as follows:

MIDI devices:
    Name: Gervill, Decription: Software MIDI Synthesizer, Vendor: OpenJDK
        Device is a synthesizer
        Open receivers:


        Default receiver: com.sun.media.sound.SoftReceiver@72f2a824

        Open receivers now:
            com.sun.media.sound.SoftReceiver@72f2a824


        Open transmitters:
        No default transmitter
    Name: Real Time Sequencer, Decription: Software sequencer, Vendor: Oracle Corporation
        Device is a sequencer
        Open receivers:


        Default receiver: com.sun.media.sound.RealTimeSequencer$SequencerReceiver@c23c5ff

        Open receivers now:
            com.sun.media.sound.RealTimeSequencer$SequencerReceiver@c23c5ff


        Open transmitters:
        Default transmitter: com.sun.media.sound.RealTimeSequencer$SequencerTransmitter@4e13aa4e


        Open transmitters now:
            com.sun.media.sound.RealTimeSequencer$SequencerTransmitter@4e13aa4e
Default system sequencer is Real Time Sequencer
Default system synthesizer is Gervill

Dumping a MIDI File

These two programs from jsresources.org dump a MIDI file to the console. The MidiSystem creates a Sequence from a file. Each track of the sequence is looped through, and each event within each track is examined. While it would be possible to print in situ, each event is passed to a Receiver object, which in this case is DumpReceiver. That object could do anything but in this case just prints the event to stdout.

The DumpSequence.java program reads a MIDI file given as a command-line argument and dumps a listing of its contents in readable form to standard output. It first gets a Sequence and prints out information about the sequence and then gets each track in turn, printing out the contents of the track.

 /*
 *      DumpSequence.java
 *
 *      This file is part of jsresources.org
 */


/*
 * Copyright (c) 1999, 2000 by Matthias Pfisterer
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */


import java.io.File;
import java.io.IOException;


import javax.sound.midi.MidiSystem;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.Sequence;
import javax.sound.midi.Track;
import javax.sound.midi.MidiEvent;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.SysexMessage;
import javax.sound.midi.Receiver;


public class DumpSequence
{
    private static String[]    sm_astrKeyNames = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"};


    private static Receiver    sm_receiver = new DumpReceiver(System.out, true);

    public static void main(String[] args) {
        /*
         *      We check that there is exactely one command-line
         *      argument. If not, we display the usage message and
         *      exit.
         */
        if (args.length != 1) {
            out("DumpSequence: usage:");
            out(" java DumpSequence <midifile>");
            System.exit(1);
        }
        /*
         *      Now, that we're shure there is an argument, we take it as
         *      the filename of the soundfile we want to play.
         */
        String  strFilename = args[0];
        File    midiFile = new File(strFilename);


        /*
         *      We try to get a Sequence object, which the content
         *      of the MIDI file.
         */
        Sequence      sequence = null;
        try {
            sequence = MidiSystem.getSequence(midiFile);
        } catch (InvalidMidiDataException e) {
            e.printStackTrace();
            System.exit(1);
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }


        /*
         *        And now, we output the data.
         */
        if (sequence == null) {
            out("Cannot retrieve Sequence.");
        } else {
            out("------------------------------------------------------------------------");
            out("File: " + strFilename);
            out("------------------------------------------------------------------------");
            out("Length: " + sequence.getTickLength() + " ticks");
            out("Duration: " + sequence.getMicrosecondLength() + " microseconds");
            out("------------------------------------------------------------------------");
            float      fDivisionType = sequence.getDivisionType();
            String     strDivisionType = null;
            if (fDivisionType == Sequence.PPQ) {
                strDivisionType = "PPQ";
            } else if (fDivisionType == Sequence.SMPTE_24) {
                strDivisionType = "SMPTE, 24 frames per second";
            } else if (fDivisionType == Sequence.SMPTE_25) {
                strDivisionType = "SMPTE, 25 frames per second";
            } else if (fDivisionType == Sequence.SMPTE_30DROP) {
                strDivisionType = "SMPTE, 29.97 frames per second";
            } else if (fDivisionType == Sequence.SMPTE_30) {
                strDivisionType = "SMPTE, 30 frames per second";
            }


            out("DivisionType: " + strDivisionType);

            String      strResolutionType = null;
            if (sequence.getDivisionType() == Sequence.PPQ) {
                strResolutionType = " ticks per beat";
            } else {
                strResolutionType = " ticks per frame";
            }
            out("Resolution: " + sequence.getResolution() + strResolutionType);
            out("------------------------------------------------------------------------");
            Track[]    tracks = sequence.getTracks();
            for (int nTrack = 0; nTrack < tracks.length; nTrack++) {
                out("Track " + nTrack + ":");
                out("-----------------------");
                Track        track = tracks[nTrack];
                for (int nEvent = 0; nEvent < track.size(); nEvent++) {
                    MidiEvent  event = track.get(nEvent);
                    output(event);
                }
                out("--------------------------------------------------------------------");
            }
        }
    }


    public static void output(MidiEvent event) {
        MidiMessage     message = event.getMessage();
        long            lTicks = event.getTick();
        sm_receiver.send(message, lTicks);
    }


    private static void out(String strMessage) {
        System.out.println(strMessage);
    }
}
/*** DumpSequence.java ***/

There are several sites with legal, free MIDI files. The file http://files.mididb.com/amy-winehouse/rehab.mid gives the result.

---------------------------------------------------------------------------
File: rehab.mid
---------------------------------------------------------------------------
Length: 251475 ticks
Duration: 216788738 microseconds
---------------------------------------------------------------------------
DivisionType: PPQ
Resolution: 480 ticks per beat
---------------------------------------------------------------------------
Track 0:
-----------------------
tick 0: Time Signature: 4/4, MIDI clocks per metronome tick: 24, 1/32 per 24 MIDI clocks: 8
tick 0: Key Signature: C major
tick 0: SMTPE Offset: 32:0:0.0.0
tick 0: Set Tempo: 145.0 bpm
tick 0: End of Track
---------------------------------------------------------------------------
Track 1:
-----------------------
tick 0: Sequence/Track Name: amy winehouse - rehab
tick 0: Instrument Name: GM Device
tick 40: Sysex message: F0 7E 7F 09 01 F7
tick 40: End of Track
---------------------------------------------------------------------------
Track 2:
-----------------------
tick 0: MIDI Channel Prefix: 1
tick 0: Sequence/Track Name: amy winehouse - rehab
tick 0: Instrument Name: GM Device  2
tick 480: [B1 79 00] channel 2: control change 121 value: 0
tick 485: [B1 0A 40] channel 2: control change 10 value: 64
tick 490: [B1 5D 14] channel 2: control change 93 value: 20
tick 495: [B1 5B 00] channel 2: control change 91 value: 0
tick 500: [B1 0B 7F] channel 2: control change 11 value: 127
tick 505: [B1 07 69] channel 2: control change 7 value: 105
tick 510: [E1 00 40] channel 2: pitch wheel change 8192
tick 515: [B1 00 00] channel 2: control change 0 value: 0
tick 520: [C1 22] channel 2: program change 34
...

Playing a MIDI File

To play a MIDI file, you create a Sequence from a File, using the MidiSystem. You also create a Sequencer from the MidiSystem and pass it the sequence. The sequencer will output MIDI messages through its Transmitter. This completes the setup of the MIDI event generation side of the system.

The play side is constructed by getting a Synthesizerfrom the MidiSystem. The Receiver is found from the synthesizer and is given to the transmitter of MIDI events. Play commences by calling start() on the sequencer, which reads from the file and passes MIDI events to its transmitter. These are passed to the synthesizer’s receiver and played. Figure 18-1 shows the UML class diagram for the relevant classes.

A435426_1_En_18_Fig1_HTML.gif
Figure 18-1. Class diagram for the SimpleMidiPlayer

This code is from playing an audio file (easy). The original is heavily commented, but I have removed much of it for print book. The logic is that you load a sequence from a file, get the default sequencer, and set the sequence into the sequencer. Sequencers are not necessarily synthesizers, but the default sequencer usually is. If not, you get the default synthesizer and then hook up the sequencer’s transmitter to the synthesizer’s receiver. Then the MIDI file is played by calling start()on the sequencer.

/*
 *        SimpleMidiPlayer.java
 *
 *        This file is part of jsresources.org
 */


/*
 * Copyright (c) 1999 - 2001 by Matthias Pfisterer
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */


import java.io.File;
import java.io.IOException;


import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.MetaEventListener;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
import javax.sound.midi.Synthesizer;
import javax.sound.midi.Receiver;
import javax.sound.midi.Transmitter;
import javax.sound.midi.MidiChannel;
import javax.sound.midi.ShortMessage;


public class SimpleMidiPlayer {

    private static Sequencer sm_sequencer = null;
    private static Synthesizer sm_synthesizer = null;


    public static void main(String[]args) {

        if (args.length == 0 || args[0].equals("-h")) {
            printUsageAndExit();
        }


        String strFilename = args[0];
        File midiFile = new File(strFilename);


        /*
         *      We read in the MIDI file to a Sequence object.
         */
        Sequence sequence = null;
        try {
            sequence = MidiSystem.getSequence(midiFile);
        }
        catch(InvalidMidiDataException e) {
            e.printStackTrace();
            System.exit(1);
        }
        catch(IOException e) {
            e.printStackTrace();
            System.exit(1);
        }


        /*
         *      Now, we need a Sequencer to play the sequence.
         *      Here, we simply request the default sequencer.
         *      With an argument of false, it does not create
         *      a default syntesizer.
         */
        try {
            sm_sequencer = MidiSystem.getSequencer(false);
        }
        catch(MidiUnavailableException e) {
            e.printStackTrace();
            System.exit(1);
        }
        if (sm_sequencer == null) {
            out("SimpleMidiPlayer.main(): can't get a Sequencer");
            System.exit(1);
        }


        try {
            sm_sequencer.open();
        }
        catch(MidiUnavailableException e) {
            e.printStackTrace();
            System.exit(1);
        }


        /*
         *      Next step is to tell the Sequencer which
         *      Sequence it has to play.
         */
        try {
            sm_sequencer.setSequence(sequence);
        }
        catch(InvalidMidiDataException e) {
            e.printStackTrace();
            System.exit(1);
        }


        Receiver synthReceiver = null;
        if (!(sm_sequencer instanceof Synthesizer)) {
            /*
             *      We try to get the default synthesizer, open()
             *      it and chain it to the sequencer with a
             *      Transmitter-Receiver pair.
             */
            try {
                sm_synthesizer = MidiSystem.getSynthesizer();
                sm_synthesizer.open();
                synthReceiver = sm_synthesizer.getReceiver();
                Transmitter seqTransmitter = sm_sequencer.getTransmitter();
                seqTransmitter.setReceiver(synthReceiver);
            }
            catch(MidiUnavailableException e) {
                e.printStackTrace();
            }
        }


        /*
         *      Now, we can start playing
         */
        sm_sequencer.start();


        try {
            Thread.sleep(5000);
        }
        catch(InterruptedException e) {
            e.printStackTrace();
        }
    }


    private static void printUsageAndExit() {
        out("SimpleMidiPlayer: usage:");
        out(" java SimpleMidiPlayer <midifile>");
        System.exit(1);
    }


    private static void out(String strMessage) {
        System.out.println(strMessage);
    }
}


Playing a file to an external MIDI synthesizer

I have an Edirol Studio Canvas SD-20 synthesizer that I bought for a few hundred Australian dollars. This plugs into a PC through a USB port. ALSA recognizes this with the following:

 $ amidi -l
Dir Device    Name
IO  hw:2,0,0  SD-20 Part A
IO  hw:2,0,1  SD-20 Part B
I   hw:2,0,2  SD-20 MIDI

The MidiDevice.Info device information lists hw:2,0,0 twice, once for input and once for output, and is similar for the other values. The device information can be identified by the toString method , which returns values such as "SD20 [hw:2,0,0]". From the device information, the device can be found like before using MidiSystem.getMidiDevice(info). The input and output devices can be distinguished by the number of maxOutputReceivers it supports: zero means none, while any other value (including –1!) means it has a MIDI receiver. Selecting an external receiver is done with code to replace the previous setting of the synthesizer with this:

                Receiver        synthReceiver = null;
                MidiDevice.Info[] devices;
                devices = MidiSystem.getMidiDeviceInfo();


                for (MidiDevice.Info info: devices) {
                    System.out.println("    Name: " + info.toString() +
                                       ", Decription: " +
                                       info.getDescription() +
                                       ", Vendor: " +
                                       info.getVendor());
                    if (info.toString().equals("SD20 [hw:2,0,0]")) {
                        MidiDevice device = MidiSystem.getMidiDevice(info);
                        if (device.getMaxReceivers() != 0) {
                            try {
                                device.open();
                                System.out.println("  max receivers: " + device.getMaxReceivers());
                                receiver = device.getReceiver();
                                System.out.println("Found a receiver");
                                break;
                            } catch(Exception e) {}
                        }
                    }
                }


               if (receiver == null) {
            System.out.println("Receiver is null");
            System.exit(1);
              }
              try {
            Transmitter seqTransmitter = sm_sequencer.getTransmitter();
            seqTransmitter.setReceiver(receiver);
              }
        catch(MidiUnavailableException e) {
            e.printStackTrace();
             }


             /*
              *      Now, we can start playing as before
              */

Changing the Soundbank

The soundbank is a set of “sounds” encoded in some way that are used to generate the music played. The default sound synthesizer for Java is the Gervill synthesizer, and it looks for its default soundbank in $HOME/.gervill/soundbank-emg.sf2. This default soundbank is tiny; it’s only 1.9MB in size. And it sounds, well, poor quality.

DaWicked1 in “Better Java-midi instrument sounds for Linux” ( www.minecraftforum.net/forums/mapping-and-modding/mapping-and-modding-tutorials/1571330-better-java-midi-instrument-sounds-for-linux ) offers two methods to improve this: the simpler way is to replace the sound font with a better one such as the FluidSynth font, using the default name.

The second method is programmatic and probably better as it allows more flexibility and choice at runtime.

Changing Pitch and Speed

Changing the speed of playback of a MIDI file means changing the rate that MIDI messages are sent from the sequencer. The Java sequencers have methods to control this such as setTempoFactor. The sequencer will respond to this method by sending the messages at a different rate.

Changing the pitch of the notes can be done by altering the pitch of the NOTE_ON and NOTE_OFF messages. This has to be done not just for future notes but also for notes currently playing. Fortunately, there is a MIDI command called Pitch Bend, which can be sent to a synthesizer to change the pitch of all currently playing and future notes. A Pitch Bend value of 0x2000 corresponds to no pitch change; values up to 0x4000 are increases in pitch, and below are decreases in pitch. There are many sites giving complex formulae for this, but the simplest seems to be MIDI Pitch Bend Range ( www.ultimatemetal.com/forum/threads/midi-pitch-bend-range.680677/ ), which states that a change of pitch by 683 is roughly a semitone. So, you change the pitch value and send a new Pitch Bend event to the receiver.

You look for input from the user of ¬, ʿ, ®, ¯ (Esc-[A, and so on). These then call the appropriate method. The program illustrating this is an adaptation of the SimpleMidiPlayer given earlier in the chapter and is called AdjustableMidiPlayer.java. In the main body, you replace the call to sleep with the following:

        BufferedReader br = new BufferedReader(new
                                               InputStreamReader(System.in));
        String str = null;
        System.out.println("Enter lines of text.");
        System.out.println("Enter 'stop' to quit.");
        do {
            try {
                str = br.readLine();
                if (str.length() >= 2) {
                    byte[] bytes = str.getBytes();
                    if (bytes[0] == 27 && bytes[1] == 91) {
                        if (bytes[2] == 65) {
                            // up
                            increasePitch();
                        } else if (bytes[2] == 66) {
                            // down
                            decreasePitch();
                        } else if (bytes[2] == 67) {
                            //right
                            faster();
                        } else if (bytes[2] == 68) {
                            //left
                            slower();
                        }
                    }
                }
            } catch(java.io.IOException e) {
            }
        } while(!str.equals("stop"));
    }
where the new functions are given by
    private void increasePitch() {
        // 683 from www.ultimatemetal.com/forum/threads/midi-pitch-bend-range.680677/
        pitch += 683;
        for (int n = 0; n < 16; n++) {
            try {
                MidiMessage msg =
                    new ShortMessage(ShortMessage.PITCH_BEND,
                                     n,
                                     pitch & 0x7F, pitch >> 7);
                synthReceiver.send(msg, 0);
            } catch (Exception e) {
            }
        }
    }


    private void decreasePitch() {
        // 683 from www.ultimatemetal.com/forum/threads/midi-pitch-bend-range.680677/
        pitch -= 683;
        for (int n = 0; n < 16; n++) {
            try {
                MidiMessage msg =
                    new ShortMessage(ShortMessage.PITCH_BEND,
                                     n,
                                     pitch & 0x7F, pitch >> 7);
                synthReceiver.send(msg, 0);
            } catch (Exception e) {
            }
        }
    }


    float speed = 1.0f;

    private void faster() {
        speed *= 1.2f;
        sm_sequencer.setTempoFactor(speed);
    }


    private void slower() {
        speed /= 1.2f;
        sm_sequencer.setTempoFactor(speed);
    }

Using TiMidity Instead of the Default Gervill Synthesizer

The soft synth TiMidity can be run as a back-end synthesizer using the ALSA sequencer with the following:

$timidity -iA -B2,8 -Os -EFreverb=0

Opening sequencer port: 128:0 128:1 128:2 128:3

(It’s similar for FluidSynth.) This is opened on ports 128:0, and so on.

Unfortunately, this is not directly visible to Java Sound, which expects either the default Gervill synthesizer or a raw MIDI synthesizer such as a hardware synthesizer. As discussed in Chapter 19, you can fix this by using ALSA raw MIDI ports.

You add raw MIDI ports with the following:

modprobe snd-seq snd-virmidi

This will bring virtual devices both into the ALSA raw MIDI and into the ALSA sequencer spaces:

$amidi -l
Dir Device    Name
IO  hw:3,0    Virtual Raw MIDI (16 subdevices)
IO  hw:3,1    Virtual Raw MIDI (16 subdevices)
IO  hw:3,2    Virtual Raw MIDI (16 subdevices)
IO  hw:3,3    Virtual Raw MIDI (16 subdevices)


$aplaymidi -l
 Port    Client name                      Port name
 14:0    Midi Through                     Midi Through Port-0
 28:0    Virtual Raw MIDI 3-0             VirMIDI 3-0
 29:0    Virtual Raw MIDI 3-1             VirMIDI 3-1
 30:0    Virtual Raw MIDI 3-2             VirMIDI 3-2
 31:0    Virtual Raw MIDI 3-3             VirMIDI 3-3

Virtual raw MIDI port 3-0 can then be connected to TiMidity port 0 with the following:

aconnect 28:0 128:0

The final step in playing to TiMidity is to change one line of AdaptableMidiPlayer.javafrom this:

if (info.toString().equals("SD20 [hw:2,0,0]")) {

to this:

if (info.toString().equals("VirMIDI [hw:3,0,0]")) {

Conclusion

This chapter built a number of programs using the MIDI API and discussed how to use external hardware synthesizers and soft synthesizers such as TiMidity.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset