Allihoopa 1.3.2

Allihoopa 1.3.2

测试已测试
Lang语言 Obj-CObjective C
许可证 MIT
发布上次发布2017年6月

Ali Baaba维护。



Allihoopa 1.3.2

  • mhallin

Allihoopa iOS SDK


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.frameworkAllihoopaCore.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 文档

最新版本的API文档可在以下位置找到:http://cocoadocs.org/docsets/Allihoopa

设置SDK

您需要一个类,例如您的应用代理,来实现 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
    }
}