Objective-C/Swift SDK,用于与 Allihoopa 交互。
根据您的配置,安装 Allihoopa SDK 有多种方法。
如果您使用 CocoaPods,您可以直接将此 SDK 添加到您的 Podfile
中:
target 'TargetName' do
pod 'Allihoopa', '~> 1.3.2'
end
然后运行以下命令下载依赖项:
pod install
之后,您运行 carthage update
命令来构建框架,然后从 "Carthage/Build" 文件夹中将生成的 Allihoopa.framework
和 AllihoopaCore.framework
拖到您的目标“嵌入式二进制”中。
如果您愿意,可以将 Allihoopa-iOS
项目作为子项目添加到您的应用程序中,并将框架作为依赖项进行构建。在这种情况下,您还需要将 AllihoopaCore-ObjC 作为依赖项包括进来,因为这个项目与 iOS SDK 共享大量代码和功能。
如果您使用上述方法之一,此依赖项将为您自动管理。
您需要将 URL 方案添加到应用的 Info.plist
中: ah-{APP_IDENTIFIER}
,例如,如果您的应用程序标识符为 figure
,则例如 ah-figure
。当您将应用程序注册到 Allihoopa 时,您将收到应用程序标识符和 API 密钥。如果您想加入我们,请发送电子邮件到 [email protected]。
为了使下流流程正常工作,您还需要将以下密钥添加到您的 Info.plist
中
<key>NSCameraUsageDescription</key>
<string>$(PRODUCT_NAME) wants to access your camera</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>$(PRODUCT_NAME) wants to access your photo library</string>
iOS 10 开始强制执行这些密钥 - 如果未指定,则会在用户点击编辑封面图片按钮时应用崩溃。在[技术问答 QA1937](Technical Q&A QA1937: Resolving the Privacy-Sensitive Data App Rejection)中了解更多相关信息。
请查看 SDKExample 文件夹以获取如何在此 SDK 上工作的说明。
最新版本的API文档可在以下位置找到:http://cocoadocs.org/docsets/Allihoopa。
您需要一个类,例如您的应用代理,来实现 AHAAllihoopaSDKDelegate
以支持“打开”功能。以下是实现此功能的方法。
import Allihoopa
// In your UIApplicationDelegate implementation
func applicationDidFinishLaunching(_ application: UIApplication) {
AHAAllihoopaSDK.shared().setup([
.applicationIdentifier: "your-application-identifier",
.apiKey: "your-api-key",
.delegate: self,
])
}
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
if AHAAllihoopaSDK.shared().handleOpen(url) {
return true
}
// Call other SDKs' URL handling methods
return false
}
#import <Allihoopa/Allihoopa.h>
// In your UIApplicationDelegate implementation
- (void)applicationDidFinishLaunching:(UIApplication*)application {
[[AHAAllihoopaSDK sharedInstance] setupWithConfiguration:@{
AHAConfigKeyApplicationIdentifier: @"your-application-identifier",
AHAConfigKeyAPIKey: @"your-api-key",
AHAConfigKeySDKDelegate: self,
}];
}
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
if ([[AHAAllihoopaSDK sharedInstance] handleOpenURL:url]) {
return YES;
}
// Call other SDKs' URL handling methods
return NO;
}
必须在调用任何其他API之前调用 setupWithConfiguration:
。如果SDK设置不当,将抛出异常:如果凭证缺失,或者您未能正确设置URL方案。有关更多信息,请参阅上面的“设置SDK”部分。
配置字典支持以下键 - 与Objective-C和Swift中名称的对比
AHAConfigKeyApplicationIdentifier
/.applicationIdentifier
:由Allihoopa提供的应用标识符的必需字符串。AHAConfigKeyAPIKey
/.apiKey
:包含应用程序API键的必需字符串。AHAConfigKeySDKDelegate
/.sdkDelegate
:用于通知应用程序用户尝试将片段导入此应用程序的可选实例。如果提供,则该实例必须符合 AHAAllihoopaSDKDelegate
协议。AHAConfigKeyFacebookAppID
/.facebookAppID
:包含应用程序的Facebook App ID的可选字符串。这将启用通过iOS内置的Accounts和Social框架进行次要社交共享。必须在您的应用程序的URL处理程序中调用 handleOpenURL:
。只有在成功处理URL的情况下,它才会返回true,这使得可以与其他URL处理程序一起链式调用此调用。
let piece = try! AHADropPieceData(
defaultTitle: "Default title", // The default title of the piece
lengthMicroseconds: 40000000, // Length of the piece, in microseconds
tempo: nil,
loopMarkers: nil,
timeSignature: nil,
basedOn: [])
let vc = AHAAllihoopaSDK.shared().dropViewController(forPiece: piece, delegate: self)
self.present(vc, animated: true, completion: nil)
extension ViewController : AHADropDelegate {
// The drop view controller will ask your application for audio data.
// You should perform work in the background and call the completion
// handler on the main queue. If you already have the data available,
// you can just call the completion handler directly.
//
// This method *must* call completion with a data bundle for the drop to
// succeed. If it doesn't, an error screen will be shown.
func renderMixStem(forPiece piece: AHADropPieceData, completion: @escaping (AHAAudioDataBundle?, Error?) -> Void) {
DispatchQueue.global().async {
// Render Wave data into an NSData object
let bundle = AHAAudioDataBundle(format: .wave, data: data)
DispatchQueue.main.async {
completion(bundle, nil)
}
}
}
}
NSError* error;
AHADropPieceData* piece = [[AHADropPieceData alloc] initWithDefaultTitle:@"Default title"
lengthMicroseconds:40000000
tempo:nil
loopMarkers:nil
timeSignature:nil
basedOnPieceIDs:@[]
error:&error];
if (piece) {
UIViewController* vc = [[AHAAllihoopaSDK sharedInstance] dropViewControllerForPiece:piece delegate:self];
[self presentViewController:vc animated:YES completion:nil];
}
@implementation ViewController
- (void)renderMixStemForPiece:(AHADropPieceData *)piece
completion:(void (^)(AHAAudioDataBundle * _Nullable, NSError * _Nullable))completion {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Render wave data into an NSData object
AHAAudioDataBundle* bundle = [[AHAAudioDataBundle alloc] initWithFormat:AHAAudioFormatWave data:data];
dispatch_async(dispatch_get_main_queue(), ^{
completion(bundle, nil);
});
});
}
@end
dropViewController
创建一个负责通过辅助代理对象放下您提供的片段的视图控制器。如果用户未登录,将首先显示登录对话框。当用户完成操作或取消时,视图控制器将自行关闭并通知代理。
片段可以包含不同类型的元数据。上面的示例显示了我们需要的最小数据量:一个默认标题、片段音频数据长度以及将音频渲染为已知格式的代理方法。
AHADropPieceData
对输入执行基本的验证:它将返回一个包含有关错误信息的 NSError
。错误可能包括诸如循环标记反向或长度超出合理范围等问题。这些问题通常是程序员的错误,而不是可以在有意义的方式中处理的运行时错误。
如果您的应用程序了解它,则可以向 AHADropPieceData
提供更多元数据,以及在 AHADropDelegate
上实现比上面所示更多方法。以下是显示您可以设置的所有数据的完整示例
let piece = try! AHADropPieceData(
defaultTitle: "Default title",
lengthMicroseconds: 100000000,
// The fixed tempo in BPM. Allowed range: 1 - 999.999 BPM
tempo: AHAFixedTempo(fixedTempo: 128),
// If the piece is a loop, you can provide the loop markers.
loopMarkers: AHALoopMarkers(startMicroseconds: 0, endMicroseconds: 500000),
// If the time signature is available and fixed, you can provide a time
// signature object.
//
// The upper numeral can range from 1 to 16, incnlusive. The lower numeral
// must be one of 2, 4, 8, and 16.
timeSignature: AHATimeSignature(upper: 8, lower: 4),
// If the piece is based on other pieces, provide a list of the IDs of those
// pieces here.
basedOn: [],
// If the piece has a known tonality, provide the scale and root here.
// Other values are AHATonality.unknown() (default if omitted), and
// AHATonality.atonal() for pieces that contain audio that doesn't have a
// tonality, e.g. drum loops.
tonality: AHATonality(tonalScale: AHAGetMajorScale(4), root: 4)
)
extension ViewController : AHADropDelegate {
// The "mix stem" is the audio data that should be used to place the piece
// on a timeline. Call the completion handler with a data bundle instance
// on the main queue when data is available.
//
// The mix stem is mandatory.
func renderMixStem(forPiece piece: AHADropPieceData, completion: @escaping (AHAAudioDataBundle?, Error?) -> Void) {
...
}
// You can supply a default cover image that the user can upload or change.
// Call the completion handler with an image of size 640x640 px, or nil.
func renderCoverImage(forPiece piece: AHADropPieceData, completion: @escaping (UIImage?) -> Void) {
...
}
// You can supply a file as an attachment to the piece. The file can be of
// any format. This file can be read back by clients when fetching a piece.
// Attachment max size is 30mb.
// Call the completion handler with a data bundle instance on the main queue
// when data is available.
renderAttachment(forPiece piece: AHADropPieceData, completion: @escaping (AHAAttachmentBundle?, Error?) -> Void) {
...
}
// If the audio to be placed on the timeline is different from what users
// should listen to, use this delegate method to provide a "preview"
// audio bundle.
//
// For example, if you're providing a short loop you can supply only the
// loop data in a lossless format as the mix stem, and then a longer track
// containing a few loops with fade in/out in a lossy format in the
// preview audio.
//
// The preview audio is what's going to be played on the website.
//
// If no preview audio is provided, the mix stem will be used instead. This
// replacement is done server-side, the mix stem data will only be uploaded
// once from the client.
func renderPreviewAudio(forPiece piece: AHADropPieceData, completion: @escaping (AHAAudioDataBundle?, Error?) -> Void) {
}
// You can implement this method to get notified when the user either
// cancels or completes a drop.
func dropViewController(forPieceWillClose piece: AHADropPieceData, afterSuccessfulDrop successfulDrop: Bool) {
}
}
UIActivityViewController
放下@IBAction func share(_ sender: UIView?) {
let piece = try! AHADropPieceData(
defaultTitle: "Default title",
lengthMicroseconds: 100000000,
tempo: nil,
loopMarkers: nil,
timeSignature: nil,
basedOn: [])
let vc = UIActivityViewController(
activityItems: [],
applicationActivities: [AHAAllihoopaSDK.shared().activity(forPiece: piece, delegate: self)])
vc.modalPresentationStyle = .popover
self.present(vc, animated: true, completion: nil)
let pop = vc.popoverPresentationController!
pop.sourceView = sender
pop.sourceRect = sender!.bounds
}
如果您已经使用 UIActivityViewController
向用户显示共享弹出菜单,则可以使用 activityForPiece:delegate:
创建 UIActivity
。它具有与上面创建放下视图控制器相同的接口,并使用相同的代理协议。
在Allihoopa中,片段的音调可以处于三种不同状态之一
对于已知调性的作品,调性由两个值表示:一个是音阶,一个是根音。音阶由十二个布尔值组成,每个布尔值代表该音高类是否为调性的成员。根音指示在数组中调性开始的索引。
这可能会有些令人困惑,但一个简单的例子可能有助于理解。C大调可以用音阶中的“所有白键”来表示。
C C# D D# E F F# G G# A A# B
Scale: [1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1]
Root: 0
如果我们看一下A小调;C大调的平行小调,我们可以看到音阶数组相同,但根音已经被移动了。
C C# D D# E F F# G G# A A# B
Scale: [1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1]
Root: 9
如何处理作品的调性元数据取决于应用程序的类型。通常,如果您的应用程序在其文档格式中包含调性字段,你应该在导入和删除时都使用它。对于更深入的集成,您可以查看我们的声乐录制应用程序[Take]。
由于调性字段有12 * 2^12种可能的值,我们并不尝试正确地显示调性的名称。Allihoopa网站目前非常天真:它完全忽略了根值,显示匹配的大调名称。如果找不到匹配的大调,则网站将显示“异国情调”。上面的两个例子都会标记为“C”。
当用户在网站上选择“在[您的基本软件]中打开”时,SDK会获取请求,获取作品元数据,并用用户想要打开的作品调用openPieceFromAllihoopa:error:
方法。在AHAPiece
实例中有下载指定格式中的音频数据的方法,您可以使用这些方法将音频导入当前文档,或保存供以后使用。AHAPiece
还包含元数据,类似于AHADropPiece
。
func openPiece(fromAllihoopa piece: AHAPiece?, error: Error?) {
if let piece = piece {
// The user wanted to open a piece
// Download the mix stem audio in AAC/M4A format. You can also use .wave
// and .oggVorbis
piece.downloadMixStem(format: .AACM4A, completion: { (data, error) in
if let data = data {
// Data downloaded successfully
}
})
}
else {
// Handle the error
//
// This should not *usually* happen, but if the piece was removed after
// opening, or if there were some connection issues we can end up here
}
}
- (void)openPieceFromAllihoopa:(AHAPiece*)piece error:(NSError*)error {
if (piece != nil) {
// The user wanted to open a piece
[piece downloadMixStemWithFormat:AHAAudioFormatAACM4A completion:^(NSData* data, NSError* error) {
if (data != nil) {
// Data downloaded successfully
}
}];
}
else {
// Handle the error
}
}