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

时间:2015-01-23 01:36:14

标签: java audio base64 bytearray encode

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

 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库,我不想使用第三方库。此外,我不知道这是否是重要信息,但我正在使用当前使用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();*/
    }
}

3 个答案:

答案 0 :(得分:2)

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

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

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

如果您想知道如何获取Clip对象,那么文档又是您最好的朋友。如果您转到documentation for Clip,并查看页面顶部,您会看到一个包含多个链接的导航行,包括“USE”链接。按照该链接,您将获得list of all methods in the Java SE API which return a Clip or take a Clip as an argument.

恰好这是一个简短的列表:从Java 8开始,只有两种方法,都是静态的,可以返回一个Clip。一个采用零参数,而另一个采用显式的Mixer.Info。通常,您只想通过默认的混音器播放声音,因此只需使用零参数getClip()方法来获取新的剪辑。

所以现在你有一个Clip,但它还没有与你的AudioInputStream相关联。再次,文档来救我们。 documentation for AudioSystem.getClip()州:

  

必须使用open(AudioFormat)open(AudioInputStream)方法打开返回的剪辑。

因此,如果我们再次返回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();
        }
    }
}

答案 1 :(得分:1)

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

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

答案 2 :(得分:1)

(代码主要来自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时文件将立即停止播放,这可能导致最后几秒被切断。