包含适用于 AUv3 应用程序扩展的有用代码的 Swift 包。目前此包中有四个产品
DSPHeaders 产品中的 EventProcessor 模板是所有 AUv3 过滤器内核的基础。 您只需要定义自己的 doRendering
方法来执行采样渲染。 几乎所有其他事情都为您处理。 您可以根据需要定义其他方法,但只有 doRendering
方法是强制性的。
EventProcessor
没有使用 C++ 虚函数来分派到派生类中的功能,而是依赖于将派生类作为模板参数给出。 这种设置被称为 “奇特的递归模板模式” (CRTP)。 该模板还使用现代 C++ 技术来检测您的类中是否存在方法,并且编译器仅在这些方法可用时才生成调用它们的代码。
AUv3Template 项目展示了所有这些,并且它被设置为开箱即用地提供一个可用的 macOS 和一个 iOS 应用程序和应用程序扩展,只需运行一个命令行脚本即可。
以下图像显示了我的 SimplyFlange 应用程序在横向 iPhone SE 模拟器中运行。 此应用程序基于 AUv3Template,因此使用 AUv3Support 包。
顶部的控件来自 AUv3Support-iOS 包,作为其提供的宿主应用程序基础结构的一部分。 在那里,您会发现
宿主应用程序支持为扩展创建和管理用户预设。 它们应该以与其他宿主应用程序(例如 GarageBand、Logic、Cubasis、AUM)中相同的方式运行。 您可以创建自己的预设。 当用户预设处于活动状态时,您可以
以下图像显示了同一 AUv3 插件的 macOS 版本。 这次,宿主控件位于应用程序的标题栏中。
此存储库也被我的其他 AUv3 项目使用
最新版本现在使用 Swift 6 和完整的并发检查进行构建。
所有代码都是我自己在开发 AUv3 应用程序扩展的几年中编写的。 在 ConstMath 中有一系列例程,它们为正弦、自然对数和指数函数提供编译时值。 这些用于在编译时生成一些查找表。 执行此操作的函数取自 Lakshay Garg 的 compile_time(无特定许可证)存储库和 Keith O'Hara 的 GCEM(Apache 许可证)存储库。 我从 compile_time
开始,但我从 GCEM
中提取了自然对数函数。 请注意,这些编译时方法的使用仅适用于非常有限的一组用例,所有这些用例在精度方面要求都不高。
在 AUv3-Support 产品中,您会找到各种类和扩展,以便在使用 AUv3 组件时更容易
BooleanParameterEditor
,它与 UISwitch/NSSwitch 控件一起使用,并且有一个 FloatParameterEditor
,它与任何可以报告浮点值以及该值可能具有的最小/最大范围的控件一起使用。SimplePlayEngine
播放的音频文件。 在演示过滤器时很有用。包含简单的 AUv3 宿主加载您的 AUv3 组件、显示其 UI 控件并允许您通过它播放音频所需的大部分内容。 使其工作的基本原理是
HostViewConfig
,其中包含特定于您的 AUv3 组件的值,然后将其传递给 Shared.embedHostView
静态函数以及您的应用程序的主 UIViewController
实例。AppDelegate.swift
文件以继承自此包中找到的 AppDelegate。 类似以下的代码就很好import UIKit
import AUv3Support
import AUv3Support_iOS
import os.log
@main
final class AppDelegate: AUv3Support_iOS.AppDelegate {
// NOTE: this special form sets the subsystem name and must run before any other logger calls.
private let log: OSLog = Shared.logger(Bundle.main.auBaseName + "Host", "AppDelegate")
}
MainViewController.swift
以执行以下操作import AUv3Support
import AUv3Support_iOS
import CoreAudioKit
import UIKit
final class MainViewController: UIViewController {
private var hostViewController: HostViewController!
override func viewDidLoad() {
super.viewDidLoad()
guard let delegate = UIApplication.shared.delegate as? AppDelegate else { fatalError() }
let bundle = Bundle.main
let component = AudioComponentDescription(componentType: bundle.auComponentType,
componentSubType: bundle.auComponentSubtype,
componentManufacturer: bundle.auComponentManufacturer,
componentFlags: 0, componentFlagsMask: 0)
let config = HostViewConfig(name: bundle.auBaseName, version: bundle.releaseVersionNumber,
appStoreId: bundle.appStoreId,
componentDescription: component, sampleLoop: .sample1) { url in
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
let hostViewController = Shared.embedHostView(into: self, config: config)
delegate.setStopPlayingBlock { hostViewController.stopPlaying() }
self.hostViewController = hostViewController
}
}
Actions
文件夹包含用于管理用户预设(例如创建、删除和重命名)的流程。 HostView
故事板包含一组对 AUv3 演示应用程序有用的 UI 元素。
与上述不同,macOS 更加复杂,因为我还没有启动并运行更简单的东西。 主要问题是在从包中解压缩时,使应用程序的委托、主窗口和主视图控制器全部建立并正常运行。 因此,在完成之前,必须在 HostViewConfig
中传递大量的 UI 元素,并使用它实例化一个 HostViewManager
。 应该尽早完成此操作,但不能在主视图控制器为其分配窗口之前完成。 因此,最好的选择是执行类似以下的操作,我们在其中监视视图上设置的窗口。 剩下的唯一任务是在首次启动时向用户显示初始提示。
override func viewDidLoad() {
super.viewDidLoad()
// When the window appears, we should be able to access all of the items from the storyboard.
windowObserver = view.observe(\.window) { _, _ in self.makeHostViewManager() }
}
func makeHostViewManager() {
guard let appDelegate = appDelegate,
appDelegate.presetsMenu != nil,
let windowController = windowController
else {
fatalError()
}
let bundle = Bundle.main
let audioUnitName = bundle.auBaseName
let componentDescription = AudioComponentDescription(componentType: bundle.auComponentType,
componentSubType: bundle.auComponentSubtype,
componentManufacturer: bundle.auComponentManufacturer,
componentFlags: 0, componentFlagsMask: 0)
let config = HostViewConfig(componentName: audioUnitName, componentDescription: componentDescription,
sampleLoop: .sample1,
playButton: windowController.playButton,
bypassButton: windowController.bypassButton,
presetsButton: windowController.presetsButton,
playMenuItem: appDelegate.playMenuItem,
bypassMenuItem: appDelegate.bypassMenuItem,
presetsMenu: appDelegate.presetsMenu,
viewController: self, containerView: containerView)
hostViewManager = .init(config: config)
}
override func viewDidAppear() {
super.viewDidAppear()
hostViewManager?.showInitialPrompt()
}
不是很好,但现在使用起来也不会太麻烦。 并且很高兴能够抽象出我的音频单元应用程序共享的所有通用功能。