在 java 中播放 Base64 编码的音频文件

Play Base64 encoded audio file in java

----解决方案--------

 public class SimpleWavPlayer {

        public final static String encoded = "base64 encoded binary that 
I previously parsed and outputted then copied here";

        public static void main(String[] args)
            throws IOException,
                   InterruptedException,
                   LineUnavailableException,
                   UnsupportedAudioFileException {
                    byte[] decoded = DatatypeConverter.parseBase64Binary(encoded);
                    AudioInputStream audioIn = AudioSystem.getAudioInputStream(
                        new ByteArrayInputStream(decoded));
                    Clip song = AudioSystem.getClip();
                    song.open(audioIn);
                    song.start();

                    // Wait for clip to finish.
                    final CountDownLatch latch = new CountDownLatch(1);
                    song.addLineListener(new LineListener() {
                        @Override
                        public void update(LineEvent event) {
                            if (event.getType().equals(LineEvent.Type.STOP)) {
                                event.getLine().close();
                                latch.countDown();
                            }
                        }
                    });
                    latch.await();
            }
        }

----原题--------

我有一个包含 base64 编码的 mp3 文件的字符串。我想解码那个文件然后播放它。

 File file = new File("song.wav");
  byte[] bytes = FileUtils.readFileToByteArray(file);

  String encoded = Base64.encodeToString(bytes, 0);                                       
  byte[] decoded = Base64.decode(encoded, 0);
  AudioInputStream audioIn = AudioSystem.getAudioInputStream(/*what do i do here?*/);
  Clip song = /*what do i do here?*/;
  song.start;

我现在已经准备好字节数组了。我如何使用这个解码的字节数组来使用 clip 或 audioinputstr

播放音乐

--------编辑 1------------

我用两种不同的做事方式更新了代码。编译和运行,我可以查看编码后的字符串,但是没有声音。而不是使用 FileUtils.readFileToByteArray();我将 Path 和 Paths.get 与 File.readAllBytes() 结合使用。我无法让 FileUtils 工作,因为它要我使用 apacha 库,而我不想使用第 3 方库。我也不知道这是否是重要信息,但我正在使用 pulseaudio 的 archlinux 安装。这是代码。感谢迄今为止的所有帮助。请原谅我懒惰的异常处理。

import java.io.OutputStream;
import java.io.InputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
//import java.io.*;
//import java.util.Base64.*;
import javax.xml.bind.DatatypeConverter;
import javax.sound.sampled.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import sun.audio.*;
import java.io.*;
 public class wavestring {
    public static void main(String[] args){
        Path path = Paths.get("/home/me/Documents/audiototext/yo.wav");
        byte[] bytes = null; 
        try{
            bytes = Files.readAllBytes(path); 
        }catch(IOException e){
            System.out.print("Idk man, something happened man");
        }
        String encoded = DatatypeConverter.printBase64Binary(bytes);
        System.out.println(encoded);
        byte[] decoded = DatatypeConverter.parseBase64Binary(encoded);
        // Convert byte array to inputStream
        InputStream is = new ByteArrayInputStream(decoded);
        // // Get AudioInputStream from InputStream                 
        AudioInputStream audioIn = null;
        try{
            audioIn = AudioSystem.getAudioInputStream(is);
        }catch(UnsupportedAudioFileException u){
            System.out.println("Well bruh...something happened");
        }catch(IOException e){
            System.out.println("brooooo");
        }
        // // Acquire audio format and create a DataLine.Infoobject: 
        AudioFormat format = audioIn.getFormat();

        /*
        //METHOD 3
        AudioInputStream audioIn = null;
        try{
            audioIn = AudioSystem.getAudioInputStream(is);
        }catch(UnsupportedAudioFileException u){
            System.out.println("Well bruh...something happened");
        }catch(IOException e){
            System.out.println("brooooo");
        }
        // // Acquire audio format and create a DataLine.Infoobject: 
        AudioFormat format = audioIn.getFormat();
        Clip song = null;
        try{
            song = AudioSystem.getClip();
            song.open(audioIn);
        }catch(LineUnavailableException l){
        }catch(IOException e){
        }
        song.start();
        */


        //METHOD 2
        SourceDataLine source_line = null;
        try{
            source_line = (SourceDataLine) AudioSystem.getLine(new DataLine.Info(SourceDataLine.class, format));
            source_line.open(format);
        }catch(LineUnavailableException l){
            System.out.println("yooooooo");
        }
        byte[] buffer = new byte[524288];
        int bytes_read = 0;
        while(true){
            try{
                bytes_read = audioIn.read(buffer);
            }catch(IOException e){
                System.out.println("idk man");
            }
            if(bytes_read < 0)
                break;
            source_line.write(buffer, 0, bytes_read);
        }
        try{
            audioIn.close();
        }catch(IOException e){
            System.out.println("yooooooooooo man");
        }
        source_line.drain();
        source_line.close();


        //METHOD 1
        /*DataLine.Info info = new DataLine.Info(Clip.class, format);
        Clip song = null;
        try{
            song = (Clip) AudioSystem.getLine(info);
        }catch(LineUnavailableException l){
            System.out.println("We were so close but something happened man");
        }
        song.start();*/
    }
}

未经测试,但您可以将其用作指南:

File file = new File("song.wav");
byte[] bytes = FileUtils.readFileToByteArray(file);

