我一直在寻找Swift文档以保存AVAudioEngine的音频输出,但我找不到任何有用的提示.
有什么建议吗?
解决方案 我找到了解决方法,这要归功于matt的答案.这里有一个示例代码,说明如何在通过AVAudioEngine传递音频之后保存音频(我认为在技术上它是之前的)
newAudio = AVAudioFile(forWriting: newAudio.url, settings: nil, error: NSErrorPointer())
//Your new file on which you want to save some changed audio, and prepared to be bufferd in some new data...
var audioPlayerNode = AVAudioPlayerNode() //or your Time pitch unit if pitch changed
//Now install a Tap on the output bus to "record" the transformed file on a our newAudio file.
audioPlayerNode.installTapOnBus(0, bufferSize: (AVAudioFrameCount(audioPlayer.duration)), format: opffb){
(buffer: AVAudioPCMBuffer!, time: AVAudioTime!) in
if (self.newAudio.length) …Run Code Online (Sandbox Code Playgroud) 此方法处理来自AVAudioEngine的输入节点上的installTap的回调.我已经确认我正在获取mono float32/48000hz缓冲区数据,我想将其转换为mono int16/16000hz.
var converter: AVAudioConverter? = nil
var convertBuffer: AVAudioPCMBuffer? = nil
let targetFormat = AVAudioFormat(commonFormat: AVAudioCommonFormat.pcmFormatInt16, sampleRate: 16000, channels: 1, interleaved: false)
func recordCallback(buffer: AVAudioPCMBuffer, time: AVAudioTime) {
if converter == nil {
convertBuffer = AVAudioPCMBuffer(pcmFormat: targetFormat, frameCapacity: buffer.frameCapacity)
convertBuffer?.frameLength = convertBuffer!.frameCapacity
converter = AVAudioConverter(from: buffer.format, to: convertBuffer!.format)
converter?.sampleRateConverterAlgorithm = AVSampleRateConverterAlgorithm_Normal
converter?.sampleRateConverterQuality = .max
}
guard let convertBuffer = convertBuffer else { return }
log.info("Converter: \(self.converter!)")
log.info("Converter buffer: \(self.convertBuffer!)")
log.info("Converter buffer format: \(self.convertBuffer!.format)")
log.info("Source buffer: \(buffer)")
log.info("Source buffer …Run Code Online (Sandbox Code Playgroud) 我正在为 iOS 应用程序创建这个简单的录音机和编辑器界面:
音频被记录到用于创建波形的浮点数组中。录制后,我将浮动数据复制到一个AVAudioPCMBuffer用于播放的AVAudioPlayerNode. 我能够从一开始就播放缓冲区,但我不知道如何只播放缓冲区的一部分。该scheduleSegment功能仅适用于文件。
我正在为 iOS 制作一个基本的音乐应用程序,按下音符会播放相应的声音。我正在尝试让存储在缓冲区中的多个声音以最小的延迟同时播放。但是,我每次只能播放一种声音。
我最初使用多个 AVAudioPlayer 对象设置声音,为每个播放器分配一个声音。虽然它确实同时播放多个声音,但它似乎无法同时启动两个声音(似乎它会在第一个声音启动后稍微延迟第二个声音)。此外,如果我以非常快的速度按下音符,引擎似乎无法跟上,并且在我按下后面的音符后,后面的声音就会很好地开始。
我正在尝试解决这个问题,从我所做的研究来看,使用 AVAudioEngine 播放声音似乎是最好的方法,我可以在缓冲区数组中设置声音,然后让它们播放从这些缓冲区中。
class ViewController: UIViewController
{
// Main Audio Engine and it's corresponding mixer
var audioEngine: AVAudioEngine = AVAudioEngine()
var mainMixer = AVAudioMixerNode()
// One AVAudioPlayerNode per note
var audioFilePlayer: [AVAudioPlayerNode] = Array(repeating: AVAudioPlayerNode(), count: 7)
// Array of filepaths
let noteFilePath: [String] = [
Bundle.main.path(forResource: "note1", ofType: "wav")!,
Bundle.main.path(forResource: "note2", ofType: "wav")!,
Bundle.main.path(forResource: "note3", ofType: "wav")!]
// Array to store the note URLs
var noteFileURL = [URL]()
// One audio file per …Run Code Online (Sandbox Code Playgroud) 每次我停止/暂停音频引擎(使用AVAudioEnginesstop()或pause())时,状态栏上都会出现一个红色的麦克风图标一秒钟。
一段时间后,这开始很烦人,每次我暂停/停止引擎时,顶部都会出现红色。
在 iOS 13 中启用音频背景模式功能时会发生这种情况。
要重现这一点,请创建一个新的单视图项目,并将其添加到视图控制器:
private let engine = AVAudioEngine()
Run Code Online (Sandbox Code Playgroud)
而在viewDidAppear:
engine.connect(
engine.inputNode,
to: engine.mainMixerNode,
format: engine.inputNode.inputFormat(forBus: 0)
)
try? engine.start()
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
self.engine.pause() // or self.engine.stop()
}
Run Code Online (Sandbox Code Playgroud)
记住要启用音频背景模式,添加NSMicrophoneUsageDescription的Info.plist,并在iOS的13设备上运行。接受麦克风权限并重新运行应用程序。您将看到红色麦克风图标闪烁一次。
有没有办法摆脱这个?
我到处寻找,但找不到解决这个问题的方法。我正在寻找一种方法来更改将AVAudioEngine在 macOS 上使用的输入/输出设备。
当简单地播放音频文件时,以下内容按预期工作:
var outputDeviceID:AudioDeviceID = xxx
let result:OSStatus = AudioUnitSetProperty(outputUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &outputDeviceID, UInt32(MemoryLayout<AudioObjectPropertyAddress>.size))
if result != 0 {
print("error setting output device \(result)")
return
}
Run Code Online (Sandbox Code Playgroud)
但是,如果我初始化音频输入(使用let input = engine.inputNode),那么当我尝试启动引擎时,我会收到错误消息:
AVAEInternal.h:88 必需条件为 false: [AVAudioEngine.mm:1055:CheckCanPerformIO: (canPerformIO)]
我知道我的播放代码没问题,因为如果我避免更改输出设备,那么我可以听到麦克风和音频文件,如果我更改输出设备但不初始化文件,则文件会播放inputNode到指定的目的地。
除此之外,我一直在尝试更改输入设备,我从各个地方了解到以下应该执行此操作:
let result1:OSStatus = AudioUnitSetProperty(inputUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Output, 0, &inputDeviceID, UInt32(MemoryLayout<AudioObjectPropertyAddress>.size))
if result1 != 0 {
print("failed with error \(result1)")
return
}
Run Code Online (Sandbox Code Playgroud)
但是,这不起作用 - 在大多数情况下它会抛出错误(10853),尽管如果我选择同时具有输入和输出的声卡,它会成功 - 看来当我尝试设置输出或输入节点时它实际上是为两者设置设备。
我认为这意味着一个AVAudioEngine实例只能处理一个设备,但是它很高兴使用默认设备(麦克风和扬声器/耳机),所以我相信这不是问题。查看一些解决方案,我看到网上人们只是简单地更改默认输入,但这并不是一个非常好的解决方案。
有谁知道这是否可能?
值得注意的是,这kAudioOutputUnitProperty_CurrentDevice是唯一可用的属性,没有等效的kAudioInputUnitProperty_CurrentDevice键,因为据我了解, …
我有以下代码块应该创建一个产生正弦波的AVAudioNode,但它在标有 - [AUAudioUnitV2Bridge setOutputProvider:]的行上崩溃:发送到实例的无法识别的选择器.
什么想法可能是错的?
// TEST
AudioComponentDescription mixerDesc;
mixerDesc.componentType = kAudioUnitType_Generator;
mixerDesc.componentSubType = kAudioUnitSubType_ScheduledSoundPlayer;
mixerDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
mixerDesc.componentFlags = 0;
mixerDesc.componentFlagsMask = 0;
[AVAudioUnit instantiateWithComponentDescription:mixerDesc options:kAudioComponentInstantiation_LoadInProcess completionHandler:^(__kindof AVAudioUnit * _Nullable audioUnit, NSError * _Nullable error) {
NSLog(@"here");
// Crashes here
audioUnit.AUAudioUnit.outputProvider = ^AUAudioUnitStatus(AudioUnitRenderActionFlags *actionFlags, const AudioTimeStamp *timestamp, AUAudioFrameCount frameCount, NSInteger inputBusNumber, AudioBufferList *inputData)
{
const double amplitude = 0.2;
static double theta = 0.0;
double theta_increment = 2.0 * M_PI * 880.0 / 44100.0;
const int channel …Run Code Online (Sandbox Code Playgroud) 我正在为Mac编写我的第一个音频应用程序,它加载外部音频单元并使用它通过AVAudioEngine实例播放声音,我一直看到这个警告:
警告:140:此应用程序或其使用的库正在使用已弃用的Carbon Component Manager来托管Audio Units.将来的版本中将删除对此的支持.此外,这使主机与版本3音频单元不兼容.请转到AudioComponent.h中的API.
我已经从使用AVAudioUnitComponents转换到AudioComponents(现在通过这个api访问),我希望能解决这个问题,但是当我调用start()我的引擎时,我仍然会看到这个警告.
任何想法在这里出了什么问题?据我所知,我不再使用已弃用的API.AVAudioEngine是否有可能在引擎盖下使用已弃用的API?
这是我正在使用的代码的片段.我正在调用selectInstrument我使用AudioComponents API检索的描述.
public func selectInstrument(withDescription description: AudioComponentDescription, callback: @escaping SelectInstrumentCallback) {
AVAudioUnit.instantiate(with: description, options: []) { avAudioUnit, error in
guard let unit = avAudioUnit else {
callback(nil)
return
}
self.disconnectCurrent()
self.connect(unit: unit)
unit.auAudioUnit.requestViewController { viewController in
callback(viewController)
}
}
}
private func disconnectCurrent() {
guard let current = currentInstrument else { return }
self.engine.disconnectNodeInput(engine.mainMixerNode)
self.engine.detach(current)
self.currentInstrument = nil
self.engine.stop()
}
private func connect(unit: AVAudioUnit) {
let hardwareFormat …Run Code Online (Sandbox Code Playgroud) 使用AVAudioUnitSampler时,我在iPhone(iOS 11)上遇到了一个奇怪的问题.
假设我有一个用钢琴声音初始化的AVAudioUnitSampler.因此,每次连接或断开耳机时,我都会听到钢琴声音加上正弦波音,声音越大,连接/断开耳机的次数越多.
因此,对我而言,每次耳机插入/拔出时都会感觉到,新的音频单元采样器将内部连接到声音输出(并且,由于它是未初始化的,因此仅产生正弦波音调).
以下类已经显示了该问题.请注意,我正在使用AudioKit来处理MIDI信号并触发采样器(虽然在这一端,一切似乎工作正常,即.startNote()并stopNote()正确调用):
class MidiController: NSObject, AKMIDIListener {
var midi = AKMIDI()
var engine = AVAudioEngine()
var samplerUnit = AVAudioUnitSampler()
override public init() {
super.init()
NotificationCenter.default.addObserver(
self,
selector: #selector(handleRouteChange),
name: .AVAudioSessionRouteChange,
object: nil)
midi.openInput()
midi.addListener(self)
engine.attach(samplerUnit)
engine.connect(samplerUnit, to: engine.outputNode)
startEngine()
}
func startEngine() {
if (!engine.isRunning) {
do {
try self.engine.start()
} catch {
fatalError("couldn't start engine.")
}
}
}
@objc func handleRouteChange(notification: NSNotification) {
let deadlineTime = DispatchTime.now() + …Run Code Online (Sandbox Code Playgroud) 我具有以下用于在macOS上获取实时麦克风输入的测试代码:
import AVFoundation // for AVAudioEngine
class Mic
{
public let audioEngine = AVAudioEngine()
func startRecording() throws
{
print("- - -")
let inputNode = audioEngine.inputNode
print("- - -")
if inputNode.inputFormat(forBus: 0).sampleRate == 0 {
exit(0);
}
let recordingFormat = inputNode.outputFormat(forBus: 0)
inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer: AVAudioPCMBuffer, when: AVAudioTime) in
print( "YES! Got some samples!")
}
audioEngine.prepare()
try audioEngine.start()
}
func stopRecording()
{
audioEngine.stop()
}
}
Run Code Online (Sandbox Code Playgroud)
我得到输出:
2019-07-22 16:26:36.773244 + 0300 realtime_mic [8111:540360] [plugin] AddInstanceForFactory:没有为ID F8BB1C28-BAE8-11D6-9C31-00039315CD46注册的工厂
2019-07-22 …
avaudioengine ×10
swift ×6
ios ×4
avfoundation ×3
core-audio ×3
macos ×3
audiounit ×2
audiokit ×1
avasset ×1
avaudiofile ×1
ios13 ×1
iphone ×1