1

----Solution--------

 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();
            }
        }

----Original question--------

I have a string that contains a base64 encoded mp3 file. I want to decode that file then play it.

 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;

I now i have the byte array ready. How can I use this decoded byte array to play music using clip or audioinputstr

--------EDIT 1------------

I've updated the code with two different ways of doing things. Both compile and run, and I can view the encoded string, but there is no sound. Instead of using FileUtils.readFileToByteArray(); I used Path and Paths.get in conjunction with File.readAllBytes(). I could not get FileUtils working because it wanted me to use the apacha library and I do not want to use 3rd party libraries. Also I don't know if this is important information, but I am on an archlinux installation that is currently using pulseaudio. Here is the code. Thanks for all the help thus far. Excuse my lazy exception handling.

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();*/
    }
}
Painguy
  • 565
  • 6
  • 13
  • 25
  • You know what encoding it and then decoding it doesn't change anything, right? You might as well write `byte[] decoded = bytes;`,or just use `bytes` directly. (Well, it does create a new array, but there's Arrays.copyOf for that) – user253751 Jan 23 '15 at 01:38
  • yeah this is true, but I was just writing all this for the sake of practice and getting to know how to use the base64 class. – Painguy Jan 23 '15 at 01:41

3 Answers3

2

The answers to all of your questions are in the documentation.

First, let's look at the documentation for AudioSystem. There are five getAudioInputStream methods. Two take explicit AudioFormat arguments, which don't apply to playing a .wav file. The remaining three methods take a File, InputStream and URL, respectively.

Since you already have a byte array, the best choice is to wrap the bytes in a ByteArrayInputStream. Now we have an InputStream we can pass to a getAudioInputStream method.

If you're wondering how to obtain a Clip object, the documentation is, again, your best friend. If you go to the 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.

It just so happens that this is a short list: As of Java 8, there are only two methods, both static, which can return a Clip. One takes zero arguments, while the other takes an explicit Mixer.Info. Normally, you just want to play sounds through the default Mixer, so just use the zero-argument getClip() method to obtain a new Clip.

So now you have a Clip, but it isn't associated with your AudioInputStream yet. Once again, the documentation comes to our rescue. The documentation for AudioSystem.getClip() states:

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

So if we return to the Clip documentation again, we see two open methods (as of Java 8). One of them takes an AudioInputStream as its only argument. That's the one you want to use.

Finally, as you seem to already know, you must start the clip by calling its inherited start() method.

Now we have enough information to write the code:

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

Update: The above should be able to play sound for you. Here is the complete program I wrote to test it:

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();
        }
    }
}
VGR
  • 40,506
  • 4
  • 48
  • 63
  • Unfrotunatley much like all the other solutions here this managed to perfectly compile, and provide an encoded string, but there is no sound. – Painguy Jan 23 '15 at 05:44
  • 1
    @Painguy Updated my answer. This works for me in Linux 3.2.0-amd64, using Oracle Java 1.7.0_76. – VGR Jan 23 '15 at 20:54
  • Thank you for your help. CountDownLatch bit fixed the issue. I'll have to look into why and how. I changed the code you provided so that I already start with a base64 encoded audio file in a String called encoded, and I simply fed that string through the .parseBase64Binary function and do the rest normally. Thanks again for your help and everyone else who contributed. Unfortunately I cant store more than about 8 secs worth of audio in a string as a constant, but I think I have a better handle on audio input streams to figure that out. – Painguy Jan 24 '15 at 08:59
1

Untested but you can use this as a guide:

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();

Partially based on this: link

Nick Vasic
  • 1,886
  • 1
  • 11
  • 6
  • I've implemented what you posted, and it compiles perfectly fine, and i can even see the encoded string, but there is no sound. I've updated the OP with more details. Thanks for your help so far. – Painguy Jan 23 '15 at 05:31
1

(code mostly taken from this answer)

To get an AudioInputStream after reading the file into memory, you could use:

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

Alternatively, you could read from the file directly. If the file is large, this saves memory (by not storing all of it in memory at once). AudioSystem has a convenient method for this:

AudioInputStream audio_in = AudioSystem.getAudioInputStream(file);

Once you have an AudioInputStream which lets you read audio from the file, you need a SourceDataLine to let you play audio through the speakers.

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

and then you can read audio from the file, and send it to the speakers, until you get to the end of the file:

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);
}

Note that the SourceDataLine will only buffer a certain amount of data; once the buffer is full, trying to write more data will block (i.e. make your program wait) until the already-written data has been played. That means the above loop will only finish once most of the file has been played.

Once it does, all that's left is to clean up:

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

SourceDataLine.drain waits for any data the line has buffered to finish playing. If you don't call it, then the file will stop playing immediately when you close the SourceDataLine, which might cause the last few seconds to be cut off.

Community
  • 1
  • 1
user253751
  • 57,427
  • 7
  • 48
  • 90
  • Thanks for the help thus far. Everything compiles but there is no sound. I've updated the OP with more detail. – Painguy Jan 23 '15 at 05:30