OSXでObjective-Cを用いてiTunesが現在再生している楽曲情報を取得する方法(& D言語でNow Playingをツイートする方法)

# はじめに
えーっと、なにがしたいか

* OSXのiTunesで現在再生している楽曲情報を取得したい
* 出来ればD言語からそれを呼び出したい(ABI使うだけだし、たぶん他の言語でも可能)

ここで検索してみた結果、`AppleScript`を使う方法は簡単に得ることが出来ましたが、僕は`Objective-C`で結果を得たかったのです(`extern "C"`することで`D言語`から呼ぶことが出来るので。)
ちなみに、僕は`Objective-C`プログラマーではないので、いかに記載するコードは`Objective-C`っぽくないかもしれないです。
それで、色々と調べた結果、`Scripting Bridge`(?)というのを使うことで実現できました(AppleのDeveloper向けのページにヒントが書いてあった)。  

[Using Scripting Bridge](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ScriptingBridgeConcepts/UsingScriptingBridge/UsingScriptingBridge.html)  


# iTunes.hをつくる。
iTunesに関する関数のプロトタイプ宣言等が記述された`iTunes.h`を生成します。
この辺に関してはよくわかってないので詳しく書くことは出来ませんが、上記のページを参考にして作成します。
次のコマンドで生成することが出来ます。

```zsh
 $ sdef /Applications/iTunes.app | sdp -fh --basename iTunes
```

# Objective-Cのコードを書く
次のようなコードを書くことで実現できました。
ここでは、D言語から簡単に扱うために`Music`という構造体を定義しています。
また、D言語で書いたプログラムとリンクするために`extern "C"`しています。
それから、うえで述べたように僕は`Objective-C`プログラマーではなく、今回はじめて`Objective-C`を書いただけなので以下のコードが`Objective-C`的であるかは分からないです(たとえば、どうやらObjective-Cではstructについて、変数の型として書くときにstructの修飾がいらないみたいだが(それでコンパイル通った)あえて書いたりした)。
それでは。

```objc:iTunes.mm
#include <stdlib.h>
#include <stdbool.h>
#import "iTunes.h"

// For ABI
extern "C" {
  struct Music {
    const char* name;
    const char* album;
    const char* artist;
  };

  bool checkiTunesIsRunning() {
    return [[SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"] isRunning];
  }

  struct Music* getCurrentiTunesPlay() {
    iTunesApplication* iTunes;
    iTunesTrack* current;
    struct Music* music = NULL;

    if (!checkiTunesIsRunning()) {
      return music;
    }

    music = (Music*)malloc(sizeof(Music));

    iTunes  = [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"];
    current = [iTunes currentTrack];

    music->name   = [[current name] UTF8String];
    music->album  = [[current album] UTF8String];
    music->artist = [[current artist] UTF8String];

    return music;
  }
}

// Test
int main() {
  struct Music* music;

  music = getCurrentiTunesPlay();

  if (music == NULL) {
    printf("[Fatal Error] - iTunes is not running\n");
    return -1;
  }

  printf("name:   %s\n", music->name);
  printf("album:  %s\n", music->album);
  printf("artist: %s\n", music->artist);

  free(music);
}
```

それではコンパイルしてみましょう。
コンパイルは次のコマンドで可能です。

```zsh
$ clang -o iTunes iTunes.mm -framework Foundation -framework iTunesLibrary -framework ScriptingBridge
```

実行してみると

<img width="972" alt="iTunes now playing in objc" src="https://qiita-image-store.s3.amazonaws.com/0/18198/0f42b5ad-de23-968d-a34c-fe2cc8d8de7d.png">

できました！！！
再生中の楽曲情報を得ることに成功しました。


# 【おまけ】 Dで簡単なNowPlayingを書いてみる
## とりあえず、Dから呼んでみる
ここで、D言語から呼んでみましょう。
と言っても非常に簡単です。
D言語側で`iTunes.mm`にてextern "C"した`Music`構造体と`getCurrentiTunesPlay`関数を`extern(C)`のなかでプロトタイプ宣言し、リンクするだけです。

とりあえず、以下にDのコードを貼り付けます。

```d:iTunesByD
import std.stdio,
       std.string;

extern(C) {
  struct Music {
    const char* name;
    const char* album;
    const char* artist;
  }

  Music* getCurrentiTunesPlay();
}

void main() {
  Music* music;

  music = getCurrentiTunesPlay();

  if (music == null) {
    throw new Error("[Fatal Error] - iTunes is not running.");
  }<img width="1046" alt="スクリーンショット 2016-02-23 11.54.11.png" src="https://qiita-image-store.s3.amazonaws.com/0/18198/3a33578d-2849-94f8-1bd8-8c9c822551cf.png">


  writefln("name:   %s", fromStringz(music.name));
  writefln("album:  %s", fromStringz(music.album));
  writefln("artist: %s", fromStringz(music.artist));
}
```

