SwiftPM で C言語を含むライブラリを作る

# SwiftPM で C言語を含むライブラリを作った

* C++ で作成された[音声分析合成システムWORLD](https://github.com/mmorise/World) の Swift ラッパーである WorldInApple を作成しています   

https://github.com/fuziki/WorldInApple

* Swift Package Manager に対応した話です
* 動作の様子  
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">worldのswiftのwrapperを公開しましたー<br>iPhoneでボイチェンができます！<a href="https://t.co/t2Y3BvhvLi">https://t.co/t2Y3BvhvLi</a> <a href="https://t.co/b7AI4F1Qkr">pic.twitter.com/b7AI4F1Qkr</a></p>&mdash; ふじき (@fzkqi) <a href="https://twitter.com/fzkqi/status/1229194017910378496?ref_src=twsrc%5Etfw">February 17, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

* Vocoder 自体の話はこちら

https://qiita.com/fuziki/items/e75aac8ca55c0a1e9dbc

# WorldInApple の実装
## やったこと
* 公式ドキュメントの [Creating C Language Targets](https://github.com/apple/swift-package-manager/blob/main/Documentation/Usage.md#creating-c-language-targets) の通りです
* C++ で実装された World 本体のターゲットを作成し、Swift のラッパーのターゲットから依存させました

## Package.swift

-> https://github.com/fuziki/WorldInApple/blob/develop/Package.swift

```swift
import PackageDescription

let package = Package(
    name: "WorldInApple",
    platforms: [.iOS(.v11), .macOS(SupportedPlatform.MacOSVersion.v10_15)],
    products: [
        .library(
            name: "WorldInApple",
            targets: ["WorldInApple"]),
    ],
    targets: [
        // C++ で書かれた、World 本体
        .target(
            name: "WorldLib"
        ),
        // WorldLib の Swift ラッパー
        .target(
            name: "WorldInApple",
            dependencies: ["WorldLib"]),  // WorldLib に依存
    ]
)
```

![direcotry.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214313/ed8f62e1-62b3-d072-c656-06c177967b32.png)


### WorldLib
* C++ で実装された World 本体です
* SwiftPM で USER_HEADER_SEARCH_PATHS を指定する方法を見つけられなかったので、コピーしてヘッダを編集しています
  * 差分は、パッチファイルにまとめています  
    https://github.com/fuziki/WorldInApple/blob/develop/fix-world-header-path.patch

#### WorldLib/include/World.h
-> https://github.com/fuziki/WorldInApple/blob/develop/Sources/WorldLib/include/World.h

* WorldLib の公開されるヘッダです
* World の必要なヘッダを公開しています

```c++
#import "../world/dio.h"
#import "../world/stonemask.h"
#import "../world/cheaptrick.h"
#import "../world/d4c.h"
#import "../world/synthesis.h"
#import "../world/harvest.h"
#import "../world/synthesisrealtime.h"
#import "../world/matlabfunctions.h"
```

### WorldInApple

* WorldLib の Swift ラッパーです
* `import WorldLib` することで、World て定義された機能を利用できます

-> https://github.com/fuziki/WorldInApple/blob/develop/Sources/WorldInApple/WorldInAppleComponents/AperiodicityEstimator.swift

```swift
import WorldLib

public class AperiodicityEstimator: WorldInAppleComponents {
    private var d4cOption = D4COption() // World で定義された構造体が利用できる
    //〜中略〜
    public func estimatAperiodicity(x: UnsafeMutablePointer<Double>, x_length: Int32) {
        // World で定義された関数が利用できる
        D4C(x, x_length, Int32(parameters.fs), parameters.time_axis, parameters.f0, Int32(parameters.f0_length), Int32(parameters.fft_size), &d4cOption, parameters.aperiodicity)
    }
}
```

# サンプルの実装 (Examples)
* WorldInApple の パッケージから参照されないようにダミーの Package.swift を配置しています  

-> https://github.com/fuziki/WorldInApple/blob/develop/Examples/Package.swift

```swift
import PackageDescription

let package = Package(
  name: "dummy",
  products: [],
  targets: []
)
```

* WorldInApple.xcworkspace から実行可能です
* Examples プロジェクトと、WorldInApple のパッケージが入っています

![workspace.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214313/69ea03a0-1e3f-4110-eeb6-9636289b4400.png)

* Examples ターゲットから WorldInApple を参照しています

![dependency.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214313/cab7377b-fcdb-4259-5d78-91453b67452b.png)

* `import WorldInApple` することで、作成した World の Swift のラッパーを利用可能です

-> https://github.com/fuziki/WorldInApple/blob/develop/Examples/Examples/Shared/ContentViewModel.swift

```swift
import AVFoundation
import Combine
import Foundation
import SwiftUI
import WorldInApple

class ContentViewModel: ObservableObject {
    
    private let world = WorldInApple(fs: 48000, frame_period: 5, x_length: 38400)
    // 〜中略〜
}
```

# 公開する & 利用する
* GitHub に push することで、pure swift のパッケージ同様に利用することが可能でした