Reproducir audio y música MIDI y sampled (wav, mp3, ogg) con Java y FFmpeg

Escrito por el .
java planeta-codigo programacion
Comentarios

Java
FFmpeg

En la API de Java en el paquete javax.sound.sampled hay unas pocas clases que permiten reproducir archivos de música o sonidos y en el paquete java.sound.midi contiene clases para la música o sonidos digitales o sintetizados. Los tipos de archivos de música o sonidos soportados son wav, au, aif para los archivos sampled, y archivos midi para los digitales. Nativamente Java con las clases incluidas en el JDK no puede reproducir varios formatos de archivo de sonido populares como mp3 u ogg.

Como Java no soporta muchos tipos de archivos para reproducir los no soportados hay que hacer una conversión a alguno de los si soportados, por ejemplo de mp3 a wav. FFmpeg es un programa con el que se pueden hacer conversiones de archivos de sonido y recodificaciones de archivos de vídeo que junto con la posibilidad de invocar desde Java un proceso del sistema habilita reproducir archivos mp3 u ogg desde Java.

La clase principal de la API de sonido es AudioSystem para los archivos sampled y MidiSystem para los archivos midi, con los métodos getAudioFileTypes() y getMidiFileTypes() se obtienen los archivos de audio soportados.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package io.github.picodotdev.blogbitix.javasound;

...

public class Main {

    public static void main(String[] args) throws Exception {
        printSupportedFileTypes();

        ...
    }

    private static void printSupportedFileTypes() {
        String audioFileTypes = Arrays.stream(AudioSystem.getAudioFileTypes()).map(it -> it.getExtension()).collect(Collectors.joining(", "));
        String midiFileTypes = Arrays.stream(MidiSystem.getMidiFileTypes()).mapToObj(it -> String.valueOf(it)).collect(Collectors.joining(", "));

        System.out.printf("Audio file types: %s%n", audioFileTypes);
        System.out.printf("Midi file types: %s%n", midiFileTypes);
        System.out.printf("Mixers info: %s%n", Arrays.stream(AudioSystem.getMixerInfo()).map(it -> it.toString()).collect(Collectors.joining(", ")));
    }

    ...
}
1
2
3
Audio file types: wav, au, aif
Midi file types: 0, 1
Mixers info: Port PCH [hw:0], version 4.20.3-arch1-1-ARCH, default [default], version 4.20.3-arch1-1-ARCH, PCH [plughw:0,0], version 4.20.3-arch1-1-ARCH, PCH [plughw:0,3], version 4.20.3-arch1-1-ARCH, PCH [plughw:0,7], version 4.20.3-arch1-1-ARCH, PCH [plughw:0,8], version 4.20.3-arch1-1-ARCH, PCH [plughw:0,9], version 4.20.3-arch1-1-ARCH, PCH [plughw:0,10], version 4.20.3-arch1-1-ARCH

Para reproducir un archivo midi hay que usar las clases MidiSystem, Sequence y Sequencer.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package io.github.picodotdev.blogbitix.javasound;

...

public class Main {

    public static void main(String[] args) throws Exception {
        printSupportedFileTypes();

        String format = args[0];
        switch (format) {
            case "midi":
                playMidi();
                break;
            case "wav":
                playWav();
                break;
            case "mp3":
                playMp3();
                break;
            case "ogg":
                playOgg();
                break;
        }
    }

    ...

    private static void playMidi() throws Exception {
        InputStream is = Main.class.getResourceAsStream("/midi.mid");

        Sequencer sequencer = MidiSystem.getSequencer();
        sequencer.open();
        sequencer.setSequence(is);

        System.out.printf("Midi duration: %d seconds%n", sequencer.getMicrosecondLength() / 1000000);

        sequencer.start();

        do  {
            Thread.sleep(100);
        } while (sequencer.isRunning());

        sequencer.close();
        is.close();
    }

    ...
}
1
Midi duration: 216 seconds

Para reproducir un archivo wav hay que usar las clases AudioSystem, AudioInputStream y Clip.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package io.github.picodotdev.blogbitix.javasound;

