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);
Related
I am working on an audio speaker project. I receive the real-time byte[] message from the front-end thus I built a speaker instance with javax.sound package to play the message.
The problem is: I get distorted audio when I change the sound channel number to 2 (that means stereo audio). Should I arrange some kind of stereo microphone or other specific devices to fix it? Or my way to initialize the speaker is not correct?
Here is the code to initialize the speaker:
import javax.sound.sampled.*;
public class SpeakerWriter {
public static final AudioFormat FORMAT_1 = new AudioFormat(44100.0f, 16, 1, true, false);
//16 is sample size in bits & 1 is channel number
private static AudioFormat format = FORMAT_1;
private SourceDataLine speakers;
public SourceDataLine getSpeakers() {
try {
DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, format);
speakers = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
speakers.open(format);
speakers.start();
}
catch(LineUnavailableException e) {
e.printStackTrace();
}
return speakers;
}
}
By the way, I reconstructed the code with Golang but the problem still exists.
Do you know for sure that your file is BigEndian? If not, I would try changing the BigEndian value to 'false'. I think this is considerably more common, and getting it wrong tends to create distortion.
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();
}
}
I have been trying to manually read a wav file in Java and read an array of bytes then write to an audio buffer for playback. I am receiving playback but it is heavily distorted. Java sound supports 16 bit sample rates but not 24-bit.
I went in to Logic 9 and exported a 24-bit audio file in to 16-bit and then used with my program. Originally, the 24-bit samples would produces white noise. Now I can hear my sample but very distorted and sounds like it has been bit crushed.
Can anyone help me to get a clean signal?
I am very new to audio programming but I am currently working on a basic Digital Audio Workstation.
import javax.sound.sampled.*;
import javax.sound.sampled.DataLine.Info;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.io.*;
public class AudioData {
private String filepath;
private String filepath1;
private File file;
private byte [] fileContent;
private Mixer mixer;
private Mixer.Info[] mixInfos;
private AudioInputStream input;
private ByteArrayOutputStream byteoutput;
public static void main (String [] args) {
AudioData audiodata = new AudioData();
}
public AudioData () {
filepath = "/Users/ivaannagen/Documents/Samples/Engineering Samples - Obscure Techno Vol 3 (WAV)/ES_OT3_Kit03_Gmin_130bpm/ES_OT3_Kit03_FX_Fast_Snare_Riser_Gmin_130bpm.wav";
filepath1 = "/Users/ivaannagen/Documents/Samples/dawsampletest.wav";
file = new File (filepath1);
readAudio();
}
public void readAudio () {
mixInfos = AudioSystem.getMixerInfo();
mixer = AudioSystem.getMixer(mixInfos[0]);
AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 44100, 16, 2, 4, 44100, false);
// set up an audio format.
try {
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); // creates data line with class type and audio format.
SourceDataLine source = (SourceDataLine) AudioSystem.getLine(info);
System.out.println("Size of data line buffer: " + source.getBufferSize());
fileContent = new byte [source.getBufferSize() / 50];
byteoutput = new ByteArrayOutputStream();
input = AudioSystem.getAudioInputStream(file);
int readBytes = 0;
while ((readBytes = input.read(fileContent, 0, fileContent.length)) != -1) {
byteoutput.write(fileContent, 0, readBytes);
}
System.out.println("Size of audio buffer: " + fileContent.length);
//byteoutput.write(0);
// byteoutput.write(0);
System.out.println("Size of audio buffer: " + byteoutput.size());
source.open(format, source.getBufferSize()); // line must be open to be recognised by the mixer.
Line[] lines = mixer.getSourceLines();
System.out.println("mixer lines: " + lines.length);
// for(byte bytes: fileContent) {
// System.out.println(bytes);
// }
Thread playback = new Thread () {
public void run () {
// System.out.println((byteoutput.size() +2) % 4);
source.start(); // play (buffer originally empty)
source.write(byteoutput.toByteArray(), 0, byteoutput.size()); // write input bytes to output buffer
} // end run (to do).
}; // end thread action
playback.start(); // start thread
}
catch (LineUnavailableException lue) {
System.out.println(lue.getMessage());
}
catch (FileNotFoundException fnfe) {
System.out.println(fnfe.getMessage());
}
catch(IOException ioe) {
System.out.println(ioe.getMessage());
}
catch(UnsupportedAudioFileException uafe) {
System.out.println(uafe.getMessage());
}
}
}
Whether or not you can load and play a 24-bit file is system dependent, afaik.
I use Audacity for conversions. You should be able import your file into Audacity and export it as 16-bit, stereo, little-endian, 44100 fps, and then load that export with Java's AudioInputStream.
What you hear when playing from Audacity or from Java should be pretty much identical (adjusting for volume). If not, the most likely reason probably pertains to a mistake or overlook in the code, which is very easy to do.
The use of a ByteOutputStream in your code is superfluous. Read from the AudioInputStream into a fixed-size byte array (size being the buffer length, I recommend trying 8 or 16 * 1024 bytes as a first try) and then use the SourceDataLine write method to ship that array.
Following is code that works on my system for loading a playing a "CD Quality" wav called "a3.wav" that I have that is in the same directory as the Java class. You should be able to swap in your own 44100, 16-bit, stereo, little-endian wav file.
I've commented out an attempt to load and play a 24-bit wav file called "spoken8000_24.wav". That attempt gave me an IllegalArgumentException: No line matching interface SourceDataLine supporting format PCM_SIGNED 8000.0 Hz, 24 bit, stereo, 6 bytes/frame, little-endian is supported.
I have to admit, I'm unclear if my system doesn't provide the needed line or if I might have coded the format incorrectly! My OS can certainly play the file. So I'm thinking there is a distinction between what an OS can do and what a "Mixer" on a given system provides to Java.
As a get-around, I just always convert everything to "CD Quality" format, as that seems to be the most widely supported.
public class TriggerSound_SDL extends JFrame
{
public TriggerSound_SDL()
{
JButton button = new JButton("Play Sound");
button.addActionListener(e -> new Thread(() -> playBuzzer()).start());
getContentPane().add(button);
}
private void playBuzzer()
{
try
{
URL url;
url = getClass().getResource("a3.wav");
// url = getClass().getResource("spoken8000_24.wav");
AudioInputStream ais = AudioSystem.getAudioInputStream(url);
System.out.println(ais.getFormat());
AudioFormat audioFmt;
// "CD Quality" 44100 fps, 16-bit, stereo, little endian
audioFmt = new AudioFormat(
AudioFormat.Encoding.PCM_SIGNED,
44100, 16, 2, 4, 44100, false);
// 8000 fps, 32-bit, stereo
// audioFmt = new AudioFormat(
// AudioFormat.Encoding.PCM_SIGNED,
// 8000, 24, 2, 6, 8000, false);
Info info = new DataLine.Info(SourceDataLine.class,
audioFmt);
SourceDataLine sdl = (SourceDataLine)AudioSystem.getLine(info);
int bufferSize = 16 * 1024;
byte[] buffer = new byte[bufferSize];
sdl.open(audioFmt, bufferSize);
sdl.start();
int numBytesRead = 0;
while((numBytesRead = ais.read(buffer)) != -1)
{
sdl.write(buffer, 0, numBytesRead);
}
}
catch (IOException | UnsupportedAudioFileException
| LineUnavailableException ex)
{
ex.printStackTrace();
}
}
private static void createAndShowGUI()
{
JFrame frame = new TriggerSound_SDL();
frame.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> createAndShowGUI());
}
}
This code, with some small tweaks should let you at least test the different formats.
EDIT:
I'm seeing where your goal is to make a DAW!
In that case, you will want to convert the bytes to PCM data. Can I suggest you borrow some code from AudioCue? I basically wrote it to be a Clip-substitute, and part of that involved making the PCM data available for manipulation. Some techniques for mixing, playing back at different frequencies, multithreading can be found in it.
Thanks for all the advice guys. I will be getting rid of the ByteOutputStream and just use the AudioInputStream, I now understand what I was doing was unnecessary!! Thanks for the advice all! I have indeed tried using AudioCue but it is not low level enough for what I want to do!
One more thing guys. Previously, I created a multitrack media player which is using the Clip class. To play all the audio tracks together, I was looping through a list of Clips and playing them. However, this means that all tracks may be playing a tiny amount after each other due to the processing of the loop. Also, Clip class created a new thread per audio. I do not wants 100 threads running on 100 tracks, I want one thread for my audio output. I am still trying to work out how to start all tracks at the same time without a loop....(im guessing AudioCue have nailed the concurrent cues).
Does anyone know the best way to play multiple audio tracks in to one output? Do I need to route/bus all my audio tracks in to one output and somehow write all data from audio files in to one output buffer then play this output in a thread?
Thanks!!
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.
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"));
}
}