Chapter 13, "Introduction to 
  the Service Provider Interfaces," explained that the javax.sound.sampled.spi 
  and javax.sound.midi.spi packages define abstract classes to be 
  used by developers of sound services. By implementing a subclass of one of these 
  abstract classes, a service provider can create a new service that extends the 
  functionality of the runtime system. Chapter 14 covered the use of the javax.sound.sampled.spi 
  package. The present chapter discusses how to use the javax.sound.midi.spi 
  package to provide new services for handling MIDI devices and files. 
Applications programmers who only use existing MIDI services in their programs can safely skip this chapter. For an overview of MIDI and the use of the installed MIDI services in an application program, see Part II, "MIDI," of this Programmer's Guide. This chapter assumes that the reader is familiar with the JavaTM Sound API methods that application programs invoke to access installed MIDI services.
 
There are four abstract classes in the javax.sound.midi.spi package, which represent four different types of services that you can provide for the MIDI system:
MidiFileWriter provides MIDI file-writing services. These services 
    make it possible for an application program to save, to a MIDI file, a MIDI 
    Sequence that it has generated or processed.  
     
  MidiFileReader provides file-reading services that return a 
    MIDI Sequence from a MIDI file for use in an application program. 
      
  MidiDeviceProvider supplies instances of one or more specific 
    types of MIDI device, possibly including hardware devices.  
     
  SoundbankReader supplies soundbank file-reading services. Concrete 
    subclasses of SoundbankReader parse a given soundbank file, producing 
    a Soundbank object that can be loaded into a Synthesizer. 
An application program will not directly create an instance of a service objectwhether 
  a provider object, such as a MidiDeviceProvider, or an object, 
  such as a Synthesizer, that is supplied by the provider object. 
  Nor will the program directly refer to the SPI classes. Instead, the application 
  program makes requests to the MidiSystem object in the javax.sound.midi 
  package, and MidiSystem in turn uses concrete subclasses of the 
  javax.sound.midi.spi classes to process these requests. 
There are three standard MIDI file formats, all of which an implementation of the Java Sound API can support: Type 0, Type 1, and Type 2. These file formats differ in their internal representation of the MIDI sequence data in the file, and are appropriate for different kinds of sequences. If an implementation doesn't itself support all three types, a service provider can supply the support for the unimplemented ones. There are also variants of the standard MIDI file formats, some of them proprietary, which similarly could be supported by a third-party vendor.
 
The ability to write MIDI files is provided by concrete subclasses of MidiFileWriter. This abstract class is directly analogous to javax.sampled.spi.AudioFileWriter.  Again, the methods are grouped into query methods for learning what types of files can be written, and methods for actually writing a file.  As with AudioFileWriter, two of the query methods are concrete:
The first of these provides general information about whether the file writer can ever write the specified type of MIDI file type. The second method is more specific: it asks whether a particular Sequence can be written to the specified type of MIDI file. Generally, you don't need to override either of these two concrete methods. In the default implementation, each invokes one of two other corresponding query methods and iterates over the results returned. Being abstract, these other two query methods need to be implemented in the subclass:boolean isFileTypeSupported(int fileType) boolean isFileTypeSupported(int fileType, Sequence sequence)
The first of these returns an array of all the file types that are supported in general. A typical implementation might initialize the array in the file writer's constructor and return the array from this method. From that set of file types, the second method finds the subset to which the file writer can write the given Sequence. In accordance with the MIDI specification, not all types of sequences can be written to all types of MIDI files.abstract int[] getMidiFileTypes() abstract int[] getMidiFileTypes(Sequence sequence)
 
The write methods of a MidiFileWriter subclass perform the encoding of the data in a given Sequence into the correct data format for the requested type of MIDI file, writing the coded stream to either a file or an output stream:
abstract int write(Sequence in, int fileType, 
                   java.io.File out) 
abstract int write(Sequence in, int fileType, 
                   java.io.OutputStream out) 
write method  must parse the Sequence by iterating over its tracks, construct an appropriate file header, and write the header and tracks to the output. The MIDI file's header format is, of course, defined by the MIDI specification. It includes such information as a "magic number" identifying this as a MIDI file, the header's length, the number of tracks, and the sequence's timing information (division type and resolution). The rest of the MIDI file consists of the track data, in the format defined by the MIDI specification. 
  Let's briefly look at how the application program, MIDI 
  system, and service provider cooperate in writing a MIDI file. In a typical 
  situation, an application program has a particular MIDI Sequence 
  to save to a file. The program queries the MidiSystem object to 
  see what MIDI file formats, if any, are supported for the particular Sequence 
  at hand, before attempting to write the file. The MidiSystem.getMidiFileTypes(Sequence) 
  method returns an array of all the MIDI file types to which the system can write 
  a particular sequence. It does this by invoking the corresponding getMidiFileTypes 
  method for each of the installed MidiFileWriter services, and collecting 
  and returning the results in an array of integers that can be thought of as 
  a master list of all file types compatible with the given Sequence. 
  When it comes to writing the Sequence to a file, the call to MidiSystem.write 
  is passed an integer representing a file type, along with the Sequence 
  to be written and the output file; MidiSystem uses the supplied 
  type to decide which installed MidiFileWriter should handle the 
  write request, and dispatches a corresponding write to the appropriate 
  MidiFileWriter. 
 
The MidiFileReader abstract class is directly analogous to javax.sampled.spi.AudioFileReader class.  Both consist of two  overloaded methods, each of which can take a File, URL, or InputStream argument.  The first of the  overloaded methods returns the file format of a specified file.  In the case of MidiFileReader, the API is:
abstract MidiFileFormat getMidiFileFormat(java.io.File file) 
abstract MidiFileFormat getMidiFileFormat(
    java.io.InputStream stream) 
