美文网首页
语音输入声波动画。AVAudioRecorder+三角函数,实现

语音输入声波动画。AVAudioRecorder+三角函数,实现

作者: weiweilong | 来源:发表于2018-09-10 18:02 被阅读378次

    前言:很久没写东西了,最近项目需要用到语音识别,需要做一个输入语音,根据声音的分贝大小,控制波纹振幅的动画,看了别人的轮子,感觉效果不错。最近比较咸鱼就自己摸索着大概模仿实现了一下。代码并不复杂请耐心观看。

    效果图:


    效果.GIF

    实现思路:

    • 1、首先获取输入声音的分贝大小变化值。
    • 2、绘制一条波纹曲线(曲线需具备两个性质,①能通过一个值控制曲线绘制的弧度,②实时平移实现流动效果)。
    • 3、波纹曲线根据声音分贝大小控制振幅。

    现在按照思路来一步一步实现。

    1、声音输入并获取分贝大小。

    简单音频处理首先想到的就是使用AVFoundation,使用起来比较简单,我们需要的效果能实现,像AudioToolbox 的AudioQueue虽然强大但是学习成本太高。

    使用AVFoundationAVAudioRecorder来实现录音及获取分贝大小。

    1)、设置录音参数。
    - (NSDictionary *)audioParamSetting {
        NSMutableDictionary *recordSetting = [[NSMutableDictionary alloc]init];
        //设置录音格式
        [recordSetting setValue:[NSNumber numberWithInt:kAudioFormatLinearPCM ] forKey:AVFormatIDKey];
        //设置录音采样率(Hz) 如:AVSampleRateKey==8000/44100/96000(影响音频的质量)
        [recordSetting setValue:[NSNumber numberWithFloat:8000] forKey:AVSampleRateKey];
        //录音通道数  1 或 2
        [recordSetting setValue:[NSNumber numberWithInt:1] forKey:AVNumberOfChannelsKey];
        //线性采样位数  8、16、24、32
        [recordSetting setValue:[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];
        //录音的质量
        [recordSetting setValue:[NSNumber numberWithInt:AVAudioQualityHigh] forKey:AVEncoderAudioQualityKey];
        
        return recordSetting;
    }
    
    2)、设置录音保存路径,并初始化AVAudioRecorder,进入录音准备状态。
    - (void)_initVoice {
        // 初始化录音参数
        NSDictionary *recordSetting = [self audioParamSetting];
        
        NSString *strUrl = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
        NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/Video.pcm", strUrl]];
        
        NSError *error;
        //初始化
        audioRecorder = [[AVAudioRecorder alloc]initWithURL:url settings:recordSetting error:&error];
        
        //开启音量检测
        audioRecorder.meteringEnabled = YES;
    
        audioRecorder.delegate = self;
        
        // 设备开启录音模式
        AVAudioSession *audioSession = [AVAudioSession sharedInstance];
        [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
        [audioSession setActive:YES error:nil];
        
        //创建录音文件,准备录音
        BOOL prepareSuccess = [audioRecorder prepareToRecord];
        NSLog(@"准备录音: %d", prepareSuccess);
    }
    

    现在只需要在点击开始时调用[audioRecorder record];方法就行了,相对停止调用[audioRecorder stop];

    3)、获取声音分贝大小。
    - (void)detectionVoice {
        [audioRecorder updateMeters];//刷新音量数据
        
        //获取音量的平均值  [recorder averagePowerForChannel:0];
        //音量的最大值  [recorder peakPowerForChannel:0];
        double lowPassResults = pow(10, (0.1 * [audioRecorder peakPowerForChannel:0]));
        NSLog(@"%f", lowPassResults);
    }
    

    通过AVAudioRecorderpeakPowerForChannel方法可以获取声音分贝峰值大小,但是获取值的范围是 -160~0 。声音峰值越大,越接近0。需要直接使用需要像上面做一下处理。

    到此声音输入并获取声音分贝大小实现了。


    接下来是实现 绘制一条波纹曲线(曲线需具备两个性质,①能通过一个值控制曲线绘制的弧度,②实时平移实现流动效果)。

    具备以上性质的曲线首选肯定是熟悉的三角函数了,y = Asin(ωx+φ)+ C,三角函数是否已忘记,要是公式中的参数意义已经忘记,请自行补一波三角函数的使用
    简单解释一下:
    三角函数公式:y = Asin(ωx+φ)+ C
    A:振幅,波纹在Y轴的高度,成正比,越大Y轴峰值越大。
    ω:和周期有关,越大周期越短。
    φ:横向偏移量,控制波纹的移动。
    C:整个波纹的Y轴偏移量。

    通过以上可以基本了解我们需要定义和控制的参数了。首先是波纹绘制显示的一个CAShapeLayer,再是一个控制振幅的参数CGFloat kRippleYMax,再是一个控制控制平移的偏移量CGFloat rippleOffset,最后是一个实时刷新的定时器CADisplayLink(控制波纹实时刷新,动起来)。


    CADisplayLink简单说明

    可能有些人对CADisplayLink比较陌生,下面简单说明一下CADisplayLink,还有和常用定时器NSTimer的区别。了解的可忽略以下说明。
    CADisplayLink也是定时器的一种。是一个能让屏幕刷新率相同的频率,将内容画到屏幕上的定时器,设备屏幕刷新周期是1秒60次。
    基本使用:

    // 创建
    CADisplayLink * displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(changeRipple)];
    [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    // 开始
    self.displayLink.paused = NO;
    // 停止
    self.displayLink.paused = YES;
    // 销毁
    [self.displayLink invalidate];
    

    和NSTimer区别:

    • 1、原理不同。
      每当屏幕显示内容刷新结束的时候,runloop就会向 CADisplayLink指定的target发送一次指定的selector消息,selector方法就会被调用一次。
      NSTimer当设定的周期时间到达后,runloop会向指定的target发送一次指定的selector消息。
    • 2、周期设置不同。
      CADisplayLink在不设置frameInterval时默认是1秒调用60次。
      NSTimer通过TimeInterval直接指定周期。
    • 3、精确度不同。
      CADisplayLink的精度更高,因为是通过屏幕刷新频率控制周期的,所有精度高。
      NSTimer精度低,当前runloop阻塞时,会推迟到下一个runloop周期执行。所以NSTimer有tolerance设置定时器的延迟范围。
    • 4、应用场景不同。
      CADisplayLink一般适合配合UI刷新重绘使用。
      NSTimer使用灵活,适合自定义循环任务。

    理解CADisplayLink定时器后,下面开始绘制波纹曲线了。
    首先设置懒加载波纹和定时器

    #pragma mark 波纹
    - (CAShapeLayer *)rippleLayer{
        if (_rippleLayer == nil) {
            CAShapeLayer * shapeLayer = [CAShapeLayer layer];
            shapeLayer.fillColor = [UIColor colorWithRed:145/255.0f green:186/255.0f blue:248/255.0f alpha:1].CGColor;
            shapeLayer.lineWidth = 2.0f;
            shapeLayer.strokeColor = [UIColor whiteColor].CGColor;
            [self.layer addSublayer:shapeLayer];
            _rippleLayer = shapeLayer;
        }
        return _rippleLayer;
    }
    #pragma mark 定时器
    - (CADisplayLink *)displayLink{
        if (_displayLink == nil) {
            CADisplayLink * displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(changeRipple)];
            [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
            _displayLink = displayLink;
        }
        return _displayLink;
    }
    

    然后再自定义的波纹view的drawRect方法中开始绘制波纹曲线。曲线使用三角函数sin绘制路径。其中kRippleYMax可控制曲线的振幅,rippleOffset可控制曲线的偏移量。

    - (void)drawRippleLayer{
        CGMutablePathRef path = CGPathCreateMutable();
        CGPathMoveToPoint(path, NULL, 0, rippleYOffset);
        for (int i = 0; i <= KRippleWidth ; i++) {
            CGFloat y = _kRippleYMax * sin(rippleCycle*i+self.rippleOffset) + rippleYOffset;
            CGPathAddLineToPoint(path, NULL, i, y);
        }
        self.rippleLayer.path = path;
        CGPathRelease(path);
    }
    

    然后再在定时器触发的方法中实时控制曲线平移。

    #pragma mark 定时器刷新页面
    - (void)changeRipple{
        self.rippleOffset += rippleSpeed;
        [self setNeedsDisplay];
    }
    
    现在获取声音的分贝峰值和波纹曲线都已经具备了,只需要定时获取声音分贝峰值大小,再根据峰值控制曲线就ok了。

    以上只贴出主要功能实现代码,具体实现请参考Demo

    相关文章

      网友评论

          本文标题:语音输入声波动画。AVAudioRecorder+三角函数,实现

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