String encoded = Base64.encodeToString(bytes, 0);                                       
byte[] decoded = Base64.decode(encoded, 0);
// Convert byte array to inputStream
InputStream is = new ByteArrayInputStream(decoded);
// Get AudioInputStream from InputStream
AudioInputStream audioIn = AudioSystem.getAudioInputStream(is);
// Acquire audio format and create a DataLine.Infoobject: 
AudioFormat format = audioIn.getFormat();
DataLine.Info info = new DataLine.Info(Clip.class, format);
Clip song = (Clip) AudioSystem.getLine(info);
song.start();

部分基于此:link

您所有问题的答案都在文档中。

首先,让我们看一下documentation for AudioSystem。有五种 getAudioInputStream 方法。两个采用显式 AudioFormat 参数,这些参数不适用于播放 .wav 文件。其余三个方法分别采用 File、InputStream 和 URL。

既然你已经有了一个字节数组,最好的选择是将字节包装在一个ByteArrayInputStream中。现在我们有了一个 InputStream,我们可以将其传递给 getAudioInputStream 方法。

如果您想知道如何获取 Clip 对象,文档再次成为您最好的朋友。如果你去 documentation for Clip, and look at the very top of the page, you'll see a navigation row with several links, including a "USE" link. Follow that link, and you will get a list of all methods in the Java SE API which return a Clip or take a Clip as an argument.

刚好这是一个短名单:截至Java8,只有两个方法,都是静态的,可以return一个Clip。一个采用零参数,而另一个采用显式 Mixer.Info。通常情况下,您只是想通过默认的 Mixer 播放声音,因此只需使用零参数 getClip() 方法来获取新的 Clip。

现在你有了一个剪辑,但它还没有与你的 AudioInputStream 相关联。文档再次为我们提供了帮助。 documentation for AudioSystem.getClip() 状态:

The returned clip must be opened with the open(AudioFormat) or open(AudioInputStream) method.

因此,如果我们再次 return 到 Clip documentation,我们会看到两个 open 方法(截至 Java 8)。其中之一将 AudioInputStream 作为其唯一参数。这就是您要使用的那个。

最后,您似乎已经知道,您必须通过调用其继承的 start() 方法来启动剪辑。

现在我们有足够的信息来编写代码了:

AudioInputStream audioIn = AudioSystem.getAudioInputStream(
    new ByteArrayInputStream(decoded));
Clip song = AudioSystem.getClip();
song.open(audioIn);
song.start();

更新: 以上应该可以播放声音了。这是我为测试它而编写的完整程序:

import java.io.IOException;
import java.io.ByteArrayInputStream;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.Files;

import java.util.concurrent.CountDownLatch;

import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;

public class SimpleWavPlayer {
    public static void main(String[] args)
    throws IOException,
           InterruptedException,
           LineUnavailableException,
           UnsupportedAudioFileException {

        for (String arg : args) {
            Path file = Paths.get(arg);
            byte[] decoded = Files.readAllBytes(file);

            AudioInputStream audioIn = AudioSystem.getAudioInputStream(
                new ByteArrayInputStream(decoded));
            Clip song = AudioSystem.getClip();
            song.open(audioIn);
            song.start();

            // Wait for clip to finish.
            final CountDownLatch latch = new CountDownLatch(1);
            song.addLineListener(new LineListener() {
                @Override
                public void update(LineEvent event) {
                    if (event.getType().equals(LineEvent.Type.STOP)) {
                        event.getLine().close();
                        latch.countDown();
                    }
                }
            });
            latch.await();
        }
    }
}

(代码主要取自 this answer

要在将文件读入内存后获得 AudioInputStream,您可以使用:

AudioInputStream audio_in = AudioSystem.getAudioInputStream(new ByteArrayInputStream(bytes));

或者,您可以直接从文件中读取。如果文件很大,这可以节省内存(通过不将所有文件一次存储在内存中)。 AudioSystem 有一个方便的方法:

AudioInputStream audio_in = AudioSystem.getAudioInputStream(file);

一旦你有一个 AudioInputStream 可以让你从文件中读取音频,你需要一个 SourceDataLine 让你通过扬声器播放音频。

AudioFormat audio_format = audio_in.getFormat();
SourceDataLine source_line = (SourceDataLine) AudioSystem.getLine(new DataLine.Info(SourceDataLine.class, audio_format));
source_line.open(audio_format);

然后您可以从文件中读取音频,并将其发送到扬声器,直到到达文件末尾:

byte[] buffer = new byte[65536]; // the exact size doesn't matter too much; I chose 64 KiB
while(true) {
    int bytes_read = audio_in.read(buffer);
    if(bytes_read < 0)
        break; // end of file reached
    source_line.write(buffer, 0, bytes_read);
}

注意SourceDataLine只会缓冲一定量的数据;一旦缓冲区已满,尝试写入更多数据将阻塞(即让您的程序等待),直到播放完已写入的数据。这意味着上面的循环只有在文件的大部分内容播放完毕后才会结束。

完成后,剩下的就是清理:

audio_in.close();
source_line.drain();
source_line.close();

SourceDataLine.drain 等待线路缓冲的任何数据完成播放。如果不调用,关闭SourceDataLine后文件会立即停止播放,可能会导致最后几秒被截断。