I´m learning how to play sound in java but with advanced controls.
I´ve found one problem: The javax.sound.sampled.AudioInputStream doesn´t support Mp3 files, and i´m running out of ideas to find how to get control of panning.
I managed to play an Mp3 file using javazoom.jl.player.advanced.AdvancedPlayer, but it doesn´t have a panning control, or i haven´t founded it.
My actual code opens a file, if the format is compatible with AudioInputStream, it plays only the right channel. If the format doesn´t, it plays using AdvancedPlayer.
Do you know a way to get panning control of mp3 files?
My code here:
import javazoom.jl.decoder.JavaLayerException;
import javazoom.jl.player.advanced.AdvancedPlayer;
import javax.sound.sampled.*;
import javax.swing.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class AudioPlayerExample2 {
private static final int BUFFER_SIZE = 4096;
public static void main(String[] args) throws IOException, LineUnavailableException, JavaLayerException {
JFileChooser fileChooser = new JFileChooser();
fileChooser.showOpenDialog(null);
new AudioPlayerExample2().play(fileChooser.getSelectedFile());
}
void play(File file) throws IOException, LineUnavailableException, JavaLayerException {
AudioInputStream audioStream;
try {
audioStream = AudioSystem.getAudioInputStream(file);
AudioFormat format = audioStream.getFormat();
System.err.println(format.toString());
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
SourceDataLine audioLine = (SourceDataLine) AudioSystem.getLine(info);
audioLine.open(format);
audioLine.start();
FloatControl pan = (FloatControl) audioLine.getControl(FloatControl.Type.PAN);
byte[] bytesBuffer = new byte[BUFFER_SIZE];
int bytesRead = -1;
while ((bytesRead = audioStream.read(bytesBuffer)) != -1) {
pan.setValue((float) (1));
audioLine.write(bytesBuffer, 0, bytesRead);
}
audioLine.drain();
audioLine.close();
audioStream.close();
} catch (UnsupportedAudioFileException e) {
FileInputStream fis = new FileInputStream(file);
AdvancedPlayer player = new AdvancedPlayer(fis);
player.play();
}
}
}
Panning and volume controls are system dependent and can sometimes be a bit flaky even if they are in place. For example, if you change the volume or pan setting too much at once, the discontinuity causes a click.
One solution is to go in there on a per-frame basis and make the changes yourself. For example, see "Manipulating the Audio Data Directly" at the end of the tutorial Processing Audio with Controls.
For an example, check out the code from the next tutorial on the trail: Using Files and Format Converters. Look under the heading "Reading Sound Files" and look for the comment in the code "\ Here, do something useful..."
I invite you to also take a look at the code I wrote and have made available, a class called AudioCue that has real time panning as well as real time volume and pitch playback controls. I've added smoothing (1024 steps for panning changes) to help mitigate the possibility of discontinuities.
It will be up to you to take the mp3 file and decode it into an array of audio data. I think that the javazoom libraries made available on github should give you enough code access to figure out how to do this (I did it for ogg/vorbis decoding). Once you have a float array of audio data (stereo, signed floats ranging from -1 to 1), this can be directly loaded into AudioCue.
First of all, thanks to Andrew Thompson and Phil Freihofner, I feel very good about being part of this community and having someone to trust. You really make feel happy :)
I leave here the full code that does exactly what I wanted.
As the JavaZoom MP3 SPI Documentation says: Make sure that JLayer, Tritonus and MP3SPI librairies are available in your CLASSPATH.
import javax.sound.sampled.*;
import javax.swing.*;
import java.io.File;
import java.io.IOException;
public class Test {
public static void main(String[] args) throws IOException,
UnsupportedAudioFileException, LineUnavailableException {
JFileChooser chooser = new JFileChooser();
chooser.showOpenDialog(null);
String path = chooser.getSelectedFile().getAbsolutePath();
System.err.println(path);
File file = new File(path);
AudioInputStream baseStream = AudioSystem.getAudioInputStream(file);
AudioFormat baseFormat = baseStream.getFormat();
System.err.println(baseFormat.toString());
AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
baseFormat.getSampleRate(),
16, baseFormat.getChannels(), baseFormat.getChannels() * 2,
baseFormat.getSampleRate(), true);
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
AudioInputStream stream = AudioSystem.getAudioInputStream(format, baseStream);
SourceDataLine audioLine = (SourceDataLine) AudioSystem.getLine(info);
audioLine.open(format);
audioLine.start();
FloatControl pan = (FloatControl) audioLine.getControl(FloatControl.Type.PAN);
pan.setValue(1);
int BUFFER_SIZE = 4096;
byte[] buffer = new byte[BUFFER_SIZE];
int read = -1;
while((read = stream.read(buffer)) != -1){
audioLine.write(buffer, 0, read);
}
audioLine.drain();
audioLine.close();
}
}
Related
I'm quite a newbie in JAVA and I am trying to read a clip. Here is my code :
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
public class TestClipBis {
protected static AudioFormat audioFormat;
public static void main(String[] args) throws Exception {
// Specification of the sound to play
// No control. We assume that the sound can be played on audio system
//File soundFile = new File("chimes.wav");
File soundFile = new File("test.wav");
InputStream is = new FileInputStream(soundFile);
InputStream bufferedIn = new BufferedInputStream(is);
//AudioInputStream sound = AudioSystem.getAudioInputStream(soundFile);
AudioInputStream sound = AudioSystem.getAudioInputStream(bufferedIn);
audioFormat = sound.getFormat();
System.out.println(audioFormat);
// Loading the sound into the memory (a Clip)
DataLine.Info info = new DataLine.Info(Clip.class, sound.getFormat());
System.out.println(info);
//Clip clip = (Clip) AudioSystem.getClip();
Clip clip = (Clip) AudioSystem.getLine(info);
System.out.println("Sound frame lenght : "+sound.getFrameLength());
System.out.println("Clip FrameLength before opening : "+clip.getFrameLength());
System.out.println("Clip will open - "+info);
System.out.println("Info format : "+info.getLineClass());
// Check before this line that everything is in memory
// Yes, but how ?
clip.open(sound);
System.out.println("Clip is open");
System.out.println("Clip FrameLength after opening : "+clip.getFrameLength());
// Due to a bug in Java Sound,
// we explicitly out of the VM when the sounds stop
clip.addLineListener(new LineListener() {
public void update(LineEvent event) {
if (event.getType() == LineEvent.Type.STOP) {
System.out.println("Methode de sortie");
event.getLine().close();
System.exit(0);
}
}
});
// Playing the clip
clip.start();
System.out.println("IsActive : "+clip.isActive());
//clip.close();
}
}
My problem is how to be sure that the clip is loaded in memory before opening and playing it ? With the above code, when I open and play the sound file, I have few seconds of playing but never the same length, randomly, and never the full song.
Or should I use something else than a clip to play a song? But I want to "move" into the song and not only streaming it from the start to the end.
Edit:
Ok, I tried few things. First, I tried to see if the "ByteArrayOuptputStream" was written. I had a "println" in the loop and yes, all is written but it don't fix the problem.
Then, when I open the clip, I tried to add the parameters : audioformat, bytearray, startpoint, bufferlength. Nothing better.
Then, I noticed that when the sounds stop, the method to exit was used. So, I tried to "mute" that method (with comment signs). The result is different : it read the file but the sound is jerky. And when I check the CPU use, it's around 100%. Is it a first clue to guess what's the problem ?
I tried to make a loop that indicates the frameposition after the start : all the frames are read but the sound is still jerky.
I also tried the thead.sleep before and after the start method : nothing better.
So, here is the code I use. Many code parts are between "comment quotes" because they are try, unsuccessfull...
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
public class TestBufferedClip {
protected static AudioFormat audioFormat;
public static ByteBuffer buffer;
public static void main(String[] args) throws Exception {
// Specification of the sound to play
// No control. We assume that the sound can be played on audio system
//File soundFile = new File("chimes.wav");
File soundFile = new File("test.wav");
FileInputStream fis = new FileInputStream(soundFile);
ByteArrayOutputStream baos = new ByteArrayOutputStream((int)soundFile.length());
byte[] buf = new byte[1024];
int n = 0;
int loop = 0;
while ((n=fis.read(buf))>=0) {
baos.write(buf);
buf=new byte[1024];
System.out.println("Loop = "+loop);
loop+=1;
}
byte[] ba = baos.toByteArray();
System.out.println("ByteArray size : "+ba.length);
ByteArrayInputStream bais = new ByteArrayInputStream(ba);
//AudioInputStream sound = AudioSystem.getAudioInputStream(soundFile);
AudioInputStream sound = AudioSystem.getAudioInputStream(bais);
audioFormat = sound.getFormat();
System.out.println(audioFormat);
// Loading the sound into the memory (a Clip)
DataLine.Info info = new DataLine.Info(Clip.class, sound.getFormat());
System.out.println("Info :"+info);
//Clip clip = (Clip) AudioSystem.getClip();
Clip clip = (Clip) AudioSystem.getLine(info);
System.out.println("Sound frame lenght : "+sound.getFrameLength());
System.out.println("Info format : "+info.getLineClass());
// Check before this line that everything is in memory
// Yes, but how ?
clip.open(audioFormat, ba, 0, ba.length);
//clip.open(sound);
System.out.println("Clip is open");
System.out.println("Clip FrameLength after opening : "+clip.getFrameLength());
// Due to a bug in Java Sound,
// we explicitly out of the VM when the sounds stop
/*
clip.addLineListener(new LineListener() {
public void update(LineEvent event) {
if (event.getType() == LineEvent.Type.STOP) {
System.out.println("Methode de sortie");
event.getLine().close();
System.exit(0);
}
}
});
*/
// Playing the clip
System.out.println("Before thread sleep");
try {
Thread.sleep(31000);
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("After thread sleep");
clip.start();
System.out.println("IsActive : "+clip.isActive());
/*
while (clip.isActive()==true) {
System.out.println("Position = "+clip.getFramePosition());
}
*/
//clip.close();
}
}
#Phil Freihofner :
I thought about your solution to read and discards data until I reach my "start" point. You wrote "In order to start at a point within the audio file, using a SourceDataLine, you would have to read and discard data from the audio input line until you got to the desired starting spot". How do you do that ? When I use the "SourceDataLine" method, my start method is a loop with a line.write(bytes, 0, bytesreads); to point the sound on the speakers.
So, how do you just read and discard ? I didn't find any "read" method with the line.
javax.sound.sampled supports two objects for playing back audio. The Clip, as you are using, has to be loaded completely into memory before one can play it back. On the plus side, it is also easy to position the playback to start from within the Clip, either using microseconds or frame position.
I see no benefit from first loading the sound into a byte buffer. That is a redundant level of buffering for Clips. I'd only do it if you were trying to do DSP or something else that requires getting to the data, something beyond the Clip's built in ability to set a start point.
If you are able to preload the possible audio choices as Clips before they are selected, that might be the best solution, as long as you don't run out of RAM.
The other option for playback is a SourceDataLine. It reads and plays back the file on a per-buffer basis. Thus, it can start up quicker than an unloaded Clip (no need to load the entire file into memory first). However, once the Clip is preloaded, the Clip will play back without having to do repeated file loads.
In order to start at a point within the audio file, using a SourceDataLine, you would have to read and discard data from the audio input line until you got to the desired starting spot. You can do this by counting frames (the format will tell you the number of bytes per frame). This reading and discarding would disrupt timing a bit, but my experience has been that reading and discarding audio data is a couple of orders of magnitude faster than playback, since playback involves blocking queues to keep the output at the required frame rate.
Check the Java Sound Tutorials for more info, which includes links to the Clip and SourceDataLine APIs.
Here is an example of the loading of a Clip:
File soundFile = new File("test.wav");
AudioInputStream sound = AudioSystem.getAudioInputStream(soundFile);
DataLine.Info info = new DataLine.Info(Clip.class, sound.getFormat());
Clip clip = (Clip) AudioSystem.getLine(info);
clip.open(sound);
The data from test.wav should now be in RAM. All that other stuff you have using byte buffers and buffered lines is unnecessary.
When you are ready to play, use clip.setMicrosecondPosition(long milliseconds) to set your sound to start at the desired location, (not needed if you are starting from the beginning, unless you've already played the Clip, in which case the position will be where it was when you stopped). Then use clip.start() to commence playing.
IMPORTANT NOTE: playback will end prematurely if the program running it exits. One way to test this is to put a Thread.sleep(long milliseconds) command after the clip.start(), where the value of milliseconds is longer than the length of the clip. But that is not a real solution, just a diagnostic to prevent the program from closing the clip playback thread. You should be handling keeping the program running from the main threads, not the thread with the audio playback.
First read whole into a byte buffer. Do this by copying all content from file to ByteArrayOutputStream. This way you will have whole media content in memory. Now you can wrap array from ByteArrayOutputStream.toByteArray() into ByteArrayInputStream and provide that pure in-memory stream as audio input stream.
This question already has answers here:
How to play .wav files with java
(10 answers)
Closed 8 years ago.
----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();*/
}
}
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();
}
}
}
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
(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.
This question is probably not even going to be asked correctly, but I promise you I'm doing my best. Here's the scenario: I wrote a little Java app that receives an audio stream from a server. Now, when I redirect the binary stream to a file, and pipe that file to mplayer, the audio is played correctly. What I want now though is to play the audio from my own app. Here's what I got so far:
The codec mplayer uses to play the stream:
AUDIO: 22050 Hz, 2 ch, s16le, 0.0 kbit/0.00% (ratio: 0->88200)
Selected audio codec: [ffaac] afm: ffmpeg (FFmpeg AAC (MPEG-2/MPEG-4 Audio))
What I coded so far:
public class StreamPlayer {
public final AudioFormat audioFormat;
public final DataLine.Info info;
public final SourceDataLine soundLine;
public StreamPlayer() throws LineUnavailableException {
audioFormat = new AudioFormat(22050, 16, 2, true, true);
info = new DataLine.Info(SourceDataLine.class, audioFormat, 1500);
soundLine = (SourceDataLine) AudioSystem.getLine(info);
}
public void startSoundLine() throws LineUnavailableException {
soundLine.open(audioFormat);
soundLine.start();
}
public void playStream(byte[] buffer) {
soundLine.write(buffer, 0, buffer.length);
}
}
and I call the playStream function for every received packet. No errors are reported, and no sound is heard. Am I even close to doing this, or off by a long shot?
P.S. I found some third party libraries on google, but I'd really like to keep them as a last resort.
Thank you!
Get your Bytes into a ByteArrayInputStream and feed this into the streamplayer instead...
ByteArrayInputStream audioStream = new ByteArrayInputStream(buffer);
I have this simple code to concatenate two wav files. Its pretty simple and the code runs without any errors. But there is a problem with the output file. The output file generated does not play, and surprisingly its size is only 44 bytes whereas my input files "a.wav" & "b.wav" are both more than 500Kb in size.
Here is my code:
import java.io.File;
import java.io.IOException;
import java.io.SequenceInputStream;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
public class WavConcat {
public static void main(String[] args) {
String wFile1 = "./sounds/a.wav";
String wFile2 = "./sounds/b.wav";
try {
AudioInputStream clip1 = AudioSystem.getAudioInputStream(new File(wFile1));
AudioInputStream clip2 = AudioSystem.getAudioInputStream(new File(wFile2));
AudioInputStream appendedFiles =
new AudioInputStream(
new SequenceInputStream(clip1, clip2),
clip1.getFormat(),
clip1.getFrameLength() + clip2.getFrameLength());
AudioSystem.write(appendedFiles,
AudioFileFormat.Type.WAVE,new File("./sounds/ab.wav"));
} catch (Exception e) {
e.printStackTrace();
}
}
}
Try this kind of structure. This worked for me
List audioInputStreamList = new ArrayList();
String wFile1 = "./sounds/a.wav";
String wFile2 = "./sounds/b.wav";
AudioInputStream audioInputStream1 = AudioSystem.getAudioInputStream(new File(wFile1));
AudioInputStream audioInputStream2 = AudioSystem.getAudioInputStream(new File(wFile2));
audioInputStreamList.add(audioInputStream1);
audioInputStreamList.add(audioInputStream2);
AudioFormat audioFormat = audioInputStream1.getFormat(); // audioInputStream2 format also same
AudioInputStream udioInputStream = new SequenceAudioInputStream(audioFormat,audioInputStreamList);
AudioSystem.write(audioInputStream, AudioFileFormat.Type.WAVE,new File("./sounds/ab.wav"));
UPDATE
check for SequenceAudioInputStream
clip1.getFormat() returns-->
MPEG2L3 24000.0 Hz, unknown bits per sample, mono, unknown frame size, 41.666668 frames/second
clip2.getFormat() returns-->
MPEG2L3 24000.0 Hz, unknown bits per sample, mono, unknown frame size, 41.666668 frames/second
That is an odd format. I can imagine the 'unknown bits per sample' is causing a problem, but also the MPEG2L3, since JavaSound has no inbuilt encoder for MP3. It seems like they are not encoded properly. Try loading them in sound editing software and save them as a type of WAV or AU that Java Sound can understand 'out of the box'. Hopefully the editing software:
Can understand the broken MP3, and..
Will write a valid WAV or AU.
If you can convert them to 8 bit mono & 8KHz during the conversion, it might reduce the byte[] size by a factor of 6 to 1. 8KHz is considered good enough to understand speech, and for this use you need to serve the bytes of the combined sound out to the browser - so reducing it in size is crucial.
Here is my code that concatenates four wav files and produces wavAppended.wav. This concatenated file nicely plays in Windows Media Player.
But through the PlaySound class, only the one.wav can be heard.
Can anyone help?
class PlaySound extends Object implements LineListener
{
File soundFile;
JDialog playingDialog;
Clip clip;
public void PlaySnd(String s) throws Exception
{
JFileChooser chooser = new JFileChooser();
soundFile = new File(s);
Line.Info linfo = new Line.Info(Clip.class);
Line line = AudioSystem.getLine(linfo);
clip = (Clip) line;
clip.addLineListener(this);
AudioInputStream ais = AudioSystem.getAudioInputStream(soundFile);
clip.open(ais);
clip.start();
}
public void update(LineEvent le)
{
LineEvent.Type type = le.getType();
playingDialog.setVisible(false);
clip.stop();
clip.close();
}
}
public class Main
{
public static void main(String[] args)
{
int i;
String wavFile[] = new String[4];
wavFile[0] = "D://one.wav";
wavFile[1] = "D://two.wav";
wavFile[2] = "D://three.wav";
wavFile[3] = "D://space.au";
AudioInputStream appendedFiles;
try
{
AudioInputStream clip0=AudioSystem.getAudioInputStream(new File(wavFile[0]));
AudioInputStream clip1=AudioSystem.getAudioInputStream(new File(wavFile[1]));
AudioInputStream clip3;
for (i=0;i<4;i++)
{
appendedFiles = new AudioInputStream(
new SequenceInputStream(clip0, clip1),
clip0.getFormat(),
clip0.getFrameLength() + clip1.getFrameLength());
AudioSystem.write(appendedFiles, AudioFileFormat.Type.WAVE, new File("D:\\wavAppended.wav"));
clip3 = AudioSystem.getAudioInputStream(new File("D:\\wavAppended.wav"));
clip0=clip3;
clip1 = AudioSystem.getAudioInputStream(new File(wavFile[i+2]));
}
PlaySound p = new PlaySound();
p.PlaySnd("D://wavAppended.wav");
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
WAV files don't work that way -- you can't just throw multiple files together (same as you can't concatenate JPEG images, for instance), as there's a header on the data, and there are multiple different formats the data may be in. I'm surprised that the file loads at all.
To get you started with the WAV processing you may have a look at my small project. It can copy and paste WAV files together based on an time index file. The project should contain all the Java WAV processing you need (using javax.sound.sampled). The Butcher implementation and Composer contain the actual processing.
The idea is simple: take input audio files and create a index of words
contained in these files. The index entry is the word, start time and
end time. When a new sentence is created it will be stitched together
with single words taken from the index.
The AudioInputStream is the main class to interact with the Java Sound
API. You read audio data from it. If you create audio data you do this
by creating a AudioInputStream the AudioSystem can read from. The
actual encoding is done by the AudioSystem implementation depending on
the output audio format.
The Butcher class is the one concerned with audio files. It can read
and write audio files and create AudioInputStreams from an input byte
array. The other interesting think the Butcher can is cutting samples
from a AudioInputStream. The AudioInputStream consists of frames that
represent the samples of the PCM signal. Frames have a length of
multiple bytes. To cut a valid range of frames from the
AudioInputStream one has to take the frame size into account. The
start and end time in milliseconds have to be translated to start byte
and end bytes of the start frame and end frame. (The start and end
data is stored as timestamps to keep them independent from the
underlying encoding of the file used.)
The Composer creates the output file. For a given sentence it takes
the audio data for each word from the input files, concatenates the
audio data and writes the result to disk.
In the end you'll need some understanding of the PCM and the WAV format. The Java sound API does not abstract that away.
In above given example you need to use the SequenceInputStream then it will work fine. please find my code below to join two files.
import java.io.File;
import java.io.IOException;
import java.io.SequenceInputStream;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
public class JoinWav{
public static void main(String... args) throws Exception{
String wav_1 = "1497434542598100215.wav";
String wav_2 = "104860397153760.wav";
AudioInputStream stream_1 = AudioSystem.getAudioInputStream(new File(wav_1));
AudioInputStream stream_2 = AudioSystem.getAudioInputStream(new File(wav_2));
System.out.println("Info : Format ["+stream_1.getFormat()+"] Frame Length ["+stream_1.getFrameLength()+"]");
AudioInputStream stream_join = new AudioInputStream(new SequenceInputStream(stream_1,stream_2),stream_1.getFormat(),stream_1.getFrameLength()+stream_2.getFrameLength());
AudioSystem.write(stream_join,AudioFileFormat.Type.WAVE,new File("join.wav"));
}
}