...

public class Main {

    ...

    private static void playWav() throws Exception {
        InputStream is = Main.class.getResourceAsStream("/wav.wav");
        AudioInputStream ais = AudioSystem.getAudioInputStream(is);

        Clip clip = AudioSystem.getClip();
        clip.open(ais);

        System.out.printf("Audio format: %s%n", ais.getFormat());
        System.out.printf("Sampled duration: %d seconds%n", clip.getMicrosecondLength() / 1000000);

        clip.start();

        do  {
            Thread.sleep(100);
        } while (clip.isRunning());

        clip.close();
        ais.close();
    }

    ...
}
1
2
Audio format: PCM_SIGNED 44100.0 Hz, 16 bit, stereo, 4 bytes/frame, little-endian
Sampled duration: 240 seconds

Para reproducir un archivo mp3 o ogg hay que convertirlo al formato wav, con el comando FFmpeg y usando una tubería leyendo de su salida resultado de la conversión con un InputStream. En el siguiente ejemplo se realiza una conversión de un mp3 a wav con el formato 44100Hz, 2 canales y de 16 bits con un proceso de FFmpeg. Para reproducir un archivo ogg el código es similar.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package io.github.picodotdev.blogbitix.javasound;

...

public class Main {

    ...

    private static void playMp3() throws Exception {
        ProcessBuilder builder = new ProcessBuilder().command("ffmpeg", "-i", "src/main/resources/mp3.mp3", "-acodec", "pcm_s16le", "-ar", "44100", "-ac", "2", "-f", "wav", "pipe:1");
        Process process = builder.start();

        InputStream is = process.getInputStream();
        AudioInputStream ais = AudioSystem.getAudioInputStream(is);
        AudioFormat af = ais.getFormat();
        byte[] bytes = is.readAllBytes();

        Clip clip = AudioSystem.getClip();
        clip.open(af, bytes, 0, bytes.length);

        System.out.printf("Audio format: %s%n", ais.getFormat());
        System.out.printf("Sampled duration: %d seconds%n", clip.getMicrosecondLength() / 1000000);

        clip.start();

        do  {
            Thread.sleep(100);
        } while (clip.isRunning());

        clip.close();
        ais.close();
    }

    ...
}
1
2
Audio format: PCM_SIGNED 44100.0 Hz, 16 bit, stereo, 4 bytes/frame, little-endian
Sampled duration: 240 seconds
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package io.github.picodotdev.blogbitix.javasound;

...

public class Main {

    ...

    private static void playOgg() throws Exception {
        ProcessBuilder builder = new ProcessBuilder().command("ffmpeg", "-i", "src/main/resources/ogg.ogg", "-acodec", "pcm_s16le", "-ar", "44100", "-ac", "2", "-f", "wav", "pipe:1");
        Process process = builder.start();

        InputStream is = process.getInputStream();
        AudioInputStream ais = AudioSystem.getAudioInputStream(is);
        AudioFormat af = ais.getFormat();
        byte[] bytes = is.readAllBytes();

        Clip clip = AudioSystem.getClip();
        clip.open(af, bytes, 0, bytes.length);

        System.out.printf("Audio format: %s%n", ais.getFormat());
        System.out.printf("Sampled duration: %d seconds%n", clip.getMicrosecondLength() / 1000000);

        clip.start();

        do  {
            Thread.sleep(100);
        } while (clip.isRunning());

        clip.close();
        ais.close();
    }
}
1
2
Audio format: PCM_SIGNED 44100.0 Hz, 16 bit, stereo, 4 bytes/frame, little-endian
Sampled duration: 240 seconds

En todos los casos como se muestra en el código es posible también conocer la duración de un archivo de sonido. En realidad al usar FFmpeg cualquier tipo de archivo de sonido que soporte la conversión a wav es reproducible con Java, y no son pocos los soportados incluso muchos no tan populares como el mp3 o ogg.

1
2
$ ffmpeg -codecs
$ ffmpeg -formats

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew run --args="midi", ./gradlew run --args="mp3", ./gradlew run --args="ogg".