《AV Foundation 开发秘籍》读书笔记(二)

第二章 音频播放和录制

1. 音频会话

音频会话分类

Category播放类型后台播放静音或屏幕关闭音频输入音频输出作用
AVAudioSessionCategoryAmbient混合播放有影响支持游戏背景音乐
AVAudioSessionCategorySoloAmbient(默认)独占播放有影响支持微信中播放语音
AVAudioSessionCategoryPlayback可选支持支持音频播放器
AVAudioSessionCategoryRecord独占录音支持支持微信中录制语音
AVAudioSessionCategoryPlayAndRecord可选支持支持支持微信语音聊天
AVAudioSessionCategoryAudioProcessing——————硬件解码音频
AVAudioSessionCategoryMultiRoute支持支持多设备输入输出

上述分类所提供的几种常见行为可以满足大部分应用程序的需要,如果需要更复杂的功能,上述其中一种分类可以通过使用 options 和 modes 方法进一步自定义开发。

激活音频会话

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    AVAudioSession *session = [AVAudioSession sharedInstance];
    NSError *error;
    if (![session setCategory:AVAudioSessionCategoryPlayback error:&error]) {
        NSLog(@"Category Error : %@", error.localizedDescription);
    }
    if (![session setActive:YES error:&error]) {
        NSLog(@"Activation Error : %@", error.localizedDescription);
    }

    return YES;
}

如果分类允许后台播放,则应该打开 Capabilities 中的 Background Modes 继而勾选后台播放音频选项

2. 音频播放

除非需要从网络流中播放音频、需要访问原始音频样本,或者需要非常低的时延,否则 AVAudioPlayer 都能胜任

- (void)viewDidLoad {
    [super viewDidLoad];

    NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"白洁01" withExtension:@"mp3"];

    // Must Maintain a strong reference to player
    self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];

    if (self.player) {
        self.player.numberOfLoops = -1;  // 循环播放
        [self.player prepareToPlay];
    }
}

- (IBAction)play {

    [self.player play];
}

prepareToPlay 方法是可选的,在调用 play 方法前也会自动调用,作用是取得需要的音频硬件并预加载 Audio Queue 的缓冲区,降低调用 play 方法后的延时。

- (IBAction)pause {

    [self.player pause];
}

- (IBAction)stop {

    [self.player stop];
    self.player.currentTime = 0.0f;
}

通过 pause 和 stop 方法停止的音频都会继续播放。最主要的区别在底层处理上,调用 stop 方法会撤销调用 prepareToPlay 时所做的设置,而调用 pause 方法则不会。

// 音量 0 ~ 1
- (IBAction)voice:(UISlider *)sender {

    self.player.volume = sender.value;
}

// 声道 -1 ~ 1
- (IBAction)pan:(UISlider *)sender {

    self.player.pan = sender.value;
}

// 速率 0.5 ~ 2
- (IBAction)speed:(UISlider *)sender {

    self.player.rate = sender.value;
}

如果要改变速率,在初始化 AVAudioPlayer 时应做出如下设置

self.player.enableRate = YES;

3. 处理中断事件

