You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 07:03:18