Everything works fine until I try to put what I receive on pc into a wav file.
I think its a problem in the saving to wav file part. Tried almost everything, AudioSystem.write etc,nothing seems to work.
If anyone can help I would really appreciate it.This is the code:
class Server {
AudioInputStream audioInputStream;
static AudioInputStream ais;
static AudioFormat format;
static boolean status = true;
static int port = 50005;
static int sampleRate = 48000;
static DataLine.Info dataLineInfo;
static SourceDataLine sourceDataLine;
public static void main(String args[]) throws Exception {
OutputStream outstream = new FileOutputStream(new File("D:/output.wav"));
DatagramSocket serverSocket = new DatagramSocket(port);
int i = 0;
/**
* Formula for lag = (byte_size/sample_rate)*2
* Byte size 9728 will produce ~ 0.45 seconds of lag. Voice slightly broken.
* Byte size 1400 will produce ~ 0.06 seconds of lag. Voice extremely broken.
* Byte size 4000 will produce ~ 0.18 seconds of lag. Voice slightly more broken then 9728.
*/
byte[] receiveData = new byte[4096];
format = new AudioFormat(sampleRate, 16, 1, true, false);
dataLineInfo = new DataLine.Info(SourceDataLine.class, format);
sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
sourceDataLine.open(format);
sourceDataLine.start();
FloatControl volumeControl = (FloatControl) sourceDataLine.getControl(FloatControl.Type.MASTER_GAIN);
volumeControl.setValue(1.00f);
DatagramPacket receivePacket = new DatagramPacket(receiveData,
receiveData.length);
ByteArrayInputStream baiss = new ByteArrayInputStream(
receivePacket.getData());
while (status == true&&i<100) {
i++;
serverSocket.receive(receivePacket);
ais = new AudioInputStream(baiss, format, receivePacket.getLength());
try {
outstream.write(receivePacket.getData(), 0, receivePacket.getData().length);
} catch (Exception e) {
System.out.println("Not working in speakers...");
e.printStackTrace();
}
}
sourceDataLine.stop(); //after i has reacher 100 this happens
sourceDataLine.close();
outstream.close();
}
}
Related
Edit: I am using a .wav file
I'm trying to figure out how to start audio at a certain position (for example: 10 seconds into audio file rather than at the start). Reading the documentation for SourceDataLine had me believe this may be achieved using the offset during:
line.write(byte[] b, int offset, int length)
but every time I've tried any value other than 0 (the default I believe), I get java.lang.IndexOutOfBoundsException, which maybe it hasn't read x byte position yet so cannot write x byte position? I'm unsure and left scratching my head.
I figured this would be a common enough request but can't seem to find anything online related to this, only pausing and resuming audio. I'm probably not searching properly.
In case it matters, here is how I'm currently doing my audio:
AudioInputStream stream = AudioSystem.getAudioInputStream("...file...");
AudioFormat format = stream.getFormat();
SourceDataLine.Info info = new DataLine.Info(SourceDataLine.class, format,((int)stream.getFrameLength()*format.getFrameSize()));
SourceDataLine line = (SourceDataLine)AudioSystem.getLine(info);
int bufferSize = line.getBufferSize();
byte inBuffer[] = new byte[bufferSize];
byte outBuffer[] = new byte[bufferSize];
int numRead, numWritten;
do {
numRead = audioStream.read(inBuffer, 0, bufferSize);
if(numRead <= 0) {
myAudio.flushStream();
} else {
myAudio.writeBytesToStream(inBuffer, numRead);
}
do {
numWritten = myAudio.readBytesFromStream(outBuffer, bufferSize);
if(numWritten > 0) {
line.write(outBuffer, 0, numWritten);
}
} while(numWritten > 0);
} while(numRead > 0);
The problem you are having probably stems from the fact that you are adjusting the offset without adjusting the length. If your array is 10 bytes long and you are starting reading 10 bytes from offset 5 instead of 0, you are reading 5 bytes past its end.
I'd recommend to first skip the appropriate number of bytes using skip(long) on the AudioInputStream and then write to the line.
AudioInputStream stream = AudioSystem.getAudioInputStream("...file...");
AudioFormat format = stream.getFormat();
// find out how many bytes you have to skip, this depends on bytes per frame (a.k.a. frameSize)
int secondsToSkip = 10;
long bytesToSkip = format.getFrameSize() * ((int)format.getFrameRate()) * secondsToSkip;
// now skip until the correct number of bytes have been skipped
int justSkipped = 0;
while (bytesToSkip > 0 && (justSkipped = stream.skip(bytesToSkip)) > 0) {
bytesToSkip -= justSkipped;
}
// then proceed with writing to your line like you have done before
[...]
Note that this only works, if the audio file is uncompressed. If you are dealing with something like .mp3, you first have to convert the stream to PCM (see https://stackoverflow.com/a/41850901/942774)
I've created an example which compiles and works. You can play a .wav file from any time point. It should also work for an mp3 file, but I haven't tested that. Invoke mp3ToWav() for that.
import javax.sound.sampled.*;
import java.io.File;
import java.io.IOException;
public class PlayWavAtTimePoint {
public static void main(String[] args) throws Exception {
String fileName = args[0];
int secondsToSkip = (Integer.parseInt(args[1]));
PlayWavAtTimePoint program = new PlayWavAtTimePoint();
AudioInputStream is = program.getAudioInputStream(fileName);
program.skipFromBeginning(is, secondsToSkip);
program.playSound(is);
}
private static void skipFromBeginning(AudioInputStream audioStream, int secondsToSkip) throws UnsupportedAudioFileException, IOException, LineUnavailableException {
AudioFormat format = audioStream.getFormat();
// find out how many bytes you have to skip, this depends on bytes per frame (a.k.a. frameSize)
long bytesToSkip = format.getFrameSize() * ((int)format.getFrameRate()) * secondsToSkip;
// now skip until the correct number of bytes have been skipped
long justSkipped = 0;
while (bytesToSkip > 0 && (justSkipped = audioStream.skip(bytesToSkip)) > 0) {
bytesToSkip -= justSkipped;
}
}
private static final int BUFFER_SIZE = 128000;
/**
* #param filename the name of the file that is going to be played
*/
public void playSound(String filename) throws IOException, UnsupportedAudioFileException, LineUnavailableException {
AudioInputStream audioStream = getAudioInputStream(filename);
playSound(audioStream);
}
private AudioInputStream getAudioInputStream(String filename) throws UnsupportedAudioFileException, IOException {
return AudioSystem.getAudioInputStream(new File(filename));
}
public void playSound(AudioInputStream audioStream) throws LineUnavailableException, IOException {
AudioFormat audioFormat = audioStream.getFormat();
DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
SourceDataLine audioOutput = (SourceDataLine) AudioSystem.getLine(info);
audioOutput.open(audioFormat);
audioOutput.start();
//This seems to be reading the whole file into a buffer before playing ... not efficient.
//Why not stream it?
int nBytesRead = 0;
byte[] abData = new byte[BUFFER_SIZE];
while (nBytesRead != -1) {
nBytesRead = audioStream.read(abData, 0, abData.length);
if (nBytesRead >= 0) {
audioOutput.write(abData, 0, nBytesRead);
}
}
audioOutput.drain();
audioOutput.close();
}
/**
* Invoke this function to convert to a playable file.
*/
public static void mp3ToWav(File mp3Data) throws UnsupportedAudioFileException, IOException {
// open stream
AudioInputStream mp3Stream = AudioSystem.getAudioInputStream(mp3Data);
AudioFormat sourceFormat = mp3Stream.getFormat();
// create audio format object for the desired stream/audio format
// this is *not* the same as the file format (wav)
AudioFormat convertFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
sourceFormat.getSampleRate(), 16,
sourceFormat.getChannels(),
sourceFormat.getChannels() * 2,
sourceFormat.getSampleRate(),
false);
// create stream that delivers the desired format
AudioInputStream converted = AudioSystem.getAudioInputStream(convertFormat, mp3Stream);
// write stream into a file with file format wav
AudioSystem.write(converted, AudioFileFormat.Type.WAVE, new File("/tmp/out.wav"));
}
}
I'd like to develop a simple java music player to accelerate and play music using this Sonic Algorithm github/Sonic.java. And here's the main class: github/Main.java. The Main.java simply calls Sonic.java and then it can play the music. Even though it works well when running a WAV file, but what I want is to write a new WAV file from the accelerated input stream.
I've tried to write bytes to a ByteArrayOutputStream in the do-while loop of Main.java, and transformed them into a local WAV file, while the generated music gets cut off and obviously there have some lost data during this process.
public class App {
private static void runSonic(
AudioInputStream audioStream,
SourceDataLine line,
float speed,
float pitch,
float rate,
float volume,
boolean emulateChordPitch,
int quality,
int sampleRate,
int numChannels) throws IOException
{
Sonic sonic = new Sonic(sampleRate, numChannels);
int bufferSize = line.getBufferSize();
byte inBuffer[] = new byte[bufferSize];
byte outBuffer[] = new byte[bufferSize];
int numRead,numWritten;
AudioFormat af = audioStream.getFormat();
ByteArrayOutputStream output = new ByteArrayOutputStream();
sonic.setSpeed(speed);
sonic.setPitch(pitch);
sonic.setRate(rate);
sonic.setVolume(volume);
sonic.setChordPitch(emulateChordPitch);
sonic.setQuality(quality);
int count = 0;
do {
numRead = audioStream.read(inBuffer, 0, bufferSize);
if(numRead <= 0) {
sonic.flushStream();
} else {
sonic.writeBytesToStream(inBuffer, numRead);
}
do {
numWritten = sonic.readBytesFromStream(outBuffer, bufferSize);
if(numWritten > 0) {
line.write(outBuffer, 0, numWritten);
output.write(outBuffer);
}
} while(numWritten > 0);
} while(numRead > 0);
byte fileBuffer[] = output.toByteArray();
ByteArrayInputStream bais1 = new ByteArrayInputStream(fileBuffer);
AudioInputStream aisAccelerated1 =
new AudioInputStream(bais1, af, fileBuffer.length);
try {
AudioSystem.write(aisAccelerated1, AudioFileFormat.Type.WAVE, new
File("newFile.wav")
);
}
catch(Exception e) {
e.printStackTrace();
}
}
public static void main(
String[] argv) throws UnsupportedAudioFileException, IOException, LineUnavailableException
{
float speed = 1.5f;
float pitch = 1.5f;
float rate = 1.0f;
float volume = 1.0f;
boolean emulateChordPitch = false;
int quality = 0;
String fileName = "file.wav";
AudioInputStream stream = AudioSystem.getAudioInputStream(new File(fileName));
AudioFormat format = stream.getFormat();
int sampleRate = (int)format.getSampleRate();
int numChannels = format.getChannels();
SourceDataLine.Info info = new DataLine.Info(SourceDataLine.class, format,
((int)stream.getFrameLength()*format.getFrameSize()));
SourceDataLine line = (SourceDataLine)AudioSystem.getLine(info);
line.open(stream.getFormat());
line.start();
runSonic(stream, line, speed, pitch, rate, volume, emulateChordPitch, quality,
sampleRate, numChannels);
line.drain();
line.stop();
}
}
Who can tell me what's going on here? I think all bytes stored in outBuffer has been writted into the output stream in this way.
You can find the whole class using the links above.
output.write(outBuffer);
The problem is here. It should be
output.write(outBuffer, 0, numWritten);
You are writing garbage to the output.
I am a newbie in programming and I want to learn more.
I want to record sound from my microphone in real-time. Below is my code for recording.
while (true) {
int numBytesRead = line.read(data, 0, data.length);
out.write(data, 0, numBytesRead);
}
I tried to enter some codes there and I got to play some chunks of data but after a few seconds, the recording delays for about 3 seconds. Also, when I tried to talk, it plays backs in loop what I tried to say
while (true) {
int numBytesRead = line.read(data, 0, data.length);
out.write(data, 0, numBytesRead);
try {
byte audio[] = out.toByteArray();
InputStream input = new ByteArrayInputStream(audio);
final SourceDataLine line1 = (SourceDataLine) AudioSystem.getLine(info1);
final AudioInputStream ais = new AudioInputStream(input, format, audio.length / format.getFrameSize());
int bufferSize = (int) format.getSampleRate() * format.getFrameSize();
line1.open(format);
line1.start();
byte buffer[] = new byte[bufferSize];
try {
while (true) {
numBytesRead = ais.read(buffer, 0, buffer.length);
if (numBytesRead == -1) break;
line1.write(buffer, 0, numBytesRead);
}
} catch (IOException e) {
System.err.println("I/O problems: " + e);
System.exit(-3);
}
}
Could somebody help me with my project.
thank you for your answer sir. but i tried adding this line to my code and the playback worked
while (numBytesRemaining>0){
numBytesRemaining-=line1.write(data,0,numBytesRemaining);
}
thank you for your help sir
why don't you use Clip for reading the recorded sound.
wavdata = out.toByteArray();
AudioInputStream ais = new AudioInputStream(new ByteArrayInputStream(wavdata), WAVFormat(), wavdata.length / WAVFormat().getFrameSize());
format = ais.getFormat();
info = new DataLine.Info(Clip.class, format);
Clip clip = (Clip) AudioSystem.getLine(info);
clip.open(ais);
//this is for playing
clip.start();
//this is for stopping or pause use it on the pause or stop button.
//clip.stop();
and this is the WAVFormat()
private AudioFormat WAVFormat() {
int channels = 2;
Encoding encoding = AudioFormat.Encoding.PCM_SIGNED;
float sampleRate = rateconstant;
int frameSize = framesize;
float frameRate = rateconstant;
int sampleSizeInBits = 16;
boolean bigEndian = false;
return new AudioFormat(encoding, sampleRate, sampleSizeInBits, channels, frameSize, frameRate, bigEndian);
}
hope it helps
I'm trying audio recording with java. And the quality is not good at all. There is a continuous noise, even though it is completely quite here in the room. Any chance to remove/filter the noise?
I've discovered the same issue on cheap android phone and some recording app. Looks like it is general issue with audio recording
package test;
import javax.sound.sampled.*;
import java.io.*;
public class Tmp {
AudioFileFormat.Type fileType = AudioFileFormat.Type.WAVE;
AudioFormat getAudioFormat() {
float sampleRate = 44100;
int sampleSizeInBits = 16;
int channels = 1;
boolean signed = true;
boolean bigEndian = true;
AudioFormat format = new AudioFormat(sampleRate, sampleSizeInBits,
channels, signed, bigEndian);
return format;
}
void start(String n) {
TargetDataLine line;
File f = new File(n);
try {
AudioFormat format = getAudioFormat();
DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
line = (TargetDataLine) AudioSystem.getLine(info);
line.open(format);
line.start();
AudioInputStream ais = new AudioInputStream(line);
AudioSystem.write(ais, fileType, f);
} catch (LineUnavailableException | IOException ex) {
System.out.println(ex);
}
}
}
I have a client and server application.
The server requests an audio stream and the client handles well.
The client proceeds to setup an AudioFormat(Note: settings are the same on both ends) and TargetDataLine. It Then writes the data to a Socket output stream using a ByteArrayOutput Stream.
The server receives the Data and reads it in a threaded method. During each buffer read it it saves to a AudioInputStream which is passed to a playSound method which is threaded and synchronized to proceed to play the sound.
When i make the playSound method non threaded it works well but slightly glitchy.
Also i know having the play sound non threaded can cause sound frames to jam up
Any help is well appreciated, and any ways I can make this audio stream more efficient and fast is also welcomed.
Client:
private void captureAudio() throws CommandException {
Socket session = new Socket(host_, port_);
try {
final AudioFormat format = getFormat();
DataLine.Info info = new DataLine.Info(
TargetDataLine.class, format);
final TargetDataLine line = (TargetDataLine)
AudioSystem.getLine(info);
line.open(format);
line.start();
int bufferSize = (int)format.getSampleRate() * format.getFrameSize();
byte buffer[] = new byte[bufferSize];
running = true;
try {
while (running) {
int count = line.read(buffer, 0, buffer.length);
if (count > 0) {
BufferedOutputStream out_ = null;
out_ = new BufferedOutputStream(socket_.getOutputStream());
out_.write(buffer, 0, count);
out_.flush();
}
}
out_.close();
line.close();
} catch (IOException e) {
throw new CommandException("I/O problems: " + e,Command.TRANSFER_ERROR);
}
} catch (LineUnavailableException e) {
throw new CommandException("Line unavailable: " + e,Command.ERROR);
}
else {
throw new CommandException("Unable to Connect to Server",Command.CONNECTION_ERROR);
}
}
private AudioFormat getFormat() {
float sampleRate = 16000;
int sampleSizeInBits = 16;
int channels = 2;
boolean signed = true;
boolean bigEndian = true;
return new AudioFormat(sampleRate,sampleSizeInBits, channels, signed, bigEndian);
}
Server:
public void readSocket(final Socket socket) {
new Thread() {
#Override
public void run() {
InputStream input;
try {
input = socket.getInputStream();
final AudioFormat format = getFormat();
int bufferSize = (int)format.getSampleRate() * format.getFrameSize();
byte buffer[] = new byte[bufferSize];
int bytesRead;
while (((bytesRead = input.read(buffer, 0, bufferSize)) != -1 ) ) {
if (bytesRead > 0) {
play(new AudioInputStream(input, format, buffer.length / format.getFrameSize()));
}
}
socket.close();
} catch (Exception ex) {
}
}
}.start();
}
private AudioFormat getFormat() {
float sampleRate = 16000;
int sampleSizeInBits = 16;
int channels = 2;
boolean signed = true;
boolean bigEndian = true;
return new AudioFormat(sampleRate,
sampleSizeInBits, channels, signed, bigEndian);
}
private synchronized void play(final AudioInputStream ais) {
new Thread() {
#Override
public void run() {
try {
final AudioFormat format = getFormat();
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
SourceDataLine line = (SourceDataLine)AudioSystem.getLine(info);
line.open(format);
line.start();
int bufferSize = (int) format.getSampleRate()
* format.getFrameSize();
byte buffer[] = new byte[bufferSize];
int count;
while ((count = ais.read(buffer, 0, buffer.length)) != -1) {
if (count > 0) {
line.write(buffer, 0, count);
}
}
line.drain();
line.close();
ais.close();
} catch (LineUnavailableException ex) {
} catch (IOException ex) {
}
}
}.start();
}