I want to play a WAV file over GSM modem. Here is my sample code
private final int BUFFER_SIZE = 8;
private File soundFile;
private AudioInputStream audioStream;
private AudioFormat audioFormat;
public void playSound(String filename) throws IOException{
String strFilename = filename;
try {
soundFile = new File(strFilename);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
try {
audioStream = AudioSystem.getAudioInputStream(soundFile);
} catch (Exception e){
e.printStackTrace();
System.exit(1);
}
audioFormat = audioStream.getFormat();
DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
int nBytesRead = 0;
byte[] abData = new byte[BUFFER_SIZE];
while (nBytesRead != -1) {
try {
nBytesRead = audioStream.read(abData);
} catch (IOException e) {
e.printStackTrace();
}
if (nBytesRead >= 0) {
outputStream.write(abData, 0, nBytesRead);
outputStream.flush();
}
}
}
But the problem is the WAV file sending through serial port is playing very fast. I don't know what's the problem . Here is my WAV file description:
ULAW 8000.0 Hz, 8 bit, mono, 1 bytes/frame, Audio Sample Rate 8Khz.
Can anyone help me to solve the issue?
I would check the following - especially #3.
Synchronization
AudioFormat
JSSRC Resampling Library
How about sleeping a while after "outputStream.flush();"
might be Thread.sleep(50)
I think your problem is at the receiving end and doing playback. Set audio sample rate there to match your audio data. Also make sure serial port flow control is enabled (or look into it if you get correct playback speed, but parts of audio are lost). You know the sample rate of the file, so set the receiving end to have same sample rate (and other parameters).
If receiving end is out of reach or can't be changed, you need to change the sample rate at the transmitting end to match what receiver expects. Easiest is to use some audio editor (such as SoX, which is command line tool) to change the audio file. You should try this first, just to check that you can get good playback with right audio format.
More flexible way is to do it in your program, so you can feed it any audio file, and then it will convert it to correct sample rate and play it correctly. But this is of course more complex, too. Look for a library, such as the one recommended in that other answer by Elliott Frisch.
Related
I want to ask the repetitive question of how to record the audio send to the speakers. But I want some insights to the previously answered.
I went to this page: Capturing speaker output in Java
I saw this code posted by a developer:
import javax.sound.sampled.*;
import java.io.*;
public class JavaSoundRecorder {
// record duration, in milliseconds
static final long RECORD_TIME = 10000; // 1 minute
// path of the wav file
File wavFile = new File("E:/RecordAudio.wav");
// format of audio file
AudioFileFormat.Type fileType = AudioFileFormat.Type.WAVE;
// the line from which audio data is captured
TargetDataLine line;
/**
* Defines an audio format
*/
AudioFormat getAudioFormat() {
float sampleRate = 16000;
int sampleSizeInBits = 8;
int channels = 1;
boolean signed = true;
boolean bigEndian = true;
AudioFormat format = new AudioFormat(sampleRate, sampleSizeInBits,
channels, signed, bigEndian);
return format;
}
/**
* Captures the sound and record into a WAV file
*/
void start() {
try {
AudioFormat format = getAudioFormat();
DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
// checks if system supports the data line
if (!AudioSystem.isLineSupported(info)) {
System.out.println("Line not supported");
System.exit(0);
}
line = (TargetDataLine) AudioSystem.getLine(info);
line.open(format);
line.start(); // start capturing
System.out.println("Start capturing...");
AudioInputStream ais = new AudioInputStream(line);
System.out.println("Start recording...");
// start recording
AudioSystem.write(ais, fileType, wavFile);
} catch (LineUnavailableException ex) {
ex.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
/**
* Closes the target data line to finish capturing and recording
*/
void finish() {
line.stop();
line.close();
System.out.println("Finished");
}
/**
* Entry to run the program
*/
public static void main(String[] args) {
final JavaSoundRecorder recorder = new JavaSoundRecorder();
// creates a new thread that waits for a specified
// of time before stopping
Thread stopper = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(RECORD_TIME);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
recorder.finish();
}
});
stopper.start();
// start recording
recorder.start();
}
}
Now I have some questions I want to ask.
This code runs OK on my windows OS but it doesn't work on my Ubuntu on the same machine(dual boot). In Ubuntu it records silence and I tried to get all mixers but can't get it working
I want to get the output going to the speakers and I am getting the output of the speakers. The sound of the vicinity with a very little sound of what I actually want.
Please answer my queries of the above 2 questions.
What I want? I want the clear audio that is currently being played and fetched to the speakers of my laptop. I don't want the audio that is already emitted and then re-recorded because that is bad. Also I need a reason as of why my Ubuntu is not supporting this code.(This is vague info but I am using BlueJ in windows to run this and NetBeans on Ubuntu(without sudo)).
I saw some YouTube videos to understand the theory:
https://www.youtube.com/watch?v=GVtl19L9GxU
https://www.youtube.com/watch?v=PTs01qr9RlY
I read 1 and a half page documentation of oracle here: https://docs.oracle.com/javase/tutorial/sound/accessing.html
There was this thing mentioned in the docs:
An applet running with the applet security manager can play, but not record, audio.
An application running with no security manager can both play and record audio.
An application running with the default security manager can play, but not record, audio.
But I don't think I turned any security manager.
In the end I found no success in what I want to do. Instead of going further in the documentation I thought to ask the question here.
I want to play the microphone input in realtime using the JavaFX media player (to analyse its frequencies). The problem is, that the MediaPlayer only accepts Strings as source. I know how to write the microphone input into a byte array and into a file.
Using the byte array as source for the MediaPlayer is (for me) not possible. I tried using a temporary file, but that causes the following error:
Exception in thread "JavaFX Application Thread" MediaException: MEDIA_UNSUPPORTED : Empty signature!
I think this is, because I'm using a file as input while I'm still writing new data into it. My full code until now:
public class Music {
static AudioFormat format;
static DataLine.Info info;
public static void input(int i, int j, int pinState) {
format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 44100, 16, 2, 4, 44100, false);
try {
info = new DataLine.Info(TargetDataLine.class, format);
final TargetDataLine targetLine = (TargetDataLine) AudioSystem.getLine(info);
targetLine.open();
AudioInputStream audioStream = new AudioInputStream(targetLine);
File temp = File.createTempFile("Input", ".wav");
temp.deleteOnExit();
Thread targetThread = new Thread() {
public void run() {
targetLine.start();
try {
AudioSystem.write(audioStream, AudioFileFormat.Type.WAVE, temp);
} catch (IOException e) {
e.printStackTrace();
}
}
};
targetThread.start();
Media media = new Media(temp.toURI().toURL().toString());
MediaPlayer player = new MediaPlayer(media);
player.setAudioSpectrumThreshold(-100);
player.setMute(false);
player.setAudioSpectrumListener(new AudioSpectrumListener() {
#Override
public void spectrumDataUpdate(double timestamp, double duration, float[] magnitudes, float[] phases) {
if(Var.nodeController[i] == 3) { //testing if the targetLine should keep on capturing sound
} else {
targetLine.stop();
targetLine.close();
player.stop();
}
}
});
player.play();
} catch (LineUnavailableException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
I need to find a solution to use the microphone input as MediaPlayer input, either using a file or a byte array or any other possible solution.
I am going to speculate that the following library might be helpful.
https://github.com/SzymonKatra/AudioAnalyzer
Note that it makes use of FFmpeg which claims to be:
A complete, cross-platform solution to record, convert and stream
audio and video.
The key component (based on my cursory look-over) seems to be the class FFTAnalyzer.java which the documentation says is based upon an FFT code/algorithm from Princeton.
So, the basic plan (I believe) would be the following:
obtain microphone input (a stream of bytes) from targetdataline
convert N frames of bytes to normalized floats or doubles and package as an array to send to FFTAnalyzer.analyze(double[] samples)
request the analysis results via FFTAnalyzer.getAmplitudes()
examine the result for each equalization band and apply smoothing as appropriate
repeat
I'm unclear as to exactly how much of the library is needed. It could be that since you are not dealing with video or cross-platform issues, only a class or two from this library would be needed.
I have a game with game logic happening in the main. I just added sound playing as per documentation I found:
//////////////////////SOUND/////////////////////////
SourceDataLine soundLine = null;
int BUFFER_SIZE = 64*1024; // 64 KB
// Set up an audio input stream piped from the sound file.
try {
File soundFile = new File("tim ph3 samplepart1.wav");
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(soundFile);
AudioFormat audioFormat = audioInputStream.getFormat();
DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
soundLine = (SourceDataLine) AudioSystem.getLine(info);
soundLine.open(audioFormat);
soundLine.start();
int nBytesRead = 0;
byte[] sampledData = new byte[BUFFER_SIZE];
while (nBytesRead != -1) {
nBytesRead = audioInputStream.read(sampledData, 0, sampledData.length);
if (nBytesRead >= 0) {
// Writes audio data to the mixer via this source data line.
soundLine.write(sampledData, 0, nBytesRead);
}
}
} catch (UnsupportedAudioFileException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
} catch (LineUnavailableException ex) {
ex.printStackTrace();
} finally {
soundLine.drain();
soundLine.close();
}
/////////////////////////////////////////////////////
It plays the file I specified out of the files in my project folder in Eclipse.
The problem? It blocks all game logic after it that appears in the main.
This makes sense - the program is sequential and until the ENTIRE song is done...I figure the game can't go on.
This obviously isn't going to work, and it appears I'm going to have to go to dreaded multithreading...BUT BEFORE I DO...I wonder...is there a Java library or some other clever solution to avoid multithreading in this case?
Yes, you need to use a separate thread. There's nothing to be afraid of. Multithreading in Java is a piece of cake. Look at the Concurrency packages.
http://docs.oracle.com/javase/tutorial/essential/concurrency/
Beware: knowing how to start a thread and knowing how to safely multithread your programs are 2 different things. For now, make sure you avoid touching the same music from multiple threads.
I have set mp3plugin.jar in the run time libraries needed in my netbeans project .But still i get the above exception when i try to play mp3 file.What is the reason for this :
import java.io.File;
import java.io.IOException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
public class tester_1 {
private static final int EXTERNAL_BUFFER_SIZE = 128000;
public static void main(String[] args)
{
/*
We check that there is exactely one command-line
argument.
If not, we display the usage message and exit.
*/
/*
Now, that we're shure there is an argument, we
take it as the filename of the soundfile
we want to play.
*/
String strFilename = "mp3tester.mp3";
File soundFile = new File("mp3tester.mp3");
/*
We have to read in the sound file.
*/
AudioInputStream audioInputStream = null;
try
{
audioInputStream = AudioSystem.getAudioInputStream(soundFile);
}
catch (Exception e)
{
/*
In case of an exception, we dump the exception
including the stack trace to the console output.
Then, we exit the program.
*/
e.printStackTrace();
System.exit(1);
}
/*
From the AudioInputStream, i.e. from the sound file,
we fetch information about the format of the
audio data.
These information include the sampling frequency,
the number of
channels and the size of the samples.
These information
are needed to ask Java Sound for a suitable output line
for this audio file.
*/
AudioFormat audioFormat = audioInputStream.getFormat();
/*
Asking for a line is a rather tricky thing.
We have to construct an Info object that specifies
the desired properties for the line.
First, we have to say which kind of line we want. The
possibilities are: SourceDataLine (for playback), Clip
(for repeated playback) and TargetDataLine (for
recording).
Here, we want to do normal playback, so we ask for
a SourceDataLine.
Then, we have to pass an AudioFormat object, so that
the Line knows which format the data passed to it
will have.
Furthermore, we can give Java Sound a hint about how
big the internal buffer for the line should be. This
isn't used here, signaling that we
don't care about the exact size. Java Sound will use
some default value for the buffer size.
*/
SourceDataLine line = null;
DataLine.Info info = new DataLine.Info(SourceDataLine.class,
audioFormat);
try
{
line = (SourceDataLine) AudioSystem.getLine(info);
/*
The line is there, but it is not yet ready to
receive audio data. We have to open the line.
*/
line.open(audioFormat);
}
catch (LineUnavailableException e)
{
e.printStackTrace();
System.exit(1);
}
catch (Exception e)
{
e.printStackTrace();
System.exit(1);
}
/*
Still not enough. The line now can receive data,
but will not pass them on to the audio output device
(which means to your sound card). This has to be
activated.
*/
line.start();
/*
Ok, finally the line is prepared. Now comes the real
job: we have to write data to the line. We do this
in a loop. First, we read data from the
AudioInputStream to a buffer. Then, we write from
this buffer to the Line. This is done until the end
of the file is reached, which is detected by a
return value of -1 from the read method of the
AudioInputStream.
*/
int nBytesRead = 0;
byte[] abData = new byte[EXTERNAL_BUFFER_SIZE];
while (nBytesRead != -1)
{
try
{
nBytesRead = audioInputStream.read(abData, 0, abData.length);
}
catch (IOException e)
{
e.printStackTrace();
}
if (nBytesRead >= 0)
{
int nBytesWritten = line.write(abData, 0, nBytesRead);
}
}
/*
Wait until all data are played.
This is only necessary because of the bug noted below.
(If we do not wait, we would interrupt the playback by
prematurely closing the line and exiting the VM.)
Thanks to Margie Fitch for bringing me on the right
path to this solution.
*/
line.drain();
/*
All data are played. We can close the shop.
*/
line.close();
/*
There is a bug in the jdk1.3/1.4.
It prevents correct termination of the VM.
So we have to exit ourselves.
*/
System.exit(0);
}
private static void printUsageAndExit()
{
out("tester_1: usage:");
out("\tjava tester_1 <soundfile>");
System.exit(1);
}
private static void out(String strMessage)
{
System.out.println(strMessage);
}
}
when i run this program i get the following exception :
java.lang.IllegalArgumentException: No line matching interface SourceDataLine supporting format MPEG1L3 44100.0 Hz, unknown bits per sample, stereo, unknown frame size, unknown frame rate, is supported.
at javax.sound.sampled.AudioSystem.getLine(AudioSystem.java:476)
at mp3tester_mp3plugin.tester_1.main(tester_1.java:178)
Java Result: 1
What is the reason i am getting this error ?
import java.io.File;
import javax.media.Format;
import javax.media.Manager;
import javax.media.MediaLocator;
import javax.media.Player;
import javax.media.PlugInManager;
import javax.media.format.AudioFormat;
public class AudioTest {
public static void main(String[] args) {
Format input1 = new AudioFormat(AudioFormat.MPEGLAYER3);
Format input2 = new AudioFormat(AudioFormat.MPEG);
Format output = new AudioFormat(AudioFormat.LINEAR);
PlugInManager.addPlugIn(
"com.sun.media.codec.audio.mp3.JavaDecoder",
new Format[]{input1, input2},
new Format[]{output},
PlugInManager.CODEC
);
try{
Player player = Manager.createPlayer(new MediaLocator(new File("mp3tester.mp3").toURI().toURL()));
player.start();
}
catch(Exception ex){
ex.printStackTrace();
}
}
}
Courtsey of morgenstille.at.
Cheers,
Wim
PS Oracle 'hid' the JMF library here.
Edit: To actually answer your question: javax.sound has issues with playing 'some' mp3 formats, while javax.media and the mp3 plugin will have less (no?) issues.
It might be worthwile to check API's like JavaZoom or others for your mp3 needs.
I was getting the same error on Linux system. The workaround for me was to run it as root. Probably, I need to sort out some device permissions.
Using Java is it possible to capture the speaker output? This output is not being generated by my program but rather by other running applications. Can this be done with Java or will I need to resort to C/C++?
I had a Java based app. that used Java Sound to tap into the sound flowing through the system to make a trace of it. It worked well on my own (Windows based) machine, but failed completely on some others.
It was determined that in order to get it working on those machines, would take nothing short of an audio loop-back in either software or hardware (e.g. connect a lead from the speaker 'out' jack to the microphone 'in' jack).
Since all I really wanted to do was plot the trace for music, and I figured how to play the target format (MP3) in Java, it became unnecessary to pursue the other option further.
(And I also heard that Java Sound on Mac. was horribly broken, but I never looked closely into it.)
Java is not the best tool when dealing with the OS. If you need/want to use it for this task, probably you will end using Java Native Interface (JNI), linking to libraries compiled in other languages (probably c/c++).
Take an AUX cable, connect to HEADPHONE JACK and other end to MICROPHONE JACK and run this code
https://www.codejava.net/coding/capture-and-record-sound-into-wav-file-with-java-sound-api
import javax.sound.sampled.*;
import java.io.*;
public class JavaSoundRecorder {
// record duration, in milliseconds
static final long RECORD_TIME = 60000; // 1 minute
// path of the wav file
File wavFile = new File("E:/Test/RecordAudio.wav");
// format of audio file
AudioFileFormat.Type fileType = AudioFileFormat.Type.WAVE;
// the line from which audio data is captured
TargetDataLine line;
/**
* Defines an audio format
*/
AudioFormat getAudioFormat() {
float sampleRate = 16000;
int sampleSizeInBits = 8;
int channels = 2;
boolean signed = true;
boolean bigEndian = true;
AudioFormat format = new AudioFormat(sampleRate, sampleSizeInBits,
channels, signed, bigEndian);
return format;
}
/**
* Captures the sound and record into a WAV file
*/
void start() {
try {
AudioFormat format = getAudioFormat();
DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
// checks if system supports the data line
if (!AudioSystem.isLineSupported(info)) {
System.out.println("Line not supported");
System.exit(0);
}
line = (TargetDataLine) AudioSystem.getLine(info);
line.open(format);
line.start(); // start capturing
System.out.println("Start capturing...");
AudioInputStream ais = new AudioInputStream(line);
System.out.println("Start recording...");
// start recording
AudioSystem.write(ais, fileType, wavFile);
} catch (LineUnavailableException ex) {
ex.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
/**
* Closes the target data line to finish capturing and recording
*/
void finish() {
line.stop();
line.close();
System.out.println("Finished");
}
/**
* Entry to run the program
*/
public static void main(String[] args) {
final JavaSoundRecorder recorder = new JavaSoundRecorder();
// creates a new thread that waits for a specified
// of time before stopping
Thread stopper = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(RECORD_TIME);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
recorder.finish();
}
});
stopper.start();
// start recording
recorder.start();
}
}