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

Android AudioRecord录制WAV保存后无法找到问题求助

Android AudioRecord录制音频后无法找到保存的WAV文件

我在Android开发中使用AudioRecord录制音频信号,通过AFSK解码音频内容,切换至SSTV模式时将录制的音频保存到设备外部存储。Logcat日志已显示文件的保存路径,但实际无法找到该WAV文件。尝试过调整代码变量的作用域(将变量设为全局、移到循环外)、创建专属保存目录等操作,问题仍未解决。

相关实现代码

startAFSKDecoder方法

private void startAFSKDecoder(double specialFrequencySSTVactivator) {
checkAndShowAFSKSignalDetected();

    Thread thread = new Thread(() -> {
        boolean saveAudio = false; // Flag to indicate when to save audio
        String timestamp = null; // Initialize timestamp outside the loop
        AudioRecord audioRecord = null; // Initialize AudioRecord outside the loop

     //   String outputFile = null;

        Log.d("349 Activation", "AFSK Successfully Activated ");
        if (isDecoderSwitched) {
            Log.d("ALERT351", "Previously Decoded Message is Cleared");
            isAFSKDecodingActive = true;
            isDecoderSwitched = false;
        }


        int bufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
        if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
            return;
        }

        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault());
        timestamp = sdf.format(new Date()); // Assign timestamp here

        // Specify the output file path with WAV extension in external storage
       // audioFilePath = externalDir.getAbsolutePath() + "/audio_" + timestamp + ".wav"; // Assign to global variable
        audioFilePath = Environment.getExternalStorageDirectory() + "/audio_" + timestamp + ".wav"; // Assign to global variable

        audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);

        outputFile = audioFilePath;

        try {
            audioRecord.startRecording();
            if (audioRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
                Log.e("AudioRecordState", "AudioRecord is not in RECORDSTATE_RECORDING state after starting.");
                return; // Exit the thread or handle the error appropriately
            }
            Log.d("AudioRecordAFSK381", "Started audio recording successfully.");
        } catch (IllegalStateException e) {
            Log.e("AFSK_Decoder", "Failed to start audio recording: " + e.getMessage());
            e.printStackTrace();
            return; // Exit the thread or handle the error appropriately
        }

        StringBuilder binaryText = new StringBuilder();
        boolean previousBit = false;
        long startTime = System.currentTimeMillis();

        while (isAFSKDecodingActive) {
            short[] buffer = new short[bufferSize];

            // Before starting recording
            Log.d("AudioRecording", "Starting audio recording...");

                // Inside your audio recording loop
            int bytesRead = audioRecord.read(buffer, 0, bufferSize);
            if (bytesRead <= 0) {
                Log.d("AudioRecording", "No audio data read.");
                continue;
            }


            double frequency = calculateInstantaneousFrequency(buffer);
            if (Math.abs(frequency - specialFrequencySSTVactivator) <= frequencyTolerance) {
                Log.d("AFSK_Decoder", "Detected Special Frequency - Switching to SSTV");

                isAFSKDecodingActive = false;
                isSSTVDecodingActive = true;

                if (audioRecord != null) {
                    try {
                        audioRecord.stop();
                        audioRecord.release();

                        // After stopping recording
                        Log.d("AudioRecording", "Recording stopped.");

                        if (audioFilePath != null) {
                            Log.d("RecordedOutputExist", "Records Exist");
                            saveAudioFile(audioFilePath, "audio_" + timestamp + ".wav");
                        } else {
                            Log.d("NoRecordedOutputExist", "NO Records Exist");
                        }

                    } catch (IllegalStateException e) {
                        Log.e("AFSK_Decoder417", "Failed to stop audio recording: " + e.getMessage());
                    }
                }

                decoder = null;
                stopRecordingAFSK();
                break;
            } else {
                for (int i = 0; i < bytesRead; i++) {
                        double amplitude = (double) buffer[i] / Short.MAX_VALUE;
                        if (Math.abs(amplitude) < THRESHOLD) {
                            amplitude = 0;
                        }
                        boolean currentBit = amplitude > 0;
                        if (currentBit != previousBit) {
                            if (currentBit) {
                                binaryText.append("1");
                                //Log.d("AFSK_Decoder", "Detected Rising Edge (Mark Bit).");
                            } else {
                                binaryText.append("0");
                                //Log.d("AFSK_Decoder", "Detected Falling Edge (Space Bit).");
                            }
                        }
                        previousBit = currentBit;
                    }
                    if (binaryText.length() >= MIN_BIT_COUNT && System.currentTimeMillis() - startTime > DURATION_THRESHOLD) {
                        String decodedText = getDecodedText(binaryText.toString(), buffer);
                        appendToDecodedText(decodedText);
                        binaryText.setLength(0);
                        startTime = System.currentTimeMillis();
                    }

            }
        }


    });
    thread.start();
}

