Android 录音文件有杂音,录音文件时长比实际时长长问题解决

问题:在使用Android的AudioRecord进行录音时,录制生成的录音文件的时长比实际录制时间长,且录制的音频有一部分是有杂音或者没有声音的。

安卓出现录音问题还可以参考另一篇文章 Android 录音功能无法正常使用

最近被安排去在写安卓的项目了,出现上述的问题,看了好久的代码都没发现原因,最后就改了两行代码就好了,希望大家不要再掉这个坑里了。。。
本文先说录音的简单流程,然后是导致出现问题的代码,最后给出解决办法。

1、录音的简单流程
开始录音函数:首先判断录音的状态,如果为准备录音则开始录音,开始录音后创建了一个新的线程,设置录音的状态为正在录音,将录音数据写入pcm文件中。写文件操作通过一个while循环判断,如果当前状态为正在录音状态,则数据写入文件
停止录音函数:停止录音后,改变录音的状态,释放资源。
原始代码:

	 /**
     * 开始录音
     */
    public void startRecordAudio() {

        if (status == Status.STATUS_NO_READY || TextUtils.isEmpty(fileName)) {
            throw new IllegalStateException("录音尚未初始化,请检查是否禁止了录音权限~");
        }
        if (status == Status.STATUS_START) {
            throw new IllegalStateException("正在录音");
        }
        Log.d("AudioRecorder", "===startRecord===" + audioRecord.getState());
        audioRecord.startRecording();

        new Thread(new Runnable() {
            @Override
            public void run() {
                writeDataTOFile();
            }
        }).start();
    }

    /**
     * 停止录音
     */
    public void stopRecordAudio() {
        Log.d("AudioRecorder", "===stopRecord===");
        if (status == Status.STATUS_NO_READY || status == Status.STATUS_READY) {
            throw new IllegalStateException("录音尚未开始");
        } else {
            audioRecord.stop();
            status = Status.STATUS_STOP;
            Log.d("AudioRecorder","release()");
            release();
        }
    }

    /**
     * 将音频信息写入文件
     * @param listener 音频流的监听
     */
    private void writeDataTOFile() {
        // new一个byte数组用来存一些字节数据,大小为缓冲区大小
        byte[] audiodata = new byte[bufferSizeInBytes];
        FileOutputStream fos = null;
        int readsize = 0;
        try {
            File file = new File(FileUtil.getPcmFileAbsolutePath(fileName));  // 创建文件/storage/emulated/0/pauseRecordDemo/pcm/2020-10-20-10:18:14.pcm
            Log.d("TAG","文件名为 == "+file);
            if (file.exists()) {
                Log.d("TAG","文件已存在!");
                file.delete();
            }
            else {
                Log.d("TAG","该文件不存在,尝试创建该文件!");
                file.createNewFile();//多级目录
                if(file.exists())
                    Log.d("TAG","文件创建成功! " + file);
            }
            fos = new FileOutputStream(file);// 建立一个可存取字节的文件
        } catch (IllegalStateException e) {
            Log.e("AudioRecorder", e.getMessage());
            throw new IllegalStateException(e.getMessage());
        } catch (FileNotFoundException e) {
            Log.e("AudioRecorder", e.getMessage());
        } catch (IOException e) {
            e.printStackTrace();
        }
        //将录音状态设置成正在录音状态
        status = Status.STATUS_START;
        while (status == Status.STATUS_START) {
            readsize = audioRecord.read(audiodata, 0, bufferSizeInBytes);
            if (AudioRecord.ERROR_INVALID_OPERATION != readsize && fos != null) {
                try {
                    fos.write(audiodata);
                    if(fos==null) {
                        Log.d("TAG","写入文件失败");
                    }
                } catch (IOException e) {
                    Log.e("AudioRecorder", e.getMessage());
                }
            }
        }
        try {
            if (fos != null) {
                fos.close();// 关闭写入流
            }
        } catch (IOException e) {
            Log.e("AudioRecorder", e.getMessage());
        }
    }

    /**
     * 释放资源
     */
    public void release() {
        isRelease=false;
        Log.d("AudioRecorder", "===release===");
        pcmFilePath=FileUtil.getPcmFileAbsolutePath(fileName);  //获取pcm文件路径
        //将pcm文件转化为wav文件
        mergePCMFilesToWAVFile(pcmFilePath);
        if (audioRecord != null) {
            audioRecord.release();
            audioRecord = null;
        }
        status = Status.STATUS_NO_READY;
        isRelease=true;
    }

2、问题分析
上面的代码看起来是没有问题的,在录制时间较短的时候,甚至可能看不出出现问题了。
文件是有音频的,表示录音实际已经是在录了,但是出现文件时长、有杂音问题,首先定位应该是写文件的时候出现了错误。一开始认为是不是缓冲区的大小设置不对,通过打印日志发现这里没有问题。找了很久的原因,发现写文件是在一个新的线程中进行的,但是在写之前需要先判断当前的录音状态,如果当前状态为正在录音状态,才将数据写入文件。而在停止录音函数时,关键的两行代码,是先停止了录音,然后改变录音的状态
这里录音已经停止了,但是状态还没有来得及改变,导致将音频写入文件的线程中的while循环还在进行。简单来说,就是已经没有音频输入了,但是还在写文件。由此导致了音频文件的时长较实际时间长,而且有杂音。

