Reputation: 2599
My app saves WAVE MONO files at 44100 and 16BIT PCM captured with AudioRecord
using a RODE VideoMicro. Everything works just fine, except for the fact that I can see some very subtle "ticks" in the WAVE files uniformly spaced by 0.135 seconds. You can hear the ticks if your volume is very loud, or you can see them clearly using Audacity. Here is a sample WAVE file for you to analyze, and here is a screenshot of Audacity with 4 ticks in the soundfile:
Any idea what is happening?
I am not sure where those ticks come from, but I suspect that there is something strange in my code, since the tics are so uniformly spaced. But of course it could be either the phone, or the external microphone, or the interaction between the two. The BufferSize
is 3528 and is retrieved with
static int BufferSize = AudioRecord.getMinBufferSize(SampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
This is the relevant part of my code:
...
recorder = new AudioRecord((denoise.isChecked()) ? MediaRecorder.AudioSource.VOICE_RECOGNITION : MediaRecorder.AudioSource.MIC,
SampleRate, AudioFormat.CHANNEL_IN_MONO, AudioEncoding, 2 * BufferSize); // 2 bytes in 16bit format
try {
recordingThread = new Thread(this::writeAudioDataToPCMFile, "AudioRecorder Thread");
recordingThread.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void writeAudioDataToPCMFile() {
String auxfile = AveActivity.REC_DIR + "/aux" + new Random().nextInt() + ".pcm";
short[] sData = new short[BufferSize];
int product;
int i, readSize;
int tooloud;
int tooloudlim = BufferSize / 50; // 2% clip
long mean;
FileOutputStream os;
try {
os = new FileOutputStream(auxfile);
} catch (FileNotFoundException e) {
runOnUiThread(() -> textsound.setText(r.getString(R.string.errstart)));
e.printStackTrace();
return;
}
waveformLastList = new ArrayList<>();
//int j=0;
while (isRecording) {
readSize = recorder.read(sData, 0, BufferSize);
tooloud = 0;
mean = 0;
for (i = 0; i < readSize; ++i) {
product = (int) (sData[i] * gain);
if (Math.abs(product) <= Short.MAX_VALUE) {
sData[i] = (short) product;
} else {
// Audio clipping!
sData[i] = (short) (Integer.signum(product) * Short.MAX_VALUE);
++tooloud;
}
mean += sData[i] * sData[i];
}
if (tooloud > tooloudlim)
runOnUiThread(this::tooLoudWarning);
try {
os.write(short2byte(sData), 0, 2 * BufferSize); // 2 bytes in 16bit format
} catch (IOException e) {
e.printStackTrace();
}
}
try {
os.close();
CharSequence time = DateFormat.format("yyMMdd-kkmmss", new Date(System.currentTimeMillis()));
song = birdname + "_" + time + ((denoise.isChecked()) ? "_NR" : "") + ((db == 0) ? "" : "_db" + db) + "." + songFormat;
runOnUiThread(() -> textsound.setText(r.getString(R.string.saving, songFormat)));
PCMtoWAV(new File(auxfile), new File(AveActivity.REC_DIR, song));
} catch (IOException e) {
runOnUiThread(() -> textsound.setText(r.getString(R.string.errsaving, songFormat)));
e.printStackTrace();
}
runOnUiThread(() -> {
activatebtns();
micColor(Color.WHITE);
});
}
private void PCMtoWAV(File input, File output) throws IOException {
// Bien rapido: 1/10 sec para cada minuto de gravacion
byte[] data = new byte[2 * BufferSize];
try (FileOutputStream outStream = new FileOutputStream(output)) {
FileInputStream inStream = new FileInputStream(input);
// Write WAVE header
outStream.write(buildWAVHeader(input.length()));
// Write audio data
while (inStream.read(data) != -1)
outStream.write(data);
// Write Metadata to LIST....INFO chunk
outStream.write(buildWAVMeta());
inStream.close();
outStream.close();
input.delete();
}
}
EDIT:
Following Uriel advice, I implemented a BlockingQueue
like this:
public void RecordSong() {
if (isRecording) {
isRecording = false;
recorder.stop();
recorder.release();
recordingThread.interrupt();
writingThread.interrupt();
...
} else {
recorder = new AudioRecord(...);
try {
LinkedBlockingDeque<Byte> queue = new LinkedBlockingDeque<>();
recorder.startRecording();
recordingThread = new Thread(() -> captureAudioData(queue), "AudioRecorder Capture Thread");
writingThread = new Thread(() -> writeAudioDataToPCMFile(queue), "AudioRecorder Writing Thread");
recordingThread.setPriority(Thread.MAX_PRIORITY);
writingThread.setPriority(Thread.MAX_PRIORITY);
recordingThread.start();
writingThread.start();
...
}
}
private void captureAudioData(LinkedBlockingDeque<Byte> q) {
...
while (isRecording) {
capture,gain,etc...
try {
for (byte b : short2byte(sData)) q.put(b);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void writeAudioDataToPCMFile(LinkedBlockingDeque<Byte> q) {
String auxfile = AveActivity.REC_DIR + "/aux" + new Random().nextInt() + ".pcm";
FileOutputStream os;
try {
os = new FileOutputStream(auxfile);
} catch (FileNotFoundException e) {
e.printStackTrace();
return;
}
try {
while (true) os.write(q.take());
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
try {
os.close();
} catch (IOException e1) {
e.printStackTrace();
}
}
}
EDIT 2:
I tried the new approach (locked in the bathroom at 2am!). Unfortunately the problem persists. No change. It seems it is either a phone issue or a mic issue. In fact, removing the mic seems to get rid of the ticks, so probably it is a mic issue.
Upvotes: 0
Views: 125
Reputation: 14622
What you are experiencing is probably a pop sound that is called "popcorn". It usually happen when there is gaps in the wave file. what I suggest you to do is 2 things.
This is what i do in my app and there are no pops. LMK if it helped.
Upvotes: 1