saveAudioFile方法

private void saveAudioFile(String sourceFilePath, String destinationFileName) {
// Check if external storage is available for read and write
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
// Get the root external storage directory
File externalDir = Environment.getExternalStorageDirectory();

        // Use externalDir for saving files directly in the root
        File destinationFile = new File(externalDir, destinationFileName);

        // Get the absolute paths
        String sourceAbsolutePath = sourceFilePath;
        String destinationAbsolutePath = destinationFile.getAbsolutePath();

        // Copy the recorded audio from source to destination
        try {
            FileInputStream inputStream = new FileInputStream(sourceAbsolutePath);
            FileOutputStream outputStream = new FileOutputStream(destinationFile);
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
            inputStream.close();
            outputStream.close();

            Log.d("FilePathDebug", "Source File Path: " + sourceFilePath);

            Log.d("FilePathDebug", "Destination File Path: " + destinationFile.getAbsolutePath());

            // Delete the source file if needed
            File sourceFile = new File(sourceAbsolutePath);
            if (sourceFile.exists()) {
                sourceFile.delete();
            }
        } catch (IOException e) {
            Log.e("FileCopyError", "Error copying audio file: " + e.getMessage());
        }
    } else {
        Log.e("ExternalStorageError", "External storage is not available or not writable.");
    }
}

问题核心原因

  1. 未实际写入音频数据到文件:AudioRecord仅将音频读取到内存缓冲区,代码中没有将read获取到的PCM数据写入到audioFilePath指向的文件,导致该路径下根本不存在文件。
  2. saveAudioFile逻辑无效:传入的源文件路径是未生成的空文件,拷贝操作从一开始就失败,后续删除操作也不会有实质效果。
  3. 缺少WAV文件头:即使写入了PCM数据,裸PCM没有WAV文件头,也无法被系统识别为可播放的WAV文件。

修复方案

步骤1:录制过程中写入PCM数据到临时文件

修改startAFSKDecoder方法,在循环读取音频数据时,将内存中的PCM数据写入到临时文件:

private void startAFSKDecoder(double specialFrequencySSTVactivator) {
    checkAndShowAFSKSignalDetected();

    Thread thread = new Thread(() -> {
        String timestamp = null;
        AudioRecord audioRecord = null;
        FileOutputStream pcmOutputStream = null;
        File pcmFile = null;

        Log.d("349 Activation", "AFSK Successfully Activated ");
        if (isDecoderSwitched) {
            Log.d("ALERT351", "Previously Decoded Message is Cleared");
            isAFSKDecodingActive = true;
            isDecoderSwitched = false;
        }

        int bufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
        // 动态申请录音权限
        if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
            return;
        }
        // 动态申请存储权限(Android 6.0+)
        if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1001);
            return;
        }

        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault());
        timestamp = sdf.format(new Date());

        // 创建临时PCM文件
        pcmFile = new File(Environment.getExternalStorageDirectory(), "audio_" + timestamp + ".pcm");
        try {
            pcmOutputStream = new FileOutputStream(pcmFile);
        } catch (IOException e) {
            Log.e("AFSK_Decoder", "Failed to create PCM file: " + e.getMessage());
            return;
        }

        audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);

        try {
            audioRecord.startRecording();
            if (audioRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
                Log.e("AudioRecordState", "AudioRecord is not in RECORDSTATE_RECORDING state after starting.");
                return;
            }
            Log.d("AudioRecordAFSK381", "Started audio recording successfully.");
        } catch (IllegalStateException e) {
            Log.e("AFSK_Decoder", "Failed to start audio recording: " + e.getMessage());
            e.printStackTrace();
            return;
        }

        StringBuilder binaryText = new StringBuilder();
        boolean previousBit = false;
        long startTime = System.currentTimeMillis();

        while (isAFSKDecodingActive) {
            short[] buffer = new short[bufferSize];
            int bytesRead = audioRecord.read(buffer, 0, bufferSize);
            if (bytesRead <= 0) {
                Log.d("AudioRecording", "No audio data read.");
                continue;
            }

            // 将PCM数据写入临时文件
            try {
                byte[] byteBuffer = shortToByteArray(buffer, bytesRead);
                pcmOutputStream.write(byteBuffer);
            } catch (IOException e) {
                Log.e("AFSK_Decoder", "Failed to write PCM data: " + e.getMessage());
            }

            double frequency = calculateInstantaneousFrequency(buffer);
            if (Math.abs(frequency - specialFrequencySSTVactivator) <= frequencyTolerance) {
                Log.d("AFSK_Decoder", "Detected Special Frequency - Switching to SSTV");

                isAFSKDecodingActive = false;
                isSSTVDecodingActive = true;

                // 关闭流和录音
                try {
                    if (pcmOutputStream != null) pcmOutputStream.close();
                    audioRecord.stop();
                    audioRecord.release();
                } catch (Exception e) {
                    Log.e("AFSK_Decoder417", "Error stopping recording: " + e.getMessage());
                }

                // 转换PCM为标准WAV文件
                convertPcmToWav(pcmFile.getAbsolutePath(), Environment.getExternalStorageDirectory() + "/audio_" + timestamp + ".wav");

                // 删除临时PCM文件
                if (pcmFile.exists()) pcmFile.delete();

                decoder = null;
                stopRecordingAFSK();
                break;
            } else {
                // 原有AFSK解码逻辑
                for (int i = 0; i < bytesRead; i++) {
                    double amplitude = (double) buffer[i] / Short.MAX_VALUE;
                    if (Math.abs(amplitude) < THRESHOLD) amplitude = 0;
                    boolean currentBit = amplitude > 0;
                    if (currentBit != previousBit) {
                        binaryText.append(currentBit ? "1" : "0");
                    }
                    previousBit = currentBit;
                }
                if (binaryText.length() >= MIN_BIT_COUNT && System.currentTimeMillis() - startTime > DURATION_THRESHOLD) {
                    String decodedText = getDecodedText(binaryText.toString(), buffer);
                    appendToDecodedText(decodedText);
                    binaryText.setLength(0);
                    startTime = System.currentTimeMillis();
                }
            }
        }
    });
    thread.start();
}

