001/* MidiSystem.java -- Access system MIDI resources 002 Copyright (C) 2005, 2012 Free Software Foundation, Inc. 003 004This file is part of GNU Classpath. 005 006GNU Classpath is free software; you can redistribute it and/or modify 007it under the terms of the GNU General Public License as published by 008the Free Software Foundation; either version 2, or (at your option) 009any later version. 010 011GNU Classpath is distributed in the hope that it will be useful, but 012WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014General Public License for more details. 015 016You should have received a copy of the GNU General Public License 017along with GNU Classpath; see the file COPYING. If not, write to the 018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01902110-1301 USA. 020 021Linking this library statically or dynamically with other modules is 022making a combined work based on this library. Thus, the terms and 023conditions of the GNU General Public License cover the whole 024combination. 025 026As a special exception, the copyright holders of this library give you 027permission to link this library with independent modules to produce an 028executable, regardless of the license terms of these independent 029modules, and to copy and distribute the resulting executable under 030terms of your choice, provided that you also meet, for each linked 031independent module, the terms and conditions of the license of that 032module. An independent module is a module which is not derived from 033or based on this library. If you modify this library, you may extend 034this exception to your version of the library, but you are not 035obligated to do so. If you do not wish to do so, delete this 036exception statement from your version. */ 037 038 039package javax.sound.midi; 040 041import gnu.classpath.ServiceFactory; 042 043import java.io.File; 044import java.io.IOException; 045import java.io.InputStream; 046import java.io.OutputStream; 047import java.net.URL; 048import java.util.ArrayList; 049import java.util.List; 050import java.util.Iterator; 051 052import javax.sound.midi.spi.MidiDeviceProvider; 053import javax.sound.midi.spi.MidiFileReader; 054import javax.sound.midi.spi.MidiFileWriter; 055import javax.sound.midi.spi.SoundbankReader; 056 057/** 058 * MidiSystem provides access to the computer system's MIDI resources, 059 * as well as utility routines for reading MIDI files and more. 060 * 061 * @author Anthony Green (green@redhat.com) 062 * @since 1.3 063 * 064 */ 065public class MidiSystem 066{ 067 private MidiSystem() 068 { 069 // Not instantiable. 070 } 071 072 /** 073 * Get an array of all available MIDI devices. 074 * 075 * @return a possibly empty array of all available MIDI devices 076 */ 077 public static MidiDevice.Info[] getMidiDeviceInfo() 078 { 079 Iterator<MidiDeviceProvider> deviceProviders = 080 ServiceFactory.lookupProviders(MidiDeviceProvider.class); 081 List<MidiDevice.Info> infoList = new ArrayList<MidiDevice.Info>(); 082 083 while (deviceProviders.hasNext()) 084 { 085 MidiDeviceProvider provider = (MidiDeviceProvider) deviceProviders.next(); 086 MidiDevice.Info[] infos = provider.getDeviceInfo(); 087 for (int i = infos.length; i > 0; ) 088 infoList.add(infos[--i]); 089 } 090 091 return infoList.toArray(new MidiDevice.Info[infoList.size()]); 092 } 093 094 /** 095 * Get the specified MIDI device. 096 * 097 * @param info a description of the device we're looking for 098 * @return the requested MIDI device 099 * @throws MidiUnavailableException if no MIDI devices are configured or found 100 * @throws IllegalArgumentException if the device described by info is not found 101 */ 102 public static MidiDevice getMidiDevice(MidiDevice.Info info) 103 throws MidiUnavailableException 104 { 105 Iterator<MidiDeviceProvider> deviceProviders = 106 ServiceFactory.lookupProviders(MidiDeviceProvider.class); 107 108 if (! deviceProviders.hasNext()) 109 throw new MidiUnavailableException("No MIDI device providers available."); 110 111 do 112 { 113 MidiDeviceProvider provider = 114 (MidiDeviceProvider) deviceProviders.next(); 115 if (provider.isDeviceSupported(info)) 116 return provider.getDevice(info); 117 } while (deviceProviders.hasNext()); 118 119 throw new IllegalArgumentException("MIDI device " 120 + info + " not available."); 121 } 122 123 /** 124 * Get the default Receiver instance. This just picks the first one 125 * it finds for now. 126 * 127 * @return the default Receiver instance 128 * @throws MidiUnavailableException if no Receiver is found 129 */ 130 public static Receiver getReceiver() throws MidiUnavailableException 131 { 132 // TODO: The 1.5 spec has a fancy mechanism to specify the default 133 // receiver device. For now, well just return the first one we find. 134 MidiDevice.Info[] infos = getMidiDeviceInfo(); 135 for (int i = 0; i < infos.length; i++) 136 { 137 MidiDevice device = getMidiDevice(infos[i]); 138 if (device instanceof Receiver) 139 return (Receiver) device; 140 } 141 throw new MidiUnavailableException("No Receiver device available"); 142 } 143 144 /** 145 * Get the default Transmitter instance. This just picks the first one 146 * it finds for now. 147 * 148 * @return the default Transmitter instance 149 * @throws MidiUnavailableException if no Transmitter is found 150 */ 151 public static Transmitter getTransmitter() throws MidiUnavailableException 152 { 153 // TODO: The 1.5 spec has a fancy mechanism to specify the default 154 // Transmitter device. For now, well just return the first one we find. 155 MidiDevice.Info[] infos = getMidiDeviceInfo(); 156 for (int i = 0; i < infos.length; i++) 157 { 158 MidiDevice device = getMidiDevice(infos[i]); 159 if (device instanceof Transmitter) 160 return (Transmitter) device; 161 } 162 throw new MidiUnavailableException("No Transmitter device available"); 163 } 164 165 /** 166 * Get the default Synthesizer instance. This just picks the first one 167 * it finds for now. 168 * 169 * @return the default Synthesizer instance 170 * @throws MidiUnavailableException if no Synthesizer is found 171 */ 172 public static Synthesizer getSynthesizer() throws MidiUnavailableException 173 { 174 // TODO: The 1.5 spec has a fancy mechanism to specify the default 175 // Synthesizer device. For now, well just return the first one we find. 176 MidiDevice.Info[] infos = getMidiDeviceInfo(); 177 for (int i = 0; i < infos.length; i++) 178 { 179 MidiDevice device = getMidiDevice(infos[i]); 180 if (device instanceof Synthesizer) 181 return (Synthesizer) device; 182 } 183 throw new MidiUnavailableException("No Synthesizer device available"); 184 } 185 186 /** 187 * Get the default Sequencer instance. This just picks the first one 188 * it finds for now. 189 * 190 * @return the default Sequencer instance 191 * @throws MidiUnavailableException if no Sequencer is found 192 */ 193 public static Sequencer getSequencer() throws MidiUnavailableException 194 { 195 // TODO: The 1.5 spec has a fancy mechanism to specify the default 196 // Sequencer device. For now, well just return the first one we find. 197 MidiDevice.Info[] infos = getMidiDeviceInfo(); 198 for (int i = 0; i < infos.length; i++) 199 { 200 MidiDevice device = getMidiDevice(infos[i]); 201 if (device instanceof Sequencer) 202 return (Sequencer) device; 203 } 204 throw new MidiUnavailableException("No Sequencer device available"); 205 } 206 207 /** 208 * Read a Soundbank object from the given stream. 209 * 210 * @param stream the stream from which to read the Soundbank 211 * @return the Soundbank object 212 * @throws InvalidMidiDataException if we were unable to read the soundbank 213 * @throws IOException if an I/O error happened while reading 214 */ 215 public static Soundbank getSoundbank(InputStream stream) 216 throws InvalidMidiDataException, IOException 217 { 218 Iterator<SoundbankReader> readers = 219 ServiceFactory.lookupProviders(SoundbankReader.class); 220 while (readers.hasNext()) 221 { 222 SoundbankReader sr = readers.next(); 223 Soundbank sb = sr.getSoundbank(stream); 224 if (sb != null) 225 return sb; 226 } 227 throw new InvalidMidiDataException("Cannot read soundbank from stream"); 228 } 229 230 /** 231 * Read a Soundbank object from the given url. 232 * 233 * @param url the url from which to read the Soundbank 234 * @return the Soundbank object 235 * @throws InvalidMidiDataException if we were unable to read the soundbank 236 * @throws IOException if an I/O error happened while reading 237 */ 238 public static Soundbank getSoundbank(URL url) 239 throws InvalidMidiDataException, IOException 240 { 241 Iterator<SoundbankReader> readers = 242 ServiceFactory.lookupProviders(SoundbankReader.class); 243 while (readers.hasNext()) 244 { 245 SoundbankReader sr = readers.next(); 246 Soundbank sb = sr.getSoundbank(url); 247 if (sb != null) 248 return sb; 249 } 250 throw new InvalidMidiDataException("Cannot read from url " + url); 251 } 252 253 /** 254 * Read a Soundbank object from the given file. 255 * 256 * @param file the file from which to read the Soundbank 257 * @return the Soundbank object 258 * @throws InvalidMidiDataException if we were unable to read the soundbank 259 * @throws IOException if an I/O error happened while reading 260 */ 261 public static Soundbank getSoundbank(File file) 262 throws InvalidMidiDataException, IOException 263 { 264 Iterator<SoundbankReader> readers = 265 ServiceFactory.lookupProviders(SoundbankReader.class); 266 while (readers.hasNext()) 267 { 268 SoundbankReader sr = (SoundbankReader) readers.next(); 269 Soundbank sb = sr.getSoundbank(file); 270 if (sb != null) 271 return sb; 272 } 273 throw new InvalidMidiDataException("Cannot read soundbank from file " 274 + file); 275 } 276 277 /** 278 * Read a MidiFileFormat object from the given stream. 279 * 280 * @param stream the stream from which to read the MidiFileFormat 281 * @return the MidiFileFormat object 282 * @throws InvalidMidiDataException if we were unable to read the MidiFileFormat 283 * @throws IOException if an I/O error happened while reading 284 */ 285 public static MidiFileFormat getMidiFileFormat(InputStream stream) 286 throws InvalidMidiDataException, IOException 287 { 288 Iterator<MidiFileReader> readers = 289 ServiceFactory.lookupProviders(MidiFileReader.class); 290 while (readers.hasNext()) 291 { 292 MidiFileReader sr = readers.next(); 293 MidiFileFormat sb = sr.getMidiFileFormat(stream); 294 if (sb != null) 295 return sb; 296 } 297 throw new InvalidMidiDataException("Can't read MidiFileFormat from stream"); 298 } 299 300 /** 301 * Read a MidiFileFormat object from the given url. 302 * 303 * @param url the url from which to read the MidiFileFormat 304 * @return the MidiFileFormat object 305 * @throws InvalidMidiDataException if we were unable to read the MidiFileFormat 306 * @throws IOException if an I/O error happened while reading 307 */ 308 public static MidiFileFormat getMidiFileFormat(URL url) 309 throws InvalidMidiDataException, IOException 310 { 311 Iterator<MidiFileReader> readers = 312 ServiceFactory.lookupProviders(MidiFileReader.class); 313 while (readers.hasNext()) 314 { 315 MidiFileReader sr = readers.next(); 316 MidiFileFormat sb = sr.getMidiFileFormat(url); 317 if (sb != null) 318 return sb; 319 } 320 throw new InvalidMidiDataException("Cannot read from url " + url); 321 } 322 323 /** 324 * Read a MidiFileFormat object from the given file. 325 * 326 * @param file the file from which to read the MidiFileFormat 327 * @return the MidiFileFormat object 328 * @throws InvalidMidiDataException if we were unable to read the MidiFileFormat 329 * @throws IOException if an I/O error happened while reading 330 */ 331 public static MidiFileFormat getMidiFileFormat(File file) 332 throws InvalidMidiDataException, IOException 333 { 334 Iterator<MidiFileReader> readers = 335 ServiceFactory.lookupProviders(MidiFileReader.class); 336 while (readers.hasNext()) 337 { 338 MidiFileReader sr = readers.next(); 339 MidiFileFormat sb = sr.getMidiFileFormat(file); 340 if (sb != null) 341 return sb; 342 } 343 throw new InvalidMidiDataException("Can't read MidiFileFormat from file " 344 + file); 345 } 346 347 /** 348 * Read a Sequence object from the given stream. 349 * 350 * @param stream the stream from which to read the Sequence 351 * @return the Sequence object 352 * @throws InvalidMidiDataException if we were unable to read the Sequence 353 * @throws IOException if an I/O error happened while reading 354 */ 355 public static Sequence getSequence(InputStream stream) 356 throws InvalidMidiDataException, IOException 357 { 358 Iterator<MidiFileReader> readers = 359 ServiceFactory.lookupProviders(MidiFileReader.class); 360 while (readers.hasNext()) 361 { 362 MidiFileReader sr = readers.next(); 363 Sequence sq = sr.getSequence(stream); 364 if (sq != null) 365 return sq; 366 } 367 throw new InvalidMidiDataException("Can't read Sequence from stream"); 368 } 369 370 /** 371 * Read a Sequence object from the given url. 372 * 373 * @param url the url from which to read the Sequence 374 * @return the Sequence object 375 * @throws InvalidMidiDataException if we were unable to read the Sequence 376 * @throws IOException if an I/O error happened while reading 377 */ 378 public static Sequence getSequence(URL url) 379 throws InvalidMidiDataException, IOException 380 { 381 Iterator<MidiFileReader> readers = 382 ServiceFactory.lookupProviders(MidiFileReader.class); 383 while (readers.hasNext()) 384 { 385 MidiFileReader sr = readers.next(); 386 Sequence sq = sr.getSequence(url); 387 if (sq != null) 388 return sq; 389 } 390 throw new InvalidMidiDataException("Cannot read from url " + url); 391 } 392 393 /** 394 * Read a Sequence object from the given file. 395 * 396 * @param file the file from which to read the Sequence 397 * @return the Sequence object 398 * @throws InvalidMidiDataException if we were unable to read the Sequence 399 * @throws IOException if an I/O error happened while reading 400 */ 401 public static Sequence getSequence(File file) 402 throws InvalidMidiDataException, IOException 403 { 404 Iterator<MidiFileReader> readers = 405 ServiceFactory.lookupProviders(MidiFileReader.class); 406 while (readers.hasNext()) 407 { 408 MidiFileReader sr = readers.next(); 409 Sequence sq = sr.getSequence(file); 410 if (sq != null) 411 return sq; 412 } 413 throw new InvalidMidiDataException("Can't read Sequence from file " 414 + file); 415 } 416 417 /** 418 * Return an array of supported MIDI file types on this system. 419 * 420 * @return the array of supported MIDI file types 421 */ 422 public static int[] getMidiFileTypes() 423 { 424 // We only support a max of 3 MIDI file types. 425 boolean supported[] = new boolean[3]; 426 // The number of supported formats. 427 int count = 0; 428 Iterator<MidiFileWriter> writers = 429 ServiceFactory.lookupProviders(MidiFileWriter.class); 430 while (writers.hasNext()) 431 { 432 MidiFileWriter fw = writers.next(); 433 int types[] = fw.getMidiFileTypes(); 434 for (int i = types.length; i > 0;) 435 { 436 int type = types[--i]; 437 if (supported[type] == false) 438 { 439 count++; 440 supported[type] = true; 441 } 442 } 443 } 444 int result[] = new int[count]; 445 for (int i = supported.length; i > 0;) 446 { 447 if (supported[--i]) 448 result[--count] = i; 449 } 450 return result; 451 } 452 453 /** 454 * Return true if the system supports writing files of type fileType. 455 * 456 * @param fileType the MIDI file type we want to write 457 * @return true if we can write fileType files, false otherwise 458 */ 459 public static boolean isFileTypeSupported(int fileType) 460 { 461 Iterator<MidiFileWriter> writers = ServiceFactory.lookupProviders(MidiFileWriter.class); 462 while (writers.hasNext()) 463 { 464 MidiFileWriter fw = writers.next(); 465 466 if (fw.isFileTypeSupported(fileType)) 467 return true; 468 } 469 return false; 470 } 471 472 /** 473 * Return an array of supported MIDI file types on this system 474 * for the given sequnce. 475 * 476 * @param sequence the sequnce to write 477 * @return the array of supported MIDI file types 478 */ 479 public static int[] getMidiFileTypes(Sequence sequence) 480 { 481 // We only support a max of 3 MIDI file types. 482 boolean supported[] = new boolean[3]; 483 // The number of supported formats. 484 int count = 0; 485 Iterator<MidiFileWriter> writers = ServiceFactory.lookupProviders(MidiFileWriter.class); 486 while (writers.hasNext()) 487 { 488 MidiFileWriter fw = (MidiFileWriter) writers.next(); 489 int types[] = fw.getMidiFileTypes(sequence); 490 for (int i = types.length; i > 0;) 491 { 492 int type = types[--i]; 493 if (supported[type] == false) 494 { 495 count++; 496 supported[type] = true; 497 } 498 } 499 } 500 int result[] = new int[count]; 501 for (int i = supported.length; i > 0;) 502 { 503 if (supported[--i]) 504 result[--count] = i; 505 } 506 return result; 507 } 508 509 /** 510 * Return true if the system supports writing files of type fileType 511 * for the given sequence. 512 * 513 * @param fileType the MIDI file type we want to write 514 * @param sequence the Sequence we want to write 515 * @return true if we can write fileType files for sequence, false otherwise 516 */ 517 public static boolean isFileTypeSupported(int fileType, Sequence sequence) 518 { 519 Iterator<MidiFileWriter> writers = ServiceFactory.lookupProviders(MidiFileWriter.class); 520 while (writers.hasNext()) 521 { 522 MidiFileWriter fw = (MidiFileWriter) writers.next(); 523 524 if (fw.isFileTypeSupported(fileType, sequence)) 525 return true; 526 } 527 return false; 528 } 529 530 /** 531 * Write a sequence to an output stream using a specific MIDI file format. 532 * 533 * @param in the sequence to write 534 * @param fileType the MIDI file format to use 535 * @param out the output stream to write to 536 * @return the number of bytes written 537 * @throws IOException if an I/O exception happens 538 * @throws IllegalArgumentException if fileType is not supported for in 539 */ 540 public static int write(Sequence in, int fileType, OutputStream out) 541 throws IOException 542 { 543 Iterator<MidiFileWriter> writers = ServiceFactory.lookupProviders(MidiFileWriter.class); 544 while (writers.hasNext()) 545 { 546 MidiFileWriter fw = (MidiFileWriter) writers.next(); 547 548 if (fw.isFileTypeSupported(fileType, in)) 549 return fw.write(in, fileType, out); 550 } 551 throw new IllegalArgumentException("File type " 552 + fileType + " is not supported"); 553 } 554 555 /** 556 * Write a sequence to a file using a specific MIDI file format. 557 * 558 * @param in the sequence to write 559 * @param fileType the MIDI file format to use 560 * @param out the file to write to 561 * @return the number of bytes written 562 * @throws IOException if an I/O exception happens 563 * @throws IllegalArgumentException if fileType is not supported for in 564 */ 565 public static int write(Sequence in, int fileType, File out) 566 throws IOException 567 { 568 Iterator<MidiFileWriter> writers = ServiceFactory.lookupProviders(MidiFileWriter.class); 569 while (writers.hasNext()) 570 { 571 MidiFileWriter fw = (MidiFileWriter) writers.next(); 572 573 if (fw.isFileTypeSupported(fileType, in)) 574 return fw.write(in, fileType, out); 575 } 576 throw new IllegalArgumentException("File type " 577 + fileType + " is not supported"); 578 } 579}