I can read WAV file (8-bit per sample) in the following function and copy it to another file. I want to play with the overall volume of the source file with given scale parameter, which is in range [0, 1]. My naive approach was to multiple byte with scale and convert it to byte again. All I got a noisy file. How can I achieve this byte by byte volume adjustment?
public static final int BUFFER_SIZE = 10000;
public static final int WAV_HEADER_SIZE = 44;
public void changeVolume(File source, File destination, float scale) {
RandomAccessFile fileIn = null;
RandomAccessFile fileOut = null;
byte[] header = new byte[WAV_HEADER_SIZE];
byte[] buffer = new byte[BUFFER_SIZE];
try {
fileIn = new RandomAccessFile(source, "r");
fileOut = new RandomAccessFile(destination, "rw");
// copy the header of source to destination file
int numBytes = fileIn.read(header);
fileOut.write(header, 0, numBytes);
// read & write audio samples in blocks of size BUFFER_SIZE
int seekDistance = 0;
int bytesToRead = BUFFER_SIZE;
long totalBytesRead = 0;
while(totalBytesRead < fileIn.length()) {
if (seekDistance + BUFFER_SIZE <= fileIn.length()) {
bytesToRead = BUFFER_SIZE;
} else {
// read remaining bytes
bytesToRead = (int) (fileIn.length() - totalBytesRead);
}
fileIn.seek(seekDistance);
int numBytesRead = fileIn.read(buffer, 0, bytesToRead);
totalBytesRead += numBytesRead;
for (int i = 0; i < numBytesRead - 1; i++) {
// WHAT TO DO HERE?
buffer[i] = (byte) (scale * ((int) buffer[i]));
}
fileOut.write(buffer, 0, numBytesRead);
seekDistance += numBytesRead;
}
fileOut.setLength(fileIn.length());
} catch (FileNotFoundException e) {
System.err.println("File could not be found" + e.getMessage());
} catch (IOException e) {
System.err.println("IOException: " + e.getMessage());
} finally {
try {
fileIn.close();
fileOut.close();
} catch (IOException e) {
System.err.println("IOException: " + e.getMessage());
}
}
}
A java byte is in a range from -128 to 127, while the byte used in the wav pcm format ranges from 0 to 255. That is most likely why you are changing your pcm data to random/noisy values.
buffer[i] = (byte) (scale * (buffer[i]<0?256+(int)buffer[i]:buffer[i]));
Related
I have question about Android decode mp3, mix few audio and encode to m4a (aac). For that I use Jlayer for android to decode mp3, audiotrack to play song, and MediaCodec with mediaformat to encode pcm. The problem is my output after encode is too fast for example: I should have 5 sec audio mix but instead I got ~ 1,5 sec. I thinking that I lose somewhere audio frames, but I dont sure about that. Thanks for answer.
(output file is ~ 25% faster that should be)
Decode mp3 code:
public void decodeMP3toPCM(Resources res, int resource) throws BitstreamException, DecoderException, IOException {
InputStream inputStream = new BufferedInputStream(res.openRawResource(resource), 1152);
Bitstream bitstream = new Bitstream(inputStream);
Decoder decoder = new Decoder();
boolean done = false;
while (!done) {
Header frameHeader = bitstream.readFrame();
if (frameHeader == null) {
done = true;
} else {
SampleBuffer output = (SampleBuffer) decoder.decodeFrame(frameHeader, bitstream);
mTimeCount += frameHeader.ms_per_frame();
short[] pcm = output.getBuffer();
mDataBuffer.addFrame(mViewId, pcm);
mReadedFrames++;
mAudioTrack.write(pcm, 0, pcm.length);
}
bitstream.closeFrame();
}
inputStream.close();
}
encode:
public class AudioEncoder {
private MediaCodec mediaCodec;
private BufferedOutputStream outputStream;
private String mediaType = "audio/mp4a-latm";
public AudioEncoder(String filePath) throws IOException {
File f = new File(filePath);
touch(f);
try {
outputStream = new BufferedOutputStream(new FileOutputStream(f));
} catch (Exception e) {
e.printStackTrace();
}
try {
//mediaCodec = MediaCodec.createEncoderByType(mediaType);
mediaCodec = MediaCodec.createByCodecName("OMX.google.aac.encoder");
} catch (IOException e) {
e.printStackTrace();
}
mediaCodec = MediaCodec.createEncoderByType(mediaType);
final int kSampleRates[] = { 8000, 11025, 22050, 44100, 48000 };
final int kBitRates[] = { 64000, 128000 };
MediaFormat mediaFormat = MediaFormat.createAudioFormat(mediaType,kSampleRates[3],2);
mediaFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
mediaFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 4608);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, kBitRates[1]);
mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mediaCodec.start();
}
public void close() {
try {
mediaCodec.stop();
mediaCodec.release();
outputStream.flush();
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public synchronized void offerEncoder(byte[] input) {
Log.e("synchro ", input.length + " is coming");
try {
ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();
int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
inputBuffer.put(input);
mediaCodec.queueInputBuffer(inputBufferIndex, 0, input.length, 0, 0);
}
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
while (outputBufferIndex >= 0) {
int outBitsSize = bufferInfo.size;
int outPacketSize = outBitsSize + 7; // 7 is ADTS size
ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
outputBuffer.position(bufferInfo.offset);
outputBuffer.limit(bufferInfo.offset + outBitsSize);
byte[] outData = new byte[outPacketSize];
addADTStoPacket(outData, outPacketSize);
outputBuffer.get(outData, 7, outBitsSize);
outputBuffer.position(bufferInfo.offset);
outputStream.write(outData, 0, outData.length);
mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
}
} catch (Throwable t) {
t.printStackTrace();
}
}
private void addADTStoPacket(byte[] packet, int packetLen) {
int profile = 2; //AAC LC
//39=MediaCodecInfo.CodecProfileLevel.AACObjectELD;
int freqIdx = 4; //44.1KHz
int chanCfg = 2; //CPE
// fill in ADTS data
packet[0] = (byte) 0xFF;
packet[1] = (byte) 0xF9;
packet[2] = (byte) (((profile - 1) << 6) + (freqIdx << 2) + (chanCfg >> 2));
packet[3] = (byte) (((chanCfg & 3) << 6) + (packetLen >> 11));
packet[4] = (byte) ((packetLen & 0x7FF) >> 3);
packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F);
packet[6] = (byte) 0xFC;
}
public void touch(File f) {
try {
if (!f.exists())
f.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
I am developing an Android App to send a file via bluetooth to a java server using the BlueCove library version 2.1.0 based on this snippet. At the beginning everything looks fine, but the file will not transfered completly. Only about 7KB of 35KB.
Android
private void sendFileViaBluetooth(byte[] data){
OutputStream outStream = null;
BluetoothDevice device = btAdapter.getRemoteDevice(address);
btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
btSocket.connect();
try {
outStream = btSocket.getOutputStream();
outStream.write( data );
outStream.write("end of file".getBytes());
outStream.flush();
} catch (IOException e) {
} finally{
try {
outStream.close();
btSocket.close();
device = null;
} catch (IOException e) {
}
}
}
PC Server
InputStream inStream = connection.openInputStream();
byte[] buffer = new byte[1024];
File f = new File("d:\\temp.jpg");
FileOutputStream fos = new FileOutputStream (f);
InputStream bis = new BufferedInputStream(inStream);
int bytes = 0;
boolean eof = false;
while (!eof) {
bytes = bis.read(buffer);
if (bytes > 0){
int offset = bytes - 11;
byte[] eofByte = new byte[11];
eofByte = Arrays.copyOfRange(buffer, offset, bytes);
String message = new String(eofByte, 0, 11);
if(message.equals("end of file")) {
eof = true;
} else {
fos.write (buffer, 0, bytes);
}
}
}
fos.close();
connection.close();
I tried already to split the byte array before writing:
public static byte[][] divideArray(byte[] source, int chunksize) {
byte[][] ret = new byte[(int)Math.ceil(source.length / (double)chunksize)][chunksize];
int start = 0;
for(int i = 0; i < ret.length; i++) {
ret[i] = Arrays.copyOfRange(source,start, start + chunksize);
start += chunksize ;
}
return ret;
}
private void sendFileViaBluetooth(byte[] data){
[...]
byte[][] chunks = divideArray(data, 1024);
for (int i = 0; i < (int)Math.ceil(data.length / 1024.0); i += 1) {
outStream.write( chunks[i][1024] );
}
outStream.write("end of file".getBytes());
outStream.flush();
[...]
}
Every help or ideas are appreciated.
You don't need any of this. The canonical way to copy a stream in Java is this:
while ((count = in.read(buffer)) > 0)
{
out.write(buffer, 0, count);
}
out.close();
Same at both ends. TCP/IP will do all the chunking for you. All you need to do is cope correctly with varying size reads, which this code does.
How can I detect silence when recording operation is started in Java? What is PCM data? How can I calculate PCM data in Java?
I found the solution :
package bemukan.voiceRecognition.speechToText;
import javax.sound.sampled.*;
import java.io.*;
public class RecordAudio {
private File audioFile;
protected boolean running;
private ByteArrayOutputStream out;
private AudioInputStream inputStream;
final static float MAX_8_BITS_SIGNED = Byte.MAX_VALUE;
final static float MAX_8_BITS_UNSIGNED = 0xff;
final static float MAX_16_BITS_SIGNED = Short.MAX_VALUE;
final static float MAX_16_BITS_UNSIGNED = 0xffff;
private AudioFormat format;
private float level;
private int frameSize;
public RecordAudio(){
getFormat();
}
private AudioFormat getFormat() {
File file = new File("src/Facebook/1.wav");
AudioInputStream stream;
try {
stream = AudioSystem.getAudioInputStream(file);
format=stream.getFormat();
frameSize=stream.getFormat().getFrameSize();
return stream.getFormat();
} catch (UnsupportedAudioFileException e) {
} catch (IOException e) {
}
return null;
}
public void stopAudio() {
running = false;
}
public void recordAudio() {
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();
Runnable runner = new Runnable() {
int bufferSize = (int) format.getSampleRate()
* format.getFrameSize();
byte buffer[] = new byte[bufferSize];
public void run() {
int readPoint = 0;
out = new ByteArrayOutputStream();
running = true;
int sum=0;
while (running) {
int count =
line.read(buffer, 0, buffer.length);
calculateLevel(buffer,0,0);
System.out.println(level);
if (count > 0) {
out.write(buffer, 0, count);
}
}
line.stop();
}
};
Thread captureThread = new Thread(runner);
captureThread.start();
} catch (LineUnavailableException e) {
System.err.println("Line unavailable: " + e);
System.exit(-2);
}
}
public File getAudioFile() {
byte[] audio = out.toByteArray();
InputStream input = new ByteArrayInputStream(audio);
try {
final AudioFormat format = getFormat();
final AudioInputStream ais =
new AudioInputStream(input, format,
audio.length / format.getFrameSize());
AudioSystem.write(ais, AudioFileFormat.Type.WAVE, new File("temp.wav"));
input.close();
System.out.println("New file created!");
} catch (IOException e) {
System.out.println(e.getMessage());
}
return new File("temp.wav");
}
private void calculateLevel (byte[] buffer,
int readPoint,
int leftOver) {
int max = 0;
boolean use16Bit = (format.getSampleSizeInBits() == 16);
boolean signed = (format.getEncoding() ==
AudioFormat.Encoding.PCM_SIGNED);
boolean bigEndian = (format.isBigEndian());
if (use16Bit) {
for (int i=readPoint; i<buffer.length-leftOver; i+=2) {
int value = 0;
// deal with endianness
int hiByte = (bigEndian ? buffer[i] : buffer[i+1]);
int loByte = (bigEndian ? buffer[i+1] : buffer [i]);
if (signed) {
short shortVal = (short) hiByte;
shortVal = (short) ((shortVal << 8) | (byte) loByte);
value = shortVal;
} else {
value = (hiByte << 8) | loByte;
}
max = Math.max(max, value);
} // for
} else {
// 8 bit - no endianness issues, just sign
for (int i=readPoint; i<buffer.length-leftOver; i++) {
int value = 0;
if (signed) {
value = buffer [i];
} else {
short shortVal = 0;
shortVal = (short) (shortVal | buffer [i]);
value = shortVal;
}
max = Math.max (max, value);
} // for
} // 8 bit
// express max as float of 0.0 to 1.0 of max value
// of 8 or 16 bits (signed or unsigned)
if (signed) {
if (use16Bit) { level = (float) max / MAX_16_BITS_SIGNED; }
else { level = (float) max / MAX_8_BITS_SIGNED; }
} else {
if (use16Bit) { level = (float) max / MAX_16_BITS_UNSIGNED; }
else { level = (float) max / MAX_8_BITS_UNSIGNED; }
}
} // calculateLevel
}
How can I detect silence when recording operation is started in Java?
Calculate the dB or RMS value for a group of sound frames and decide at what level it is considered to be 'silence'.
What is PCM data?
Data that is in Pulse-code modulation format.
How can I calculate PCM data in Java?
I do not understand that question. But guessing it has something to do with the speech-recognition tag, I have some bad news. This might theoretically be done using the Java Speech API. But there are apparently no 'speech to text' implementations available for the API (only 'text to speech').
I have to calculate rms for speech-recognition project. But I do not know how can I calculate in Java.
For a single channel that is represented by signal sizes in a double ranging from -1 to 1, you might use this method.
/** Computes the RMS volume of a group of signal sizes ranging from -1 to 1. */
public double volumeRMS(double[] raw) {
double sum = 0d;
if (raw.length==0) {
return sum;
} else {
for (int ii=0; ii<raw.length; ii++) {
sum += raw[ii];
}
}
double average = sum/raw.length;
double sumMeanSquare = 0d;
for (int ii=0; ii<raw.length; ii++) {
sumMeanSquare += Math.pow(raw[ii]-average,2d);
}
double averageMeanSquare = sumMeanSquare/raw.length;
double rootMeanSquare = Math.sqrt(averageMeanSquare);
return rootMeanSquare;
}
There is a byte buffer to save input values from the line, and what I should have to do with this buffer?
If using the volumeRMS(double[]) method, convert the byte values to an array of double values ranging from -1 to 1. ;)
You need to catch the value like a number silence is zero or near
Please adapt your code to your requirement!!!
In this case a variable named UMBRAL (Threshold in spanish)...
Suppose that you have access to WAV file like bytes ByteHeader...
private Integer Byte2PosIntBig(byte Byte24, byte Byte16, byte Byte08, byte Byte00) {
return new Integer (
((Byte24) << 24)|
((Byte16 & 0xFF) << 16)|
((Byte08 & 0xFF) << 8)|
((Byte00 & 0xFF) << 0));
}
Before ....
RandomAccessFile RAFSource = new RandomAccessFile("your old file wav", "r");
Begins here...
int PSData = 44;
byte[] Bytes = new byte[4];
byte[] ByteHeader = new byte[44];
RAFSource.seek(0);
RAFSource.read(ByteHeader);
int WavSize = Byte2PosIntBig(ByteHeader[43],ByteHeader[42],ByteHeader[41],ByteHeader[40]);
int NumBits = Byte2PosIntBig(ByteHeader[35],ByteHeader[34]);
int NumByte = NumBits/8;
for (int i = PSData;i < PSData+WavSize;i+=NumByte) {
int WavSample = 0;
int WavResultI =0;
int WavResultO = 0;
if (NumByte == 2) {
RAFSource.seek(i);
Bytes[0] = RAFSource.readByte();
Bytes[1] = RAFSource.readByte();
WavSample = (int)(((Bytes[1]) << 8)|((Bytes[0] & 0xFF) << 0));
if (Math.abs(WavSample) < UMBRAL) {
//SILENCE DETECTED!!!
}
} else {
RAFSource.seek(i);
WavSample = (short)(RAFSource.readByte() & 0xFF);
short sSamT = (short)WavSample;
sSamT += 128;
double dSamD = (double)sSamT*Multiplier;
if ((double)sSamT < UMBRAL) {
//SILENCE DETECTED!!!
}
}
I have to encrypt some file (jpg) using vigenere cipher. I wrote some code, but after encryption and decryption my file is corrupted. The first 1/4 of image displays okay, but the rest of it is corrupted. Here is my code:
#Override
public byte[] encryptFile(byte[] file, String key) {
char[] keyChars = key.toCharArray();
byte[] bytes = file;
for (int i = 0; i < file.length; i++) {
int keyNR = keyChars[i % keyChars.length] - 32;
int c = bytes[i] & 255;
if ((c >= 32) && (c <= 127)) {
int x = c - 32;
x = (x + keyNR) % 96;
bytes[i] = (byte) (x + 32);
}
}
return bytes;
}
#Override
public byte[] decryptFile(byte[] file, String key) {
char[] keyChars = key.toCharArray();
byte[] bytes = file;
for (int i = 0; i < file.length; i++) {
int keyNR = keyChars[i % keyChars.length] - 32;
int c = bytes[i] & 255;
if ((c >= 32) && (c <= 127)) {
int x = c - 32;
x = (x - keyNR + 96) % 96;
bytes[i] = (byte) (x + 32);
}
}
return bytes;
}
What did I do wrong?
EDIT:
reading and writing to file:
public void sendFile(String selectedFile, ICipher cipher, String key) {
try {
DataOutputStream outStream = new DataOutputStream(client
.getOutputStream());
outStream.flush();
File file = new File(selectedFile);
FileInputStream fileStream = new FileInputStream(file);
long fileSize = file.length();
long completed = 0;
long bytesLeft = fileSize - completed;
String msg = "SENDING_FILE:" + file.getName() + ":" + fileSize;
outStream.writeUTF(cipher.encryptMsg(msg, key));
while (completed < fileSize) {
int step = (int) (bytesLeft > 150000 ? 150000 : bytesLeft);
byte[] buffer = new byte[step];
fileStream.read(buffer);
buffer = cipher.encryptFile(buffer, key);
outStream.write(buffer);
completed += step;
bytesLeft = fileSize - completed;
}
outStream.writeUTF(cipher.encryptMsg("SEND_COMPLETE", key));
fileStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private void downloadFile(String fileName, int fileSize,DataInputStream input,ICipher cipher, String key) {
try {
FileOutputStream outStream = new FileOutputStream("C:\\" + fileName);
int bytesRead = 0, counter = 0;
while (counter < fileSize) {
int step = (int) (fileSize > 150000 ? 150000 : fileSize);
byte[] buffer = new byte[step];
bytesRead = input.read(buffer);
if (bytesRead >= 0) {
buffer = cipher.decryptFile(buffer, key);
outStream.write(buffer, 0, bytesRead);
counter += bytesRead;
}
if (bytesRead < 1024) {
outStream.flush();
break;
}
}
Display.getDefault().syncExec(new Runnable() {
#Override
public void run() {
window.handleMessage("Download sucessfully");
}
});
outStream.close();
} catch (Exception e) {
Display.getDefault().syncExec(new Runnable() {
#Override
public void run() {
window.handleMessage("Error on downloading file!");
}
});
}
}
You encode the file in whatever chunks come from the disk I/O:
int step = (int) (bytesLeft > 150000 ? 150000 : bytesLeft);
byte[] buffer = new byte[step];
fileStream.read(buffer);
buffer = cipher.encryptFile(buffer, key);
But you decode the file in whatever chunks come from the network I/O:
bytesRead = input.read(buffer);
if (bytesRead >= 0) {
buffer = cipher.decryptFile(buffer, key);
outStream.write(buffer, 0, bytesRead);
counter += bytesRead;
}
These chunks are likely to disagree. The disk I/O may always give you full chunks (lucky for you), but the network I/O will likely give you packet-sized chunks (1500 bytes minus header).
The cipher should get an offset into the already encoded/decoded data (or encode/decode everything at once), and use that to shift the key appropriately, or this may happen:
original: ...LOREM IPSUM...
key : ...abCde abCde...
encoded : ...MQUIR JRVYR...
key : ...abCde Cdeab... <<note the key got shifted
decoded : ...LOREM GNQXP... <<output wrong after the first chunk.
Since the packet data size is (for Ethernet-sized TCP/IP packets) aligned at four bytes, a key of length four is likely to be always aligned.
another issue is that you are ignoring the number of bytes read from disk when uploading the file. While disk I/O is likely to always give you full-sized chunks (the file's likely to be memory-mapped or the underlying native API does provide this guarantee), nothing's taken for granted. Always use the amount of bytes actually read: bytesRead = fileStream.read(buffer);
I currently use the following to read a bluetooth inputstream and save it as a file. It works well with small files but then with larger files its creating a large byte array first. Whats the most efficient way of doing this AND making sure that it reads the only the length specified, no more, no less?
public void getAndWrite(InputStream is, long length, String filename)
throws IOException {
// Create the byte array to hold the data
byte[] bytes = new byte[(int) length];
// Read in the bytes
int offset = 0;
int numRead = 0;
while (offset < bytes.length
&& (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
offset += numRead;
}
// Ensure all the bytes have been read in
if (offset < bytes.length) {
throw new IOException("Could not completely read stream ");
}
// Write the byte array to file
FileOutputStream fos = null;
try {
fos = mContext.openFileOutput(filename, Context.MODE_PRIVATE);
} catch (FileNotFoundException e) {
Log.e(TAG, "Problem finding internal storage", e);
}
try {
fos.write(bytes);
fos.close();
} catch (IOException e) {
Log.e(TAG, "Problem writing file", e);
}
}
int bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
int remaining = length;
int read = 0;
while (remaining > 0
&& (read = in.read(buffer, 0, Math.min(remaining, bufferSize))) >= 0) {
out.write(buffer, 0, read);
remaining -= read;
}
Note that the above makes sure you don't write more that length bytes. But it doesn't make sure you write exactly length bytes. I don't see how you could do this without reading length bytes in memory, or reading length bytes and writing to a temp file, then writing the temp file to the final destination.