abstract MidiFileFormat getMidiFileFormat(java.net.URL url) 
MidiFileFormat object describing the format of the specified MIDI file (or stream or URL), assuming that the file is of a type supported by the file reader and that it contains valid header information. Otherwise, an InvalidMidiDataException should be thrown.
 
The other overloaded method returns a MIDI Sequence from a given file, stream, or URL :
Theabstract Sequence getSequence(java.io.File file) abstract Sequence getSequence(java.io.InputStream stream) abstract Sequence getSequence(java.net.URL url)
getSequence method performs the actual work of parsing the bytes in the MIDI input file and constructing a corresponding Sequence object. This is essentially the inverse of the process used by MidiFileWriter.write. Because there is a one-to-one correspondence between the contents of a MIDI file as defined by the MIDI specification and a Sequence object as defined by the Java Sound API, the details of the parsing are straightforward. If the file passed to getSequence contains data that the file reader can't parse (for example, because the file has been corrupted or doesn't conform to the MIDI specification), an InvalidMidiDataException should be thrown.
 
 
A MidiDeviceProvider can be considered a factory that supplies one or more particular types of MIDI device. The class consists of a method that returns an instance of a MIDI device, as well as query methods to learn what kinds of devices this provider can supply.
  As with the other javax.sound.midi.spi 
  services, application developers get indirect access to a MidiDeviceProvider 
  service through a call to MidiSystem methods, in this case MidiSystem.getMidiDevice 
  and MidiSystem.getMidiDeviceInfo. The purpose of subclassing MidiDeviceProvider 
  is to supply a new kind of device, so the service developer must also create 
  an accompanying class for the device being returnedjust as we saw with 
  MixerProvider in the javax.sound.sampled.spi package. 
  There, the returned device's class implemented the javax.sound.sampled.Mixer 
  interface; here it implements the javax.sound.midi.MidiDevice interface. 
  It might also implement a subinterface of MidiDevice, such as Synthesizer 
  or Sequencer. 
 
Because a single subclass of MidiDeviceProvider can provide more than one type of MidiDevice, the getDeviceInfo method of the class returns an array of MidiDevice.Info objects enumerating the different MidiDevices available:
abstract MidiDevice.Info[] getDeviceInfo()
The returned array can contain a single element, of course. A typical implementation 
  of the provider might initialize an array in its constructor and return it here. 
  This allows MidiSystem to iterate over all installed MidiDeviceProviders 
  to construct a list of all installed devices. MidiSystem 
  can then return this list (MidiDevice.Info[] array) to an application 
  program. 
 
MidiDeviceProvider also includes a concrete query method:
This method permits the system to query the provider about a specific kind of device. Generally, you don't need to override this convenience method. The default implementation iterates over the array returned by getDeviceInfo and compares the argument to each element.boolean isDeviceSupported(MidiDevice.Info info)
 
The third and final MidiDeviceProvider method returns the requested device: 
This method should first test the argument to make sure it describes a device that this provider can supply. If it doesn't, it should throw anabstract MidiDevice getDevice(MidiDevice.Info info)
IllegalArgumentException. Otherwise, it returns the device. 
 
  A SoundBank is a set of Instruments 
  that can be loaded into a Synthesizer. An Instrument 
  is an implementation of a sound-synthesis algorithm that produces a particular 
  sort of sound, and includes accompanying name and information strings. A SoundBank 
  roughly corresponds to a bank in the MIDI specification, but it's a more extensive 
  and addressable collection; it can perhaps better be thought of as a collection 
  of MIDI banks. (For more background information on SoundBanks and 
  Synthesizers, see Chapter 12, "Synthesizing 
  Sound.") 
 
SoundbankReader consists of a single overloaded method, which the system invokes to read a Soundbank object from a soundbank file:
abstract Soundbank getSoundbank(java.io.File file) abstract Soundbank getSoundbank(java.io.InputStream stream) abstract Soundbank getSoundbank(java.net.URL url)
Concrete subclasses of SoundbankReader will work in tandem with 
  particular provider-defined implementations of SoundBank, Instrument, 
  and Synthesizer to allow the system to load a SoundBank 
  from a file into an instance of a particular Synthesizer class. 
  Synthesis techniques may differ wildly from one Synthesizer to 
  another, and, as a consequence, the data stored in an Instrument 
  or SoundBank providing control or specification data for the synthesis 
  process of a Synthesizer can take a variety of forms. One synthesis 
  technique may require only a few bytes of parameter data; another may be based 
  on extensive sound samples. The resources present in a SoundBank 
  will depend upon the nature of the Synthesizer into which they 
  get loaded, and therefore the implementation of the getSoundbank 
  method of a SoundbankReader subclass has access to knowledge of 
  a particular kind of SoundBank. In addition, a particular subclass 
  of SoundbankReader understands a particular file format for storing 
  the SoundBank data. That file format may be vendor-specific and 
  proprietary. 
 
SoundBank is just an interface, with only weak constraints on the contents of a SoundBank object. The methods an object must support to implement this interface (getResources, getInstruments, getVendor, getName, etc.) impose loose requirements on the data that the object contains. For example, getResources and getInstruments can return empty arrays. The actual contents of a subclassed SoundBank object, in particular its instruments and its non-instrument resources, are defined by the service provider. Thus, the mechanism of parsing a soundbank file depends entirely on the specification of that particular kind of soundbank file.
Soundbank files are created outside the Java Sound API, typically by the vendor of the synthesizer that can load that kind of soundbank. Some vendors might supply end-user tools for creating such files.