3、问题解决
解决办法:先改变录音状态,再停止录音,只需要更换停止录音函数里面两行代码的位置。
正确代码:

	 /**
     * 停止录音
     */
    public void stopRecordAudio() {
        Log.d("AudioRecorder", "===stopRecord===");
        if (status == Status.STATUS_NO_READY || status == Status.STATUS_READY) {
            throw new IllegalStateException("录音尚未开始");
        } else {
            status = Status.STATUS_STOP;  //改变当前录音状态
            audioRecord.stop();  //停止录音
            Log.d("AudioRecorder","release()");
            release();  //释放资源
        }
    }

--------如有侵权,联系删除!

<think>好的,用户想了解如何在Unity中将录音保存为WAV文件。首先,我需要确定用户的使用场景。可能他们正在开发需要录音功能的应用程序,比如语音备忘录、游戏中的语音聊天功能,或者教育类的应用。用户可能是开发者,对Unity有一定基础,但可能熟悉音频处理部分。 接下来,要分析用户的需求。用户明确提到将录音转为WAV文件保存,但可能深层需求还包括如何开始录音、结束录音、保存文件的具体步骤,以及代码实现。此外,可能还需要考虑跨平台兼容性,比如在移动设备上的权限问题,以及如何确保音频质量和文件大小平衡。 首先,Unity的Microphone类是用来处理录音的,所以需要介绍如何使用它开始和结束录音。然后,将获取的音频数据转换为WAV文件需要了解WAV的格式,包括头文件的结构和PCM数据的处理。用户可能需要具体的代码示例,所以应该提供关键代码片段,比如开始录音、转换数据、保存文件的函数。 需要注意的点包括:录音的采样率、声道数,处理音频数据的字节转换,特别是正确处理头文件和PCM数据。另外,要考虑同平台下的文件存储路径,比如Application.persistentDataPath的使用。可能还需要提醒用户检查麦克风权限,尤其是在Android和iOS平台上,这可能会影响功能是否正常运行。 用户可能遇到的问题包括音频文件无法保存、格式正确、或者录制的音频杂音。因此,在回答中需要包含常见问题解决方法,比如检查麦克风是否可用,确保数据转换正确,处理同的音频格式等。 最后,要确保回答结构清晰,分步骤讲解,从设置录音参数到保存文件,每一步都详细说明,并提供完整的代码示例供用户参考。同,提醒用户测试代码,确保在同设备上运行正常。</think>在Unity中实现录音并保存为WAV文件需要以下关键步骤: 一、基础设置 1. 添加命名空间 ```csharp using UnityEngine; using System.Collections; using System.IO; ``` 二、核心代码结构 ```csharp public class AudioRecorder : MonoBehaviour { private AudioClip recordedClip; private bool isRecording = false; void Start() { // 检查麦克风设备 if (Microphone.devices.Length == 0) { Debug.LogError("未检测到麦克风设备"); } } } ``` 三、录音控制方法 ```csharp // 开始录音 public void StartRecording() { if (!isRecording) { recordedClip = Microphone.Start(null, true, 10, 44100); isRecording = true; } } // 停止录音 public void StopRecording() { if (isRecording) { Microphone.End(null); isRecording = false; SaveWavFile("test.wav", recordedClip); } } ``` 四、WAV格式转换核心算法 ```csharp private byte[] ConvertToWav(AudioClip clip) { using (MemoryStream stream = new MemoryStream()) { // WAV头文件(44字节) WriteHeader(stream, clip); // PCM数据写入 float[] samples = new float[clip.samples * clip.channels]; clip.GetData(samples, 0); byte[] bytes = new byte[samples.Length * 2]; for (int i = 0; i < samples.Length; i++) { short value = (short)(samples[i] * 32767); BitConverter.GetBytes(value).CopyTo(bytes, i * 2); } stream.Write(bytes, 0, bytes.Length); return stream.ToArray(); } } ``` 五、文件保存方法 ```csharp private void SaveWavFile(string filename, AudioClip clip) { string path = Path.Combine(Application.persistentDataPath, filename); File.WriteAllBytes(path, ConvertToWav(clip)); Debug.Log($"文件已保存至:{path}"); } ``` 六、注意事项 1. 音频参数优化: - 采样率推荐使用44100Hz(CD音质) - 单声道录制可节省存储空间 - 根据需求调整最大录制时长(当前设置为10秒) 2. 平台差异处理: - Android需要添加录音权限: ```xml <uses-permission android:name="android.permission.RECORD_AUDIO" /> ``` - iOS需在Player Settings启用"Microphone Usage Description" 3. 性能优化: - 长录音建议分块处理 - 使用内存流避免内存峰值 - 异步保存文件防止主线程阻塞 完整代码示例和WAV头文件写入方法可通过Unity Package Manager获取,建议在实际项目中添加错误处理机制和录音状态提示功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值