当有电话呼入、闹钟响起的时候,播放中的音频会慢慢消失和暂停,但是终止通话后,播放、停止按钮的控件和音频的播放没有恢复。为了优化用户体验,需要监听这些事件,并作出处理:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
- (void)handleInterruption:(NSNotification *)notification
{
    NSDictionary *info = notification.userInfo;

    AVAudioSessionInterruptionType type = [info[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
    if (type == AVAudioSessionInterruptionTypeBegan) {

        // 中断开始,设置停止音乐
        [self.player pause];

    } else {

        // 中断结束,判断是否允许继续播放
        AVAudioSessionInterruptionOptions options = [info[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue];
        if (options == AVAudioSessionInterruptionOptionShouldResume) {

            // 允许继续播放,则继续播放
            [self.player play];
        }
    }
}

4. 对线路改变的响应

播放音频期间插入耳机,音频输出线路变成耳机插孔并继续播放。断开耳机连接,音频线路再次回到设备的内置扬声器播放。虽然线路变化和预期一样,不过按照苹果官方文档,认为该音频应该处于静音状态。

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleRouteChange:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]];
- (void)handleRouteChange:(NSNotification *)notification
{
    NSDictionary *info = notification.userInfo;

    AVAudioSessionRouteChangeReason reason = [info[AVAudioSessionRouteChangeReasonKey] unsignedIntegerValue];
    if (reason == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {

        AVAudioSessionRouteDescription *previousRoute  = info[AVAudioSessionRouteChangePreviousRouteKey];
        AVAudioSessionPortDescription  *previousOutput = previousRoute.outputs[0];
        if ([previousOutput.portType isEqualToString:AVAudioSessionPortHeadphones]) {

            // 停止播放音乐
            [self.player stop];
        }
    }
}

5. 音频录制

一般情况存在录音功能,必然会有播放功能,所以不能使用默认的录制音频会话,应该使用既可以录制又能播放的 AVAudioSessionCategoryPlayAndRecord

- (void)viewDidLoad {
    [super viewDidLoad];

    NSURL *url = [NSURL fileURLWithPath:[@"Users/mayan/Desktop" stringByAppendingPathComponent:@"voice.caf"]];
    NSDictionary *settings = @{
                               AVFormatIDKey            : @(kAudioFormatAppleIMA4),
                               AVSampleRateKey          : @22050.0f,
                               AVNumberOfChannelsKey    : @1,
                               };
    self.recorder = [[AVAudioRecorder alloc] initWithURL:url settings:settings error:nil];

    if (self.recorder) {
        self.recorder.delegate = self;
        [self.recorder prepareToRecord];
    }
}

- (IBAction)record {

    [self.recorder record];
}

- (IBAction)recordFinish {

    [self.recorder stop];
}

// 音频录制完成调用
- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag
{
    if (flag) {

        // 一般把录制好的音频复制或者剪切到目的文件夹下
        NSURL *srcURL = self.recorder.url;
        NSURL *destURL = [NSURL URLWithString:@"目的文件路径/音频文件名称.caf"];

        [[NSFileManager defaultManager] copyItemAtURL:srcURL toURL:destURL error:nil];
    }
}

在录制音频过程中,Core Audio Format(CAF)通常是最好的容器格式,因为它和内容无关可以保存 Core Audio 支持的任何音频格式。在设置字典中指定的键值信息也值得讨论一番:

音频格式

AVFormatIDKey 定义了写入内容的音频格式,下面是常用格式:

  • kAudioFormatLinearPCM:将未压缩的音频流写入到文件中。保真度最高,文件也最大;
  • kAudioFormatMPEG4AAC(AAC) 或 kAudioFormat-AppleIMA4(Apple IMA4):文件显著缩小,还能保证高质量音频

采样率

AVSampleRateKey 定义了录音器的采样率,采样率定义了对输入的模拟音频信号每一秒的采样数。采样率越高,越能得到高质量的内容,不过文件相对越大。标准的采样率:8000、16000、22050、44100

通道数

AVNumberOfChannelsKey 定义记录音频内容的通道数。默认值 1 是单声道录制,2 是立体声录制。除非使用外部硬件录制,否则应该创建单声道录音。

6. 音频测量

AVAudioPlayer 和 AVAudioRecorder 中最实用的功能就是对音频进行测量。Audio Metering 可以读取音频的平均分贝和峰值分贝数据,并使用这些数据以可视化方式将声音大小呈现给用户。

首先在初始化 AVAudioPlayer 或 AVAudioRecorder 时应做出如下设置

self.player.meteringEnabled = YES;
self.recorder.meteringEnabled = YES;

点击音频播放或者音频录制,开始测量

- (void)startMeterTimer
{
    [self.link invalidate];
    self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateMeter)];
    self.link.frameInterval = 4;  // 时间间隔为刷新率的 1/4
    [self.link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}

- (void)stopMeterTimer
{
    [self.link invalidate];
    self.link = nil;
}

下面分别是音频播放情况下测量、音频录制情况下测量

- (void)updateMeter
{
    [self.player updateMeters];  // 刷新

    CGFloat num1 = [self.player averagePowerForChannel:0];
    CGFloat num2 = [self.player peakPowerForChannel:0];

    NSLog(@"平均分贝:%f, 峰值分贝:%f", num1, num2);
}
- (void)updateMeter
{
    [self.recorder updateMeters];  // 刷新

    CGFloat num1 = [self.recorder averagePowerForChannel:0];
    CGFloat num2 = [self.recorder peakPowerForChannel:0];

    NSLog(@"平均分贝:%f, 峰值分贝:%f", num1, num2);
}

上面方法都会返回用于表示声音分贝(dB)等级的浮点值,这个值的范围是 -160dB ~ 0dB

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: WPF(Windows Presentation Foundation)后台框架是一种用于创建Windows应用程序的技术。开发指的是在现有WPF后台框架的基础上进行进一步的定制和扩展。 在进行WPF后台框架的开发时,我们可以通过以下几个步骤进行: 首先,了解原有WPF后台框架的结构和功能。这包括学习WPF的基本概念和技术,了解其组件和布局,以及熟悉其数据绑定和命令机制等。这将帮助我们理解后台框架的架构和工作原理。 其次,确定开发的目标和需求。根据自己的业务需求,确定要添加、修改或删除哪些功能。这可能涉及界面的布局调整、新增控件或修改现有控件的行为,以及增强数据处理和业务逻辑等。 然后,进行具体的开发工作。可以使用Visual Studio等工具,利用C#或其他 .NET 相关语言进行编码。根据需求,可以使用WPF提供的控件、布局和样式等进行界面设计,同时也可以使用MVVM(Model-View-ViewModel)或其他设计模式来组织代码。 在开发过程中,要注意遵循良好的编码规范和设计原则,确保代码结构清晰、可扩展和易于维护。同时,也要进行必要的测试和调试工作,以确保开发后的框架在功能和性能上符合预期。 最后,进行部署和发布。根据具体情况,可能需要将开发后的框架打包成DLL或其他可执行文件,并将其集成到相应的应用程序中。 总之,WPF后台框架的开发可以帮助我们更好地满足业务需求,提供定制化的界面和功能。通过充分利用WPF的特性和工具,我们可以快速有效地进行开发,提升应用程序的用户体验和功能扩展性。 ### 回答2: WPF(Windows Presentation Foundation)是一个用于创建Windows桌面应用程序的框架。它提供了一种基于XAML(可扩展应用程序标记语言)的方式来构建用户界面。WPF的后台框架开发是指基于WPF框架进行进一步定制和扩展以满足特定需求的开发过程。 在WPF后台框架开发中,可以通过以下几个方面来实现定制和扩展: 1. 创建自定义控件:WPF提供了一种称为UserControl的机制,可以通过组合现有控件来创建自定义控件。这样可以根据特定需求来定制控件的外观和行为。 2. 扩展现有控件:WPF提供了一种称为CustomControl的机制,可以派生自现有的控件,并添加新的功能或修改现有功能。这样可以根据特定需求来扩展现有控件的能力。 3. 自定义数据绑定:WPF的数据绑定机制非常灵活,可以通过创建自定义的绑定来实现特定的数据绑定逻辑。这样可以根据特定需求来处理数据的绑定和转换。 4. 修改样式和主题:WPF的样式和主题机制使得修改应用程序的外观变得容易。可以通过创建自定义样式和主题文件来修改控件的外观,以及整个应用程序的风格。 5. 扩展路由事件和命令:WPF的路由事件和命令机制可以实现控件之间的通信和交互。可以通过扩展路由事件和命令来实现特定的交互逻辑。 通过以上这些方式,可以对WPF框架进行开发,定制和扩展应用程序的外观和行为,以满足特定需求。在进行WPF后台框架开发时,需要对WPF框架的原理和机制有一定的了解,并具备良好的编程能力和问题解决能力。 ### 回答3: WPF(Windows Presentation Foundation)是一种用于创建Windows桌面应用程序的框架,它提供了丰富的图形渲染、布局、数据绑定和动画等功能。WPF后台框架的开发指的是在已有的WPF框架基础上进行定制化的开发。 通过开发WPF后台框架,可以满足特定需求、增加特定功能以及提升用户体验。具体而言,开发可以包括以下几个方面的工作: 1. UI定制:可以根据实际需求调整现有的界面布局,修改颜色、字体、样式等来满足企业或个人的品牌需求。也可以根据用户习惯和操作习惯,优化现有的界面元素,提升用户的使用体验。 2. 功能增强:可以根据具体业务需求,增加新的功能模块或者扩展现有功能。比如,添加新的菜单项、按钮等来实现特定的操作,或者在现有功能基础上增加新的交互方式,让应用程序更加灵活且功能丰富。 3. 数据处理:可以在后台框架中进行数据的处理和计算。通过对数据进行加工、过滤、聚合等操作,可以实现更加复杂的数据展示和分析,提供更加精确和有用的信息给用户。 4. 业务逻辑实现:根据实际业务需求,在WPF后台框架中实现特定的业务逻辑。可以通过修改现有的事件处理、数据绑定等方式来实现相关功能,实现应用程序的核心业务需求。 总之,通过WPF后台框架的开发开发人员可以根据具体需求对框架进行灵活的定制和扩展,以满足不同应用场景的要求。这种方式可以提高开发效率,同时也能够使得应用程序更加符合用户的期望和需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值