使用AudioRecord录制的WAVE文件出现周期性细微滴答噪声问题
RODE VideoMicro录制WAV文件出现规律滴答噪声的排查
问题概述
使用RODE VideoMicro麦克风,通过Android的AudioRecord录制44100采样率、16位PCM的单声道WAVE文件,录制功能整体正常,但生成的文件中存在间隔均匀(0.135秒)的细微滴答噪声——音量调至最大可听见,也可通过Audacity清晰观察到。
因噪声间隔规律,最初怀疑是代码异常,但也不排除手机、外接麦克风或二者交互导致问题。当前录制的BufferSize为3528,通过AudioRecord.getMinBufferSize(SampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT)方法获取。
初始录制代码
... 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(); } }
BlockingQueue优化方案
按照建议,将录音采集与文件写入拆分为两个独立线程,通过LinkedBlockingDeque缓冲数据,但问题仍未解决:
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(); } } }
排查结论
尝试上述代码优化后,滴答噪声依然存在;但移除RODE VideoMicro麦克风后,噪声完全消失,因此怀疑是麦克风本身故障导致该问题。
内容的提问来源于stack exchange,提问作者Luis A. Florit
相关产品推荐
相关产品推荐

