Reputation: 2436
I am using Sonic Algorithm developed by waywardgeek to speed and slow down my audio files. It's working fine. But what I need is I need to save that playing audio to wav
file. I have tried to save using ByteArrayInputStream
and TargetDataLink
. But I am failed to do that. So anybody can help me to save that playing audio to wav
file? And also I am new to Java Audio.
Here is the code,
Main.java
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;
import javax.sound.sampled.UnsupportedAudioFileException;
public class Main {
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;
sonic.setSpeed(speed);
sonic.setPitch(pitch);
sonic.setRate(rate);
sonic.setVolume(volume);
sonic.setChordPitch(emulateChordPitch);
sonic.setQuality(quality);
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);
}
} while (numWritten > 0);
} while (numRead > 0);
}
public static void main(String[] argv) throws UnsupportedAudioFileException, IOException, LineUnavailableException {
float speed = 1.0f;
float pitch = 1.0f;
float rate = 1.5f;
float volume = 1.0f;
boolean emulateChordPitch = false;
int quality = 0;
AudioInputStream stream = AudioSystem.getAudioInputStream(new File("C:\\audio1.wav"));
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();
}
}
I have tried with the following code and it saved to wav
file but when playing it has one-word audio. And also with the TargetDataLink
, nothing works.
byteArrayOutputStream = new ByteArrayInputStream(outBuffer);
audioStreams = new AudioInputStream(byteArrayOutputStream, line.getFormat(), bufferSize);
AudioSystem.write(audioStreams, AudioFileFormat.Type.WAVE, new File("C:\\audio2.wav"));
Sonic.java
Can't post the full code here because of the limited characters allowed. Code is here.
PS: The code is developed by Bill Cox. Thank you Bill Cox for this great code.
Upvotes: 2
Views: 3890
Reputation: 7910
I'm pretty sure that both questions, how to change the playback rate, and how to save wavs have been covered in past questions. For example Java - Adjust playback speed of a WAV file See the answer on linear interpolation. This is not that hard to implement. The chosen answer is fine if you only wish to double the speed, but the interpolation algorithm allows for many other speeds to be designated.
As for saving to wav, I recommend starting with the Oracle Sound tutorial. https://docs.oracle.com/javase/tutorial/sound/converters.html
As far as doing this as a stream rather than holding the complete files in memory, it takes a bit of management, but this is a pretty common task and should be in any programmer's bag of tricks. While inputting and processing data, you save it to an output buffer array, and track the progress on the output buffer. Only write to the output stream when this buffer is ready.
Overview of steps: read bytes, convert to PCM, do the pitch shift, if a full buffer is ready for output: convert PCM back to bytes, write bytes.
Upvotes: 1
Reputation: 2436
I have tried and now I came up with a solution using ByteArrayInputStream
and ByteArrayOutputStream
. First I write outBuffer
using ByteArrayOutputStream
as same as SourceDataLine
line.write()
. And then convert it to byteArray
and saved it using AudioInputStream
. This tutorial from java2s
helped me to do this. It's working but I don't know if this is the best solution. So any suggestions are welcome. Thank you.
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;
import javax.sound.sampled.UnsupportedAudioFileException;
public class Main {
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);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
int bufferSize = line.getBufferSize();
byte inBuffer[] = new byte[bufferSize];
byte outBuffer[] = new byte[bufferSize];
int numRead, numWritten;
sonic.setSpeed(speed);
sonic.setPitch(pitch);
sonic.setRate(rate);
sonic.setVolume(volume);
sonic.setChordPitch(emulateChordPitch);
sonic.setQuality(quality);
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);
byteArrayOutputStream.write(outBuffer, 0, numWritten);
}
} while (numWritten > 0);
} while (numRead > 0);
byte audioBytes[] = byteArrayOutputStream.toByteArray();
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(audioBytes);
AudioFormat format = line.getFormat();
AudioInputStream audioInputStream = new AudioInputStream(byteArrayInputStream, format, audioBytes.length / format.getFrameSize());
AudioSystem.write(audioInputStream, AudioFileFormat.Type.WAVE, new File("C:\\audio2.wav"));
}
public static void main(String[] argv) throws UnsupportedAudioFileException, IOException, LineUnavailableException {
float speed = 1.0f;
float pitch = 1.0f;
float rate = 1.5f;
float volume = 1.0f;
boolean emulateChordPitch = false;
int quality = 0;
AudioInputStream stream = AudioSystem.getAudioInputStream(new File("C:\\audio1.wav"));
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();
}
}
Upvotes: 1