**先ほどの`iTunes.mm`から`main関数`を削除してから** D言語とリンクします
コンパイル&リンクは以下のコマンドで出来ます

```zsh
$ clang -c iTunes.mm
$ dmd iTunesByD.d iTunes.o -L-framework -LFoundation -L-framework -LiTunesLibrary -L-framework -LScriptingBridge
```

これで実行してみると

<img width="1046" alt="iTunes now playing in D" src="https://qiita-image-store.s3.amazonaws.com/0/18198/30c01af6-2071-8c24-a32a-52ca452fa93f.png">

出来ました！！


##　NowPlayingを書く

さて、ここまで来たら`NowPlaying`をツイートしたくなりますよね?
それで、拙作のD言語製Twitterライブラリ [Twitter4D](https://github.com/alphaKAI/Twitter4D) を用いて書いてみましょう。
D言語には`DUB`というビルドツール兼パッケージマネージャーが存在しますが、ここでは簡単のため、それを用いずに `Twitter4D` をcloneしそのなかの`twitter4d.d`を作業ディレクトリにコピーすることにします(`DUB`について書かないといけなくなるので、こうすることにしました)。

とりあえず、`iTunes.mm`の`main関数`を削除したものとして話を進めます。
早速コードを書くことにします。
解説については特に無いので...  
あ、`ConsumerKey`と`AccessToken`等は各自で用意して以下の`nowPlaying.d`の中に書き込んでください。。。
`ConsumerKey`は持っているが、`AccessToken`を持っていない場合は拙作のツールを使うといいかもしれません: [accessTokenGetter](https://github.com/alphaKAI/accessTokenGetter)  


```d:nowPlaying.d

import std.stdio,
       std.string;
import twitter4d;

extern(C) {
  struct Music {
    const char* name;
    const char* album;
    const char* artist;
  }

  Music* getCurrentiTunesPlay();
}


void main() {
  Twitter4D t4d;
  Music* music;
  
  string[string] keys = [
    // Please Replace Here
    "consumerKey"       : "",
    "consumerSecret"    : "",
    "accessToken"       : "",
    "accessTokenSecret" : ""
  ];

  music = getCurrentiTunesPlay();

  if (music == null) {
    throw new Error("[Fatal Error] - iTunes is not running.");
  }

  t4d = new Twitter4D(keys);
  string name   = cast(string)fromStringz(music.name),
         album  = cast(string)fromStringz(music.album),
         artist = cast(string)fromStringz(music.artist);

  string nowPlayingString = "Now Playing: \"" ~ name ~ "\" from \"" ~ album ~ "\" (" ~ artist ~ ") #NowPlaying";

  writeln("NowPlaying:");
  writefln("name:   %s", name);
  writefln("album:  %s", album);
  writefln("artist: %s", artist);
  writeln;

  writeln("[Tweet] - ", nowPlayingString);
  t4d.request("POST", "statuses/update.json", ["status" : nowPlayingString]);
}
```

うえのコードを`nowPlaying.d`として保存してください。(以下に記述するコマンドで作成するnowPlayingDでぃれくとりに配置してください)

```zsh
$ mkdir nowPlayingD
$ cd nowPlayingD
$ mv ../iTunes.mm ../iTunes.h .
$ git clone https://github.com/alphaKAI/Twitter4D
$ cp Twitter4D/source/twitter4d.d .
$ clang -c iTunes.mm
$ dmd nowplaying.d twitter4d.d itunes.o -L-framework -LFoundation -L-framework -LiTunesLibrary -L-framework -LScriptingBridge
```

これでコンパイル&リンクされます

これを実行することで

<img width="551" alt="スクリーンショット 2016-02-23 12.18.50.png" src="https://qiita-image-store.s3.amazonaws.com/0/18198/8200077a-ef81-96dd-d05d-a5e08fb30d4c.png">

となり、同時にTwitterには

<blockquote class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr">Now Playing: &quot;千本桜&quot; from &quot;VOCALO CLASSIC&quot; (石川綾子) <a href="https://twitter.com/hashtag/NowPlaying?src=hash">#NowPlaying</a></p>&mdash; α改 (@alpha_kai_NET) <a href="https://twitter.com/alpha_kai_NET/status/701968332056977408">February 23, 2016</a></blockquote> <script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

と投稿されます!!

お疲れ様でした。

僕は全然`Objective-C`や`OS XのiTunes`について詳しくないのでわかりませんが、何かしらの方法で次の曲が再生されたイベントをフック出来るのであれば完璧に`NowPlaying`を実装することが出来ますね。

では、以上です。