// short数组转byte数组工具方法
private byte[] shortToByteArray(short[] shorts, int length) {
    byte[] bytes = new byte[length * 2];
    for (int i = 0; i < length; i++) {
        bytes[i * 2] = (byte) (shorts[i] & 0x00FF);
        bytes[i * 2 + 1] = (byte) ((shorts[i] & 0xFF00) >> 8);
    }
    return bytes;
}

步骤2:添加PCM转WAV的方法

给裸PCM数据添加WAV文件头,生成可识别的WAV文件:

private void convertPcmToWav(String pcmFilePath, String wavFilePath) {
    File pcmFile = new File(pcmFilePath);
    File wavFile = new File(wavFilePath);
    long pcmSize = pcmFile.length();

    try (FileInputStream fis = new FileInputStream(pcmFile);
         FileOutputStream fos = new FileOutputStream(wavFile)) {

        // 写入WAV文件头
        byte[] header = new byte[44];
        // RIFF标识
        header[0] = 'R'; header[1] = 'I'; header[2] = 'F'; header[3] = 'F';
        // 文件总大小
        long totalSize = pcmSize + 36;
        header[4] = (byte)(totalSize & 0xff);
        header[5] = (byte)((totalSize >> 8) & 0xff);
        header[6] = (byte)((totalSize >> 16) & 0xff);
        header[7] = (byte)((totalSize >> 24) & 0xff);
        // WAVE标识
        header[8] = 'W'; header[9] = 'A'; header[10] = 'V'; header[11] = 'E';
        // fmt子块标识
        header[12] = 'f'; header[13] = 'm'; header[14] = 't'; header[15] = ' ';
        // fmt子块大小(PCM为16)
        header[16] = 16; header[17] = 0; header[18] = 0; header[19] = 0;
        // 音频格式(PCM为1)
        header[20] = 1; header[21] = 0;
        // 声道数(单声道为1)
        header[22] = 1; header[23] = 0;
        // 采样率
        int sampleRate = SAMPLE_RATE;
        header[24] = (byte)(sampleRate & 0xff);
        header[25] = (byte)((sampleRate >> 8) & 0xff);
        header[26] = (byte)((sampleRate >> 16) & 0xff);
        header[27] = (byte)((sampleRate >> 24) & 0xff);
        // 字节率
        int byteRate = sampleRate * 1 * 16 / 8;
        header[28] = (byte)(byteRate & 0xff);
        header[29] = (byte)((byteRate >> 8) & 0xff);
        header[30] = (byte)((byteRate >> 16) & 0xff);
        header[31] = (byte)((byteRate >> 24) & 0xff);
        // 块对齐
        header[32] = (byte)(1 * 16 / 8); header[33] = 0;
        // 位深(16位)
        header[34] = 16; header[35] = 0;
        // data子块标识
        header[36] = 'd'; header[37] = 'a'; header[38] = 't'; header[39] = 'a';
        // 数据大小
        header[40]
相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 00:02:49