# Audio
# Play a MIDI file
MIDI files can be played by using several classes from the javax.sound.midi
package. A Sequencer
performs playback of the MIDI file, and many of its methods can be used to set playback controls such as loop count, tempo, track muting, and others.
General playback of MIDI data can be done in this way:
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.Sequence;
import javax.sound.midi.Sequencer;
public class MidiPlayback {
public static void main(String[] args) {
try {
Sequencer sequencer = MidiSystem.getSequencer(); // Get the default Sequencer
if (sequencer==null) {
System.err.println("Sequencer device not supported");
return;
}
sequencer.open(); // Open device
// Create sequence, the File must contain MIDI file data.
Sequence sequence = MidiSystem.getSequence(new File(args[0]));
sequencer.setSequence(sequence); // load it into sequencer
sequencer.start(); // start the playback
} catch (MidiUnavailableException | InvalidMidiDataException | IOException ex) {
ex.printStackTrace();
}
}
}
To stop the playback use:
sequencer.stop(); // Stop the playback
A sequencer can be set to mute one or more of the sequence's tracks during playback so none of the instruments in those specified play. The following example sets the first track in the sequence to be muted:
import javax.sound.midi.Track;
// ...
Track[] track = sequence.getTracks();
sequencer.setTrackMute(track[0]);
A sequencer can play a sequence repeatedly if the loop count is given. The following sets the sequencer to play a sequence four times and indefinitely:
sequencer.setLoopCount(3);
sequencer.setLoopCount(Sequencer.LOOP_CONTINUOUSLY);
The sequencer does not always have to play the sequence from the beginning, nor does it have to play the sequence until the end. It can start and end at any point by specifying the tick in the sequence to start and end at. It is also possible to specify manually which tick in the sequence the sequencer should play from:
sequencer.setLoopStartPoint(512);
sequencer.setLoopEndPoint(32768);
sequencer.setTickPosition(8192);
Sequencers can also play a MIDI file at a certain tempo, which can be controlled by specifying the tempo in beats per minute (BPM) or microseconds per quarter note (MPQ). The factor at which the sequence is played can be adjusted as well.
sequencer.setTempoInBPM(1250f);
sequencer.setTempoInMPQ(4750f);
sequencer.setTempoFactor(1.5f);
When you finished using the Sequencer
, remeber to close it
sequencer.close();
# Play an Audio file Looped
Needed imports:
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
This code will create a clip and play it continuously once started:
Clip clip = AudioSystem.getClip();
clip.open(AudioSystem.getAudioInputStream(new URL(filename)));
clip.start();
clip.loop(Clip.LOOP_CONTINUOUSLY);
Get an Array with all supported file types:
AudioFileFormat.Type [] audioFileTypes = AudioSystem.getAudioFileTypes();
# Basic audio output
The Hello Audio! of Java that plays a sound file from local or internet storage looks as follows. It works for uncompressed .wav files and should not be used for playing mp3 or compressed files.
import java.io.*;
import java.net.URL;
import javax.sound.sampled.*;
public class SoundClipTest {
// Constructor
public SoundClipTest() {
try {
// Open an audio input stream.
File soundFile = new File("/usr/share/sounds/alsa/Front_Center.wav"); //you could also get the sound file with an URL
AudioInputStream audioIn = AudioSystem.getAudioInputStream(soundFile);
AudioFormat format = audioIn.getFormat();
// Get a sound clip resource.
DataLine.Info info = new DataLine.Info(Clip.class, format);
Clip clip = (Clip)AudioSystem.getLine(info);
// Open audio clip and load samples from the audio input stream.
clip.open(audioIn);
clip.start();
} catch (UnsupportedAudioFileException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (LineUnavailableException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new SoundClipTest();
}
}
# Bare metal sound
You can also go almost bare-metal when producing sound with java. This code will write raw binary data into the OS audio buffer to generate sound. It's extremely important to understand the limitations and necessary calculations to generating sound like this. Since playback is basically instantaneous, calculations need to be performed at almost real-time.
As such this method is unusable for more complicated sound-sampling. For such purposes using specialized tools is the better approach.
The following method generates and directly outputs a rectangle-wave of a given frequency in a given volume for a given duration.
public void rectangleWave(byte volume, int hertz, int msecs) {
final SourceDataLine dataLine;
// 24 kHz x 8bit, single-channel, signed little endian AudioFormat
AudioFormat af = new AudioFormat(24_000, 8, 1, true, false);
try {
dataLine = AudioSystem.getSourceDataLine(af);
dataLine.open(af, 10_000); // audio buffer size: 10k samples
} catch (LineUnavailableException e) {
throw new RuntimeException(e);
}
int waveHalf = 24_000 / hertz; // samples for half a period
byte[] buffer = new byte[waveHalf * 20];
int samples = msecs * (24_000 / 1000); // 24k (samples / sec) / 1000 (ms/sec) * time(ms)
dataLine.start(); // starts playback
int sign = 1;
for (int i = 0; i < samples; i += buffer.length) {
for (int j = 0; j < 20; j++) { // generate 10 waves into buffer
sign *= -1;
// fill from the jth wave-half to the j+1th wave-half with volume
Arrays.fill(buffer, waveHalf * j, waveHalf * (j+1), (byte) (volume * sign));
}
dataLine.write(buffer, 0, buffer.length); //
}
dataLine.drain(); // forces buffer drain to hardware
dataLine.stop(); // ends playback
}
For a more differentiated way to generate different soundwaves sinus calculations and possibly larger sample sizes are necessary. This results in significantly more complex code and is accordingly omitted here.
# Remarks
Instead of using the javax.sound.sampled Clip
, you can also use the AudioClip
which is from the applet API. It is however recommended to use Clip
since AudioClip
is just older and presents limited functionalities.