problem of read method in AudioRecord class
Asked Answered
B

2

7

public int read (byte[] audioData, int offsetInBytes, int sizeInBytes).

This method reads audio data from the audio hardware for recording into a buffer.

Its Parameters are : audioData the array to which the recorded audio data is written. offsetInBytes index in audioData from which the data is written expressed in bytes. sizeInBytes the number of requested bytes.

It Returns the number of bytes that were read or or ERROR_INVALID_OPERATION if the object wasn't properly initialized, or ERROR_BAD_VALUE if the parameters don't resolve to valid data and indexes. The number of bytes will not exceed sizeInBytes.

I have written this method in my code like this : int num; byte[] buf = new byte[160]; num = record.read(buf, 0, 160);

The problem is that it always returns 160 (i.e. the requested byte to be read) not less than 160 even if the data is not available. what's the problem? help me. Thanks in advance.

Blowfly answered 31/12, 2010 at 10:2 Comment(5)
The input signal is a continuous stream, how do you expect it to end (i.e. have less than 160 bytes available)?Skin
I am getting voice sample from microphone and My sampling rate is 8000 Hz so 8000 samples per second encoding in 16 bit so to read 160 byte it requires at least 10 milliseconds but it send data at every 1 ms.Blowfly
Is it non blocking method? If yes than how to use this method?Blowfly
read is a blocking method. It will only return after it has read as many bytes you told it to read, or if the stream is closed, or if the stream indicates there is no more data available (for example when reading a file).Skin
Guess I answered it? Answer coming up.Skin
B
15

UPDATE: This bug in Android got fixed somewhere after 4.2.2 and before 5.01. On 5.01, the callbacks work exactly like the documents say they should.

It looks like read is blocking due to some shortsightedness of the developers.

Basically, when a audio recorder is initialized it allocates a sort of ring buffer, and when its told to .start(), it begins recording to that buffer.

Then when .read() is called, it reads up to half of the buffer size (or the requested size, whichever is less) and returns.

If it wants to read 1000 samples and there are only 900 available, it has to wait for 100 more samples before returning. If, however, there are more than 1000 samples, it reads those instantly and then returns right away.

Ideally, they would either provide a non-blocking read so that whatever is available is returned, or provide a way to know when a full read's worth of data is available so a non-blocking read can be performed.

I don't think they support the first. They appear to attempt to support the second by using the set period callback method, but so far I can't get that call back at the correct time to do a quick non-blocking read.

I've cloned the full source (8.5G bytes) of the native C++ source code and I'm trying to follow the functionality through all the layers to see how it's supposed to work.

The trick for non-blocking .read(0)ing is to only read when there's a full read's worth of samples ready. But determining when that condition is true is what I haven't figure out yet.

References: Here's the java code for .read() which calls a native C++ function:

http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.3.4_r1/android/media/AudioRecord.java#AudioRecord.read%28java.nio.ByteBuffer%2Cint%29

The above read() method calls the native native_read_in_direct_buffer() which is found in:

http://pdroid.googlecode.com/svn/android-2.3.4_r1/trunk/frameworks/base/core/jni/android_media_AudioRecord.cpp

which calls or points to android_media_AudioRecord_readInByteArray() which in turn calls AudioRecord.read() in frameworks/base/media/libmedia/frameworks/base/media/libmedia/AudioRecord.cpp (as best as I can tell) and it looks like in this function there's a do/while loop which essentially blocks until the desired number of bytes have been read (or half of the buffersize, whichever is smaller.)

I have tried to get the callbacks to function nicely but they seem to only call back at a time when read() has to wait for the audio buffer to be filled before it can return -- so what's the point.

My next goal is to try to track down the notify callback source code so I can get a guess on exactly what that's supposed to do and when.

Ballentine answered 3/4, 2013 at 7:7 Comment(4)
Hi Gordon, I'm developing an VoIP app which must know the delay of audio in hardware. The problem is AudioRecord.read() is a blocking method, so the only way I can do now is to read the full buffer to make it blocking everytime, and set the delay to the blocking time. i.e. Assume AudioRecord.getMinBufSize()=2048 Bytes, SampleRate=16000Hz, channel=MONO, so I read 1024 Samples everytime and assume it will blocking 1024/16=64ms per time, sometimes it worked, from 56~65ms, but sometimes it blocking up to 200+ ms, where are those 136ms audio going? How to do it properly? Regards.Khichabia
Hi BillHoo, I found that the audio recording feature in Android is buggy. I kept digging deeper and deeper into the source code - first java, then the underlying C++ - until I was way in over my head. It looks like the callbacks don't work the way they should and that there has been an open bug on android developer website for a year or two already and I kind of gave up. Obviously, blocking io is out of style now and it was clear from the source code that the authors intended for us to be able to write non-blocking callback driven audio recording, but they goofed and it doesn't work, unlessBallentine
BilLHoo (cont) unless you use threads... So, if I get the time, my plan is to use blocking reads but in a separate thread who's sole job is to just keep calling the blocking read. Then, one should be able to have the callback trigger in the main thread to grab the data as soon as it's available -- so you might try something like that. Unfortunately, I haven't tried this so I can't comment on how. I did find that by using blocking read I was able to capture every sample without losing sync.Ballentine
Thx Gordon. I am using thread indeed, but not the way you mentioned above. I'm keep reading from AudioRecord in T-1 and put the samples into a queue, and dealing with the queue in T-2 frame by frame(one frame is 10ms of samples) if there's any frame available. But the problem is I still don't know what the exact time the first sample of a frame is captured by the audio hardware, cus the sample is delayed by the AudioRecord blocking mechanism. I'll try your method sooner and if I get any progress, I'll give a feedback here. Thanks again for your idea :)Khichabia
S
8

read is a blocking method. It will only return after it has read as many bytes you told it to read, or if the stream is closed, or if the stream indicates there is no more data available (for example when reading a file).

AudioRecord being a continuous stream, the case of "no more data available" never applies.

Skin answered 1/1, 2011 at 5:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.