Reputation: 17
I generated sine wave and make a sound using Source data line.
The sampling rate is 44100, and block size 441.
If I play 0.1 second, then generate 4410 short sine wave and make a sound like below.
ByteBuffer cBuf = ByteBuffer.allocate(Constants.BLOCK_SIZE);
for(int i=0; i<SamplesTotal; i++){
if(!cBuf.hasRemaining()){
cBuf.putShort((short) (Short.MAX_VALUE * Math.sin(2 * Math.PI * time)));
time += samplingInterval;
}else{
line.write(cBuf.array(), 0, cBuf.position());
cBuf.clear();
cBuf.putShort((short) (Short.MAX_VALUE * Math.sin(2 * Math.PI * time)));
time += samplingInterval;
}
}
line.write(cBuf.array(), 0, cBuf.position());
The problem now, when I read the data from this sound with smart phone, to find frequency used fftpack read 441 short type data and transform. Such as below source.
while (started) {
int bufferReadResult = audioRecord.read(buffer, 0, blockSize);
for (int i = 0; i < blockSize && i < bufferReadResult; i++) {
toTransform[i] = (double) buffer[i] / (Short.MAX_VALUE);
}
transformer.ft(toTransform);
publishProgress(toTransform);
}
To find specific frequency 19000 and 20000, I filtered like that and I printed the frequency like below.
int temp = 0;
int temp_idx=0;
double freq=0;
int testFreq_19 = 380;
int testFreq_20 = 400;
for (int i = 0; i < toTransform[0].length; i++) {
int x = i;
int downy = (int) (100 - (toTransform[0][i] * 10));
int upy = 100;
if(i>testFreq_19-1 && downy<99){
if(temp<downy){
temp=downy;
temp_idx=i;
}
}
canvas.drawLine(x/2, downy, x/2, upy, paint);
}
if(temp_idx>testFreq_19-10 && temp_idx<testFreq_19+10){
freq = testFreq_19*(frequency/882);
}else if(temp_idx>testFreq_20-10 && temp_idx<testFreq_20+10){
freq = testFreq_20*(frequency/882);
}else{
freq= -1;
}
Log.d("frequency log", "frequency: " + String.valueOf(freq));
The problem is when I generated for 0.1 second, then 10 block(4410) will write. When I read from the smart phone I thought I will receive 4410 block which means print 19000 10 times with above source.
However, the result is terrible. Sometimes receive 20 or 30 times even if 5 times I received. Between the data there is blank(no data) I got the example of results like the following:
frequency: -1
frequency: -1
frequency: 19000.0
frequency: 19000.0
frequency: 19000.0
frequency: -1
frequency: -1
frequency: -1
frequency: 19000.0
frequency: -1
frequency: -1
frequency: -1
frequency: -1
frequency: -1
frequency: 20000
frequency: 20000
frequency: 20000
frequency: 20000
frequency: 20000
frequency: 20000
frequency: 20000
frequency: 20000
frequency: -1
frequency: -1
frequency: 20000
frequency: 20000
frequency: 20000
frequency: 20000
frequency: -1
frequency: 20000
frequency: 20000
frequency: 20000
frequency: 20000
frequency: -1
frequency: -1
frequency: -1
frequency: -1
What's wrong with this code?
Upvotes: 0
Views: 1713
Reputation: 23001
When you're generating the sound wave, you are writing out the data in 16-bit samples. However, when you're reading the data back in again, you are processing the data one byte at a time (but still dividing the value by Short.MAX_VALUE
) which doesn't seem right.
What is the audio data format of the AudioRecord
object - ENCODING_PCM_8BIT
or ENCODING_PCM_16BIT
? If it's 8-bit then you should surely be dividing by 127 (Byte.MAX_VALUE
). If it's 16-bit, then you should assumedly be processing two bytes at a time and converting them to a short.
Update
If the buffer
variable is actually a short array rather than a byte array, then your read code is probably fine as it is and you can ignore my comments above. However, I've just noticed something strange in your write code. In this section:
if(!cBuf.hasRemaining()){
cBuf.putShort((short) (Short.MAX_VALUE * Math.sin(2 * Math.PI * time)));
time += samplingInterval;
}else{
line.write(cBuf.array(), 0, cBuf.position());
cBuf.clear();
cBuf.putShort((short) (Short.MAX_VALUE * Math.sin(2 * Math.PI * time)));
time += samplingInterval;
}
Assumedly the condition in that if
should just be cBuf.hasRemaining()
without the !
operator. If the buffer has space remaining you want to write to it. If not, you want to flush what has already been accumulated in the buffer and then clear it before trying to write.
However, as soon as you fix that, I expect you'll get a BufferOverflowException. As I mentioned in the comments, you're using a ByteBuffer
with a size that is an odd number of bytes, but writing out two bytes at a time. Assumedly the declaration should really be:
ByteBuffer cBuf = ByteBuffer.allocate(Constants.BLOCK_SIZE*2);
I'm not sure that any of this is the cause of your other problems, but it's worth fixing to see if it makes any difference.
Upvotes: 1