iOS音频控制与处理全解析
1. 声音的远程控制
在iOS系统中,声音的远程控制分为硬件和软件两种方式。硬件远程控制例如带有按钮的耳塞;软件远程控制则体现在控制中心(图14 - 1)和锁屏界面(图14 - 2)的播放控制上。
若要让你的应用能够响应远程控制事件,需要满足一定条件。首先,应用的音频会话类别必须是“Solo Ambient”或“Playback”,并且应用要实际产生声音,这样应用的声音才会成为设备当前正在播放的声音。规则是,能够接收远程控制事件且最后实际产生声音的运行中的应用,将成为远程控制事件的目标。若没有其他应用符合此规则,远程控制事件的目标默认是音乐应用。
要配置应用以接收远程控制事件,需使用Media Player框架(
import MediaPlayer
)。通过
MPRemoteCommandCenter
的共享类方法获取共享命令中心,与远程命令中心进行交互,并配置其命令向应用发送消息,应用再做出相应响应。配置方式有两种:为命令指定目标 - 动作对,或直接传递一个函数。
以下是一个示例,假设应用播放音频,要响应远程暂停或恢复音频的命令。在视图控制器的
viewDidLoad
方法中进行如下配置:
let scc = MPRemoteCommandCenter.shared()
scc.playCommand.addTarget(self, action:#selector(doPlay))
scc.pauseCommand.addTarget(self, action:#selector(doPause))
scc.togglePlayPauseCommand.addTarget(self, action: #selector(doPlayPause))
同时,还需要实现
doPlay
、
doPause
和
doPlayPause
方法:
@objc func doPlayPause(_ event:MPRemoteCommandEvent) {
let p = self.player
if p.isPlaying { p.pause() } else { p.play() }
}
@objc func doPlay(_ event:MPRemoteCommandEvent) {
let p = self.player
p.play()
}
@objc func doPause(_ event:MPRemoteCommandEvent) {
let p = self.player
p.pause()
}
当应用播放声音后,就可以使用控制中心或耳塞开关暂停和恢复声音。不过,注册目标到远程命令中心后,当目标即将消失时,必须记得取消注册,否则远程命令中心可能会向不存在的目标发送远程控制事件,导致应用崩溃。若在视图控制器的
viewDidLoad
中注册,可在
deinit
方法中取消注册:
deinit {
let scc = MPRemoteCommandCenter.shared()
scc.togglePlayPauseCommand.removeTarget(self)
scc.playCommand.removeTarget(self)
scc.pauseCommand.removeTarget(self)
}
建立应用与软件远程控制界面的连接后,还可以优化该界面。例如,使用
MPNowPlayingInfoCenter
影响用户在远程控制界面看到的正在播放的信息。以下代码可让命令中心显示应用正在播放的声音文件的标题和艺术家:
let mpic = MPNowPlayingInfoCenter.default()
mpic.nowPlayingInfo = [
MPMediaItemPropertyArtist: "Matt Neuburg",
MPMediaItemPropertyTitle: "About Tiagol",
]
若要在软件远程控制界面显示进度视图,展示声音的持续时间和当前播放位置,需要告诉
MPNowPlayingInfoCenter
声音的持续时间。当开始播放时,可以这样设置:
let mpic = MPNowPlayingInfoCenter.default()
mpic.nowPlayingInfo = [
MPMediaItemPropertyArtist: "Matt Neuburg",
MPMediaItemPropertyTitle: "About Tiagol",
MPMediaItemPropertyPlaybackDuration: self.player.duration,
MPNowPlayingInfoPropertyElapsedPlaybackTime: 0,
MPNowPlayingInfoPropertyPlaybackRate: 1
]
由于
MPNowPlayingInfoCenter
不会实际监控声音的播放,只是盲目地推进当前播放位置的显示,所以当声音暂停或恢复时,需要更新
MPNowPlayingInfoCenter
。当声音暂停时,需要告知它不仅暂停了,还要告知当前的播放位置:
let p = self.player
let mpic = MPNowPlayingInfoCenter.default()
if var d = mpic.nowPlayingInfo {
d[MPNowPlayingInfoPropertyPlaybackRate] = 0
d[MPNowPlayingInfoPropertyElapsedPlaybackTime] = p.currentTime
mpic.nowPlayingInfo = d
}
若不希望用户能够滑动滑块来改变当前播放位置,可以使用
MPRemoteCommandCenter
禁用该功能:
let scc = MPRemoteCommandCenter.shared()
scc.changePlaybackPositionCommand.isEnabled = false
MPRemoteCommandCenter
还提供了许多其他可配置的命令,配置后相应的软件远程控制界面会激活。例如,为
likeCommand
指定目标 - 动作对后,控制中心会出现一个菜单按钮,用户点击该按钮可看到包含“喜欢”命令按钮的操作表。
2. 后台播放声音
默认情况下,当用户从应用切换到其他应用时,应用会被挂起并停止产生声音。但如果应用的主要功能是播放声音,可能希望应用在后台继续播放。要实现后台播放声音,应用必须完成以下操作:
- 在
Info.plist
中,必须包含“Required background modes”键(
UIBackgroundModes
),其数组值应包含“App plays audio or streams audio/video using AirPlay”(音频)。最简单的方法是通过目标编辑器的“Capabilities”选项卡中的“Background Modes”复选框“Audio, AirPlay, and Picture in Picture”来设置(图14 - 3)。
- 音频会话的策略必须是活动的,并且必须是“Playback”。
满足这些条件后,当用户点击主屏幕按钮关闭应用、切换到其他应用或锁定屏幕时,应用在前台播放的声音将继续播放,此时应用为了播放声音在后台运行。
应用在后台播放时,可能会被前台应用的音频会话策略中断。不过,注册了
AVAudioSession.interruptionNotification
后,应用可能在后台接收到此通知,并且如果
AVAudioSession.InterruptionType
为
.ended
,应用可能能够在后台继续播放。
远程控制事件在应用处于后台时仍然有效。实际上,即使应用在进入后台时没有主动播放声音,由于之前播放过声音,它仍可能成为远程控制目标。在这种情况下,如果用户触发远程控制事件,处于后台挂起状态的应用将被唤醒(仍在后台)以接收远程控制事件,然后开始播放声音。如果应用是可混合的(
.mixWithOthers
),即使之前没有播放,也可能在后台开始播放。
当应用能够在后台播放声音时,会产生一些有趣的副产品。例如,在播放声音时,一个在前台创建并调度的
Timer
可以在后台触发,除非应用当前没有播放任何声音。此外,通常应用委托可能永远不会收到
applicationWillTerminate(_:)
消息,因为应用终止时已经被挂起,无法接收任何事件。但正在后台播放声音的应用显然没有被挂起,如果在后台播放声音时被终止,它将收到
applicationWillTerminate(_:)
消息。
3. AVAudioEngine简介
AVAudioEngine
模仿调音台设计,可以实时构建和操作一个声音产生对象的图形,改变它们的相对音量和其他属性,将它们混合成一个声音。以下是一些关键类:
| 类名 | 描述 |
| ---- | ---- |
|
AVAudioEngine
| 整体引擎对象,代表所有操作发生的环境。通常一次只创建和保留一个,可以用新的引擎替换旧的以重新开始。主要工作是连接和断开节点(
AVAudioNode
),以及启动和停止声音的产生。 |
|
AVAudioNode
| 抽象类,包含各种用于产生、处理、混合和接收声音的对象类型。音频节点只有附加到音频引擎后才有用,有输入和输出,音频引擎可以将一个节点的输出连接到另一个节点的输入。还可以在节点上设置一个tap,将节点的声音数据复制到缓冲区中,用于分析、监控或保存到文件。 |
|
AVAudioMixerNode
| 具有输出音量的节点,将其输入混合为一个输出。
AVAudioEngine
的内置
mixerNode
就是一个
AVAudioMixerNode
。 |
|
AVAudioIONode
| 连接到系统(设备)自身输入(
AVAudioInputNode
)或输出(
AVAudioOutputNode
)的节点。
AVAudioEngine
的内置
inputNode
和
outputNode
是
AVAudioIONodes
。 |
|
AVAudioPlayerNode
| 产生声音的节点,类似于
AVAudioPlayer
,可以从文件或缓冲区播放。 |
|
AVAudioEnvironmentNode
| 对声音源进行三维空间控制(适用于游戏),使用它会激活一系列额外的
AVAudioNode
属性。 |
|
AVAudioUnit
| 对输入应用特殊效果后再传递到输出的节点。内置子类包括
AVAudioUnitTimePitch
、
AVAudioUnitVarispeed
、
AVAudioUnitDelay
、
AVAudioUnitDistortion
、
AVAudioUnitEQ
和
AVAudioUnitReverb
等。 |
以下是使用
AVAudioEngine
的一些示例:
-
简单播放文件
:
let player = AVAudioPlayerNode()
let url = Bundle.main.url(forResource:"aboutTiagol", withExtension:"m4a")!
let f = try! AVAudioFile(forReading: url)
let mixer = self.engine.mainMixerNode
self.engine.attach(player)
self.engine.connect(player, to: mixer, format: f.processingFormat)
player.scheduleFile(f, at: nil) { [unowned self] in
delay(0.1) {
if self.engine.isRunning {
self.engine.stop()
}
}
}
self.engine.prepare()
try! self.engine.start()
player.play()
- 同时播放两个声音并应用效果 :
// first sound
let player = AVAudioPlayerNode()
let url = Bundle.main.url(forResource:"aboutTiagol", withExtension:"m4a")!
let f = try! AVAudioFile(forReading: url)
self.engine.attach(player)
// add some effect nodes to the chain
let effect = AVAudioUnitTimePitch()
effect.rate = 0.9
effect.pitch = -300
self.engine.attach(effect)
self.engine.connect(player, to: effect, format: f.processingFormat)
let effect2 = AVAudioUnitReverb()
effect2.loadFactoryPreset(.cathedral)
effect2.wetDryMix = 40
self.engine.attach(effect2)
self.engine.connect(effect, to: effect2, format: f.processingFormat)
// patch last node into engine mixer and start playing first sound
let mixer = self.engine.mainMixerNode
self.engine.connect(effect2, to: mixer, format: f.processingFormat)
player.scheduleFile(f, at: nil) {
delay(0.1) {
if self.engine.isRunning {
self.engine.stop()
}
}
}
self.engine.prepare()
try! self.engine.start()
player.play()
// second sound; loop it
let url2 = Bundle.main.url(forResource:"Hooded", withExtension: "mp3")!
let f2 = try! AVAudioFile(forReading: url2)
let buffer = AVAudioPCMBuffer(
pcmFormat: f2.processingFormat, frameCapacity: UInt32(f2.length))
try! f2.read(into:buffer!)
let player2 = AVAudioPlayerNode()
self.engine.attach(player2)
self.engine.connect(player2, to: mixer, format: f2.processingFormat)
player2.scheduleBuffer(buffer!, at: nil, options: .loops)
// mix down a little, start playing second sound
player.pan = -0.5
player2.volume = 0.5
player2.pan = 0.5
player2.play()
- 将节点输出拆分到多个节点 :
let effect = AVAudioUnitDelay()
effect.delayTime = 0.4
effect.feedback = 0
self.engine.attach(effect)
let effect2 = AVAudioUnitReverb()
effect2.loadFactoryPreset(.cathedral)
effect2.wetDryMix = 40
self.engine.attach(effect2)
let mixer = self.engine.mainMixerNode
// patch player node to _both_ effect nodes _and_ the mixer
let cons = [
AVAudioConnectionPoint(node: effect, bus: 0),
AVAudioConnectionPoint(node: effect2, bus: 0),
AVAudioConnectionPoint(node: mixer, bus: 1),
]
self.engine.connect(player, to: cons,
fromBus: 0, format: f.processingFormat)
// patch both effect nodes into the mixer
self.engine.connect(effect, to: mixer, format: f.processingFormat)
self.engine.connect(effect2, to: mixer, format: f.processingFormat)
- 将声音处理到文件中 :
let url = Bundle.main.url(forResource:"Hooded", withExtension: "mp3")!
let f = try! AVAudioFile(forReading: url)
let player = AVAudioPlayerNode()
self.engine.attach(player)
// patch the player into the effect
let effect = AVAudioUnitReverb()
effect.loadFactoryPreset(.cathedral)
effect.wetDryMix = 40
self.engine.attach(effect)
self.engine.connect(player, to: effect, format: f.processingFormat)
let mixer = self.engine.mainMixerNode
self.engine.connect(effect, to: mixer, format: f.processingFormat)
let fm = FileManager.default
let doc = try! fm.url(for:.documentDirectory, in: .userDomainMask,
appropriateFor: nil, create: true)
let outurl = doc.appendingPathComponent("myfile.aac", isDirectory:false)
let outfile = try! AVAudioFile(forWriting: outurl, settings: [
AVFormatIDKey : kAudioFormatMPEG4AAC,
AVNumberOfChannelsKey : 1,
AVSampleRateKey : 22050,
])
var done = false
player.scheduleFile(f, at: nil)
let sz : UInt32 = 4096
try! self.engine.enableManualRenderingMode(.offline,
format: f.processingFormat, maximumFrameCount: sz)
self.engine.prepare()
try! self.engine.start()
player.play()
let outbuf = AVAudioPCMBuffer(
pcmFormat: f.processingFormat, frameCapacity: sz)!
var rest : Int64 { return f.length - self.engine.manualRenderingSampleTime }
while rest > 0 {
let ct = min(outbuf.frameCapacity, UInt32(rest))
let stat = try! self.engine.renderOffline(ct, to: outbuf)
if stat == .success {
try! outfile.write(from: outbuf)
}
}
不过,处理结果可能存在一个问题:由于输入文件耗尽后就停止向输出文件写入,混响效果在输出末尾没有机会逐渐消失。可以通过在
rest
的大小上任意增加几秒,或者检查
outbuf
的内容并在读取输入文件后继续循环,直到声音数据的振幅低于某个阈值来解决这个问题。
以上就是关于iOS音频控制与处理的详细介绍,希望能帮助开发者更好地实现音频相关功能。
iOS音频控制与处理全解析
4. AVAudioEngine操作流程总结
为了更清晰地展示使用
AVAudioEngine
进行不同操作的流程,下面通过mermaid流程图和步骤总结来呈现。
4.1 简单播放文件流程
graph LR
A[创建AVAudioPlayerNode] --> B[获取音频文件URL]
B --> C[创建AVAudioFile]
C --> D[获取引擎的主混音器节点]
D --> E[将AVAudioPlayerNode附加到引擎]
E --> F[连接AVAudioPlayerNode到混音器节点]
F --> G[调度音频文件播放并设置完成回调]
G --> H[准备引擎]
H --> I[启动引擎]
I --> J[开始播放音频]
操作步骤如下:
1. 创建
AVAudioPlayerNode
对象。
2. 获取音频文件的URL。
3. 使用URL创建
AVAudioFile
对象。
4. 获取引擎的主混音器节点。
5. 将
AVAudioPlayerNode
附加到引擎。
6. 连接
AVAudioPlayerNode
到混音器节点。
7. 调度音频文件播放,并设置完成回调函数,在回调中停止引擎。
8. 准备引擎。
9. 启动引擎。
10. 开始播放音频。
代码示例:
let player = AVAudioPlayerNode()
let url = Bundle.main.url(forResource:"aboutTiagol", withExtension:"m4a")!
let f = try! AVAudioFile(forReading: url)
let mixer = self.engine.mainMixerNode
self.engine.attach(player)
self.engine.connect(player, to: mixer, format: f.processingFormat)
player.scheduleFile(f, at: nil) { [unowned self] in
delay(0.1) {
if self.engine.isRunning {
self.engine.stop()
}
}
}
self.engine.prepare()
try! self.engine.start()
player.play()
4.2 同时播放两个声音并应用效果流程
graph LR
A1[创建第一个AVAudioPlayerNode] --> B1[获取第一个音频文件URL]
B1 --> C1[创建第一个AVAudioFile]
C1 --> D1[将第一个AVAudioPlayerNode附加到引擎]
D1 --> E1[创建时间 - 音高效果节点并设置参数]
E1 --> F1[将时间 - 音高效果节点附加到引擎]
F1 --> G1[连接第一个AVAudioPlayerNode到时间 - 音高效果节点]
G1 --> H1[创建混响效果节点并设置参数]
H1 --> I1[将混响效果节点附加到引擎]
I1 --> J1[连接时间 - 音高效果节点到混响效果节点]
J1 --> K1[连接混响效果节点到混音器节点]
K1 --> L1[调度第一个音频文件播放并设置完成回调]
L1 --> M1[准备引擎]
M1 --> N1[启动引擎]
N1 --> O1[开始播放第一个音频]
A2[创建第二个AVAudioPlayerNode] --> B2[获取第二个音频文件URL]
B2 --> C2[创建第二个AVAudioFile]
C2 --> D2[创建音频缓冲区并读取文件内容]
D2 --> E2[将第二个AVAudioPlayerNode附加到引擎]
E2 --> F2[连接第二个AVAudioPlayerNode到混音器节点]
F2 --> G2[调度音频缓冲区循环播放]
G2 --> H2[设置第一个音频的声道和音量]
H2 --> I2[设置第二个音频的声道和音量]
I2 --> J2[开始播放第二个音频]
操作步骤如下:
1. 第一个音频处理:
1. 创建
AVAudioPlayerNode
对象。
2. 获取第一个音频文件的URL。
3. 使用URL创建
AVAudioFile
对象。
4. 将
AVAudioPlayerNode
附加到引擎。
5. 创建
AVAudioUnitTimePitch
效果节点并设置参数。
6. 将效果节点附加到引擎。
7. 连接
AVAudioPlayerNode
到效果节点。
8. 创建
AVAudioUnitReverb
效果节点并设置参数。
9. 将混响效果节点附加到引擎。
10. 连接时间 - 音高效果节点到混响效果节点。
11. 连接混响效果节点到混音器节点。
12. 调度第一个音频文件播放并设置完成回调。
13. 准备引擎。
14. 启动引擎。
15. 开始播放第一个音频。
2. 第二个音频处理:
1. 创建第二个
AVAudioPlayerNode
对象。
2. 获取第二个音频文件的URL。
3. 使用URL创建第二个
AVAudioFile
对象。
4. 创建音频缓冲区并将第二个音频文件内容读取到缓冲区。
5. 将第二个
AVAudioPlayerNode
附加到引擎。
6. 连接第二个
AVAudioPlayerNode
到混音器节点。
7. 调度音频缓冲区循环播放。
8. 设置第一个音频的声道和音量。
9. 设置第二个音频的声道和音量。
10. 开始播放第二个音频。
代码示例:
// first sound
let player = AVAudioPlayerNode()
let url = Bundle.main.url(forResource:"aboutTiagol", withExtension:"m4a")!
let f = try! AVAudioFile(forReading: url)
self.engine.attach(player)
// add some effect nodes to the chain
let effect = AVAudioUnitTimePitch()
effect.rate = 0.9
effect.pitch = -300
self.engine.attach(effect)
self.engine.connect(player, to: effect, format: f.processingFormat)
let effect2 = AVAudioUnitReverb()
effect2.loadFactoryPreset(.cathedral)
effect2.wetDryMix = 40
self.engine.attach(effect2)
self.engine.connect(effect, to: effect2, format: f.processingFormat)
// patch last node into engine mixer and start playing first sound
let mixer = self.engine.mainMixerNode
self.engine.connect(effect2, to: mixer, format: f.processingFormat)
player.scheduleFile(f, at: nil) {
delay(0.1) {
if self.engine.isRunning {
self.engine.stop()
}
}
}
self.engine.prepare()
try! self.engine.start()
player.play()
// second sound; loop it
let url2 = Bundle.main.url(forResource:"Hooded", withExtension: "mp3")!
let f2 = try! AVAudioFile(forReading: url2)
let buffer = AVAudioPCMBuffer(
pcmFormat: f2.processingFormat, frameCapacity: UInt32(f2.length))
try! f2.read(into:buffer!)
let player2 = AVAudioPlayerNode()
self.engine.attach(player2)
self.engine.connect(player2, to: mixer, format: f2.processingFormat)
player2.scheduleBuffer(buffer!, at: nil, options: .loops)
// mix down a little, start playing second sound
player.pan = -0.5
player2.volume = 0.5
player2.pan = 0.5
player2.play()
4.3 将节点输出拆分到多个节点流程
graph LR
A[创建延迟效果节点并设置参数] --> B[将延迟效果节点附加到引擎]
B --> C[创建混响效果节点并设置参数]
C --> D[将混响效果节点附加到引擎]
D --> E[获取引擎的主混音器节点]
E --> F[创建连接点数组]
F --> G[连接AVAudioPlayerNode到多个节点]
G --> H[连接延迟效果节点到混音器节点]
H --> I[连接混响效果节点到混音器节点]
操作步骤如下:
1. 创建
AVAudioUnitDelay
效果节点并设置参数。
2. 将延迟效果节点附加到引擎。
3. 创建
AVAudioUnitReverb
效果节点并设置参数。
4. 将混响效果节点附加到引擎。
5. 获取引擎的主混音器节点。
6. 创建
AVAudioConnectionPoint
数组。
7. 连接
AVAudioPlayerNode
到多个节点(延迟效果节点、混响效果节点和混音器节点)。
8. 连接延迟效果节点到混音器节点。
9. 连接混响效果节点到混音器节点。
代码示例:
let effect = AVAudioUnitDelay()
effect.delayTime = 0.4
effect.feedback = 0
self.engine.attach(effect)
let effect2 = AVAudioUnitReverb()
effect2.loadFactoryPreset(.cathedral)
effect2.wetDryMix = 40
self.engine.attach(effect2)
let mixer = self.engine.mainMixerNode
// patch player node to _both_ effect nodes _and_ the mixer
let cons = [
AVAudioConnectionPoint(node: effect, bus: 0),
AVAudioConnectionPoint(node: effect2, bus: 0),
AVAudioConnectionPoint(node: mixer, bus: 1),
]
self.engine.connect(player, to: cons,
fromBus: 0, format: f.processingFormat)
// patch both effect nodes into the mixer
self.engine.connect(effect, to: mixer, format: f.processingFormat)
self.engine.connect(effect2, to: mixer, format: f.processingFormat)
4.4 将声音处理到文件中流程
graph LR
A[获取音频文件URL] --> B[创建AVAudioFile]
B --> C[创建AVAudioPlayerNode]
C --> D[将AVAudioPlayerNode附加到引擎]
D --> E[创建混响效果节点并设置参数]
E --> F[将混响效果节点附加到引擎]
F --> G[连接AVAudioPlayerNode到混响效果节点]
G --> H[连接混响效果节点到混音器节点]
H --> I[创建输出文件URL和AVAudioFile]
I --> J[调度音频文件播放]
J --> K[启用离线渲染模式]
K --> L[准备引擎]
L --> M[启动引擎]
M --> N[开始播放音频]
N --> O[创建音频缓冲区]
O --> P[循环读取音频数据并写入输出文件]
操作步骤如下:
1. 获取音频文件的URL。
2. 使用URL创建
AVAudioFile
对象。
3. 创建
AVAudioPlayerNode
对象。
4. 将
AVAudioPlayerNode
附加到引擎。
5. 创建
AVAudioUnitReverb
效果节点并设置参数。
6. 将混响效果节点附加到引擎。
7. 连接
AVAudioPlayerNode
到混响效果节点。
8. 连接混响效果节点到混音器节点。
9. 创建输出文件的URL和
AVAudioFile
对象。
10. 调度音频文件播放。
11. 启用引擎的离线渲染模式。
12. 准备引擎。
13. 启动引擎。
14. 开始播放音频。
15. 创建音频缓冲区。
16. 循环读取音频数据,将其写入输出文件,直到输入文件数据耗尽。
代码示例:
let url = Bundle.main.url(forResource:"Hooded", withExtension: "mp3")!
let f = try! AVAudioFile(forReading: url)
let player = AVAudioPlayerNode()
self.engine.attach(player)
// patch the player into the effect
let effect = AVAudioUnitReverb()
effect.loadFactoryPreset(.cathedral)
effect.wetDryMix = 40
self.engine.attach(effect)
self.engine.connect(player, to: effect, format: f.processingFormat)
let mixer = self.engine.mainMixerNode
self.engine.connect(effect, to: mixer, format: f.processingFormat)
let fm = FileManager.default
let doc = try! fm.url(for:.documentDirectory, in: .userDomainMask,
appropriateFor: nil, create: true)
let outurl = doc.appendingPathComponent("myfile.aac", isDirectory:false)
let outfile = try! AVAudioFile(forWriting: outurl, settings: [
AVFormatIDKey : kAudioFormatMPEG4AAC,
AVNumberOfChannelsKey : 1,
AVSampleRateKey : 22050,
])
var done = false
player.scheduleFile(f, at: nil)
let sz : UInt32 = 4096
try! self.engine.enableManualRenderingMode(.offline,
format: f.processingFormat, maximumFrameCount: sz)
self.engine.prepare()
try! self.engine.start()
player.play()
let outbuf = AVAudioPCMBuffer(
pcmFormat: f.processingFormat, frameCapacity: sz)!
var rest : Int64 { return f.length - self.engine.manualRenderingSampleTime }
while rest > 0 {
let ct = min(outbuf.frameCapacity, UInt32(rest))
let stat = try! self.engine.renderOffline(ct, to: outbuf)
if stat == .success {
try! outfile.write(from: outbuf)
}
}
5. 总结
通过以上介绍,我们详细了解了iOS系统中声音的远程控制、后台播放声音以及
AVAudioEngine
的使用方法。在实际开发中,开发者可以根据具体需求选择合适的音频处理方式。
- 声音远程控制 :可以让用户通过控制中心、锁屏界面或耳塞按钮方便地控制应用的音频播放,增强用户体验。
- 后台播放声音 :满足了一些音频应用在用户切换应用或锁定屏幕时继续播放的需求。
- AVAudioEngine :提供了强大的音频处理功能,如实时混音、添加效果、音频文件处理等,为开发者实现复杂的音频功能提供了可能。
希望这些内容能帮助开发者更好地掌握iOS音频控制与处理技术,开发出更优质的音频应用。
超级会员免费看
5807

被折叠的 条评论
为什么被折叠?



