美文网首页iOS聊天audio
iOS 降噪(PCM,任何文件格式都适用)

iOS 降噪(PCM,任何文件格式都适用)

作者: 编程_书恨少 | 来源:发表于2019-05-09 18:23 被阅读0次

    0.Demo地址

    https://github.com/majianghai/denoise

    1.为什么要降噪

    最近在做语音识别的东西,但是使用iPhone手机直接录制的语音中含有噪声,导致语音识别的时候,一直识别不出来,所以出现了降噪的需求。
    但是-----网上的资料真的很少,能用的更少!!!
    费了半天劲,终于找到一个wav格式降噪的iOS的,但是我要降噪的文件格式是PCM啊。这里其实降噪的函数用法是一样的,只是换一个文件就ok了。但是我也是初涉音频处理,里面有一个函数的参数,实在是刚开始不知道要传什么,都是一个一个试出来,加百度百科,开始研究这些声音格式和采样原理,最后终于搞定了。知道了这些概念之后,再做起来真的不难,就是刚开始没基础知识,没入门。

    废话不多说,直接讲代码

    2.降噪

    2.1 使用到的库

    这里我用到的库是WebRTC,个人感觉直接操作底层函数比较有安全感,改起来也方便,但是问题是很多函数的参数是没有具体的解释的,配置起来真的是费劲,需要反复尝试,查相关资料。值得庆幸的是,网上的资料真的少,对进度产生了非常大的影响。
    其实设计到的函数并不多,就四五个。
    导致耗时的问题,
    1、降噪函数需要一个,计算音频数据的总采样数,这样一个参数。刚开始不知道这个采样总数是如何计算的,网上搜索也没有给出什么有用的解释,最后还是找到一个似是而非的公式,抱着试一试的心态写了,然后和Demo中的wav音频的采样总数做对比,发现一样,才相信是这么回事。

    总音频采样数 = 音频总时长(毫秒) / 10 * 采样率
    

    就是这么个公式,但是真的不好搜,你可以试试。

    2.2 音频录制

    这里先用iphone录制音频,然后进行降噪处理,录制音频的采样率一定要和降噪处理的采样率保持一致

    // 开始录音
    - (void)startRecorder {
        
        AVAudioSession * session = [AVAudioSession sharedInstance];
        NSError *sessionError;
        [session setCategory:AVAudioSessionCategoryPlayAndRecord error:&sessionError];
        if (session == nil) {
        }else{
            [session setActive:YES error:nil];
        }
        
        //录音设置
        NSMutableDictionary * recordSetting = [[NSMutableDictionary alloc]init];
        //设置录音格式
        [recordSetting  setValue:[NSNumber numberWithInt:kAudioFormatLinearPCM] forKey:AVFormatIDKey];
        //设置录音采样率(HZ)
        [recordSetting setValue:[NSNumber numberWithFloat:16000] forKey:AVSampleRateKey];
        //录音通道数
        [recordSetting setValue:[NSNumber numberWithInt:1] forKey:AVNumberOfChannelsKey];
        //线性采样位数
        [recordSetting  setValue:[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];
        //录音的质量
        [recordSetting  setValue:[NSNumber numberWithInt:AVAudioQualityMax] forKey:AVEncoderAudioQualityKey];
        
        
        NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES)lastObject];
        
        
        NSFileManager *fileManager = [NSFileManager defaultManager];
        if(![fileManager fileExistsAtPath:docPath]) {
            [fileManager createDirectoryAtPath:docPath withIntermediateDirectories:YES attributes:nil error:nil];
        }
        //创建url
        self.playerPath = [VoiceTool filePath];
        
        NSURL * url = [NSURL fileURLWithPath:self.playerPath];//voice.aac
        NSError *error;
        //初始化AVAudioRecorder
        if(!self.recorder){
            self.recorder = [[AVAudioRecorder alloc] initWithURL:url settings:recordSetting error:&error];
            //开启音量监测
            self.recorder.meteringEnabled = YES;
            self.recorder.delegate = self;
        }
        
        if(error){
            NSLog(@"创建录音对象时发生错误,错误信息:%@",error.localizedDescription);
        }
        
        [self.recorder record];
    }
    
    // 结束录音
    - (void)cancelRecorder {
        [self.recorder stop];
    }
    
    // 播放录音
    - (void)playVoice {
        NSLog(@"-------");
        
        NSString *filePath = [VoiceTool filePath];
        
        NSURL * url = [NSURL fileURLWithPath:filePath];//voice.aac
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        self.player = [[AVAudioPlayer alloc] initWithData:data error:nil];
        [self.player prepareToPlay];
        [self.player play];
    }
    
    + (NSString *)filePath {
        NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES)lastObject];
        
        NSString *filePa = [NSString stringWithFormat:@"%@/voice.pcm",docPath];
    
        return  filePa;
    }
    

    2.3 音频降噪

    降噪代码如下

    +(int)nsProcess {
        
        NSString *filePath = [VoiceTool filePath];
        
        NSData *sourceData = [NSData dataWithContentsOfFile:filePath];
        int16_t *buffer = (int16_t *)[sourceData bytes];
        uint32_t sampleRate = 16000;
        int samplesCount = [VoiceTool voiceLength];
        int level = kLow;
        
        if (buffer == nullptr) return -1;
        if (samplesCount == 0) return -1;
        size_t samples = MIN(160, sampleRate / 100);
        if (samples == 0) return -1;
        uint32_t num_bands = 1;
        int16_t *input = buffer;
        size_t nTotal = (samplesCount / samples);
        NsHandle *nsHandle = WebRtcNs_Create();
        int status = WebRtcNs_Init(nsHandle, sampleRate);
        if (status != 0) {
            printf("WebRtcNs_Init fail\n");
            return -1;
        }
        status = WebRtcNs_set_policy(nsHandle, level);
        if (status != 0) {
            printf("WebRtcNs_set_policy fail\n");
            return -1;
        }
        for (int i = 0; i < nTotal; i++) {
            int16_t *nsIn[1] = {input};   //ns input[band][data]
            int16_t *nsOut[1] = {input};  //ns output[band][data]
            WebRtcNs_Analyze(nsHandle, nsIn[0]);
            WebRtcNs_Process(nsHandle, (const int16_t *const *) nsIn, num_bands, nsOut);
            input += samples;
        }
        WebRtcNs_Free(nsHandle);
        
        
        NSData *data = [NSData dataWithBytes:buffer length:[sourceData length]];
        BOOL isWrite = [data writeToFile:filePath atomically:YES];
        if (isWrite) {
            NSLog(@"----写入成功");
        }
        
        return 1;
    }
    

    涉及到的函数

    // 1.创建句柄
    NsHandle *WebRtcNs_Create()
    
    // 2.根据句柄和采样率进行初始化
    第一个参数是句柄,第二个参数是采样率
    int WebRtcNs_Init(NsHandle *NS_inst, uint32_t fs)
    
    // 3.根据句柄和降噪模式,设置降噪策略
    // mode:4种模式分别对应:0/1/2/3,数值越高,效果越明显
    int WebRtcNs_set_policy(NsHandle *NS_inst, int mode)
    
    // 4.降噪,根据采样间隔,将数据输入到降噪函数
    void WebRtcNs_Process(NsHandle *NS_inst,
                          const int16_t *const *spframe,
                          size_t num_bands,
                          int16_t *const *outframe)
    

    3.降噪之后的音频播放

    降噪之后的音频,用之前写的播放功能是播不出来的,需要使用VLC工具,这个可以自行下载。

    打开mac终端,输入如下命令:
    /Applications/VLC.app/Contents/MacOS/VLC --demux=rawaud --rawaud-channels 1 --rawaud-samplerate 16000  + {$pcm文件路径}
    
    
    

    4.遇到的坑

    4.1 音频总采样数

    这个上面说了

    4.2 读取音频数据为空

    + (int)voiceLength {
        
        NSString *filePath = [VoiceTool filePath];
    
        NSURL *url = [NSURL fileURLWithPath:filePath];
        
        NSData *data = [NSData dataWithContentsOfURL:url];
    
        AVAudioPlayer* player = [[AVAudioPlayer alloc] initWithData:data error:nil];
        
        float duration = player.duration;
    
        float lengMs = duration * 1000;
    
        int len = lengMs / 10 * VOICE_RATE_UNIT;
        
        return len;
    }
    

    在这个方法中,上面这样写才能读取到文件
    但是还有一种写法,下面就是这种写法,下面这种写法,当文件在电脑上的时候可以,但是当文件在document中的时候,就读取不到数据了

    - (void)voiceLength {
       NSString *inpath = [[NSBundle mainBundle] pathForResource:@"a.wav" ofType:nil];
    
        AVURLAsset* audioAsset =[AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath: inpath] options:nil];
    
        CMTime audioDuration = audioAsset.duration;
    
        float audioDurationSeconds = CMTimeGetSeconds(audioDuration);
    
        float lengMs = audioDurationSeconds * 1000;
    
        int len = lengMs / 10 * 80;
    }
    

    相关文章

      网友评论

        本文标题:iOS 降噪(PCM,任何文件格式都适用)

        本文链接:https://www.haomeiwen.com/subject/hfmwoqtx.html