水曜日, 2月 11, 2015

iOS開発再入門 3 AudioKitを利用して複数のサウンドファイルを鳴らす

前回の最後に予想した通り、今回の目的にはAVAudioPlayerよりAVAudioUnitSamplerというクラスを使う方が良さそう。loadAudioFilesAtURLs()というメソッドがあり、これで複数のファイルを読み込むことができる。他に音声を扱うにはMedia Player framework、Audio Toolbox framework、Audio Unit framework、OpenAL frameworkがある。旧バージョンはOpenALを使用した。

AVAudioUnitSamplerはiOS 8から使えるAPIで、これを採用するということは動作環境がiOS 8以上である必要がある。2月2日時点のシェアは72%とのこと。このページで確認できる。

loadAudioFilesAtURLs(_ audioFiles: [AnyObject]!, error outError: NSErrorPointer)というメソッドで複数のファイルを読み込める。AVAudioUnitMIDIInstrumentクラスを継承しているので、音を鳴らすにはAVAudioUnitMIDIInstrumentクラスのメソッドを利用することになりそうだ。

…と思ったが全く使い方が分からなかった。いくつか検索して調べた結果、音声ファイルにメタデータを入れておくとかファイル名に音階を含むとMIDIと同様に扱えるなどと書いてある。任意のMIDIノート番号とチャンネルに、任意の音声ファイルをアサインできれば便利な気がするが、どうもそういう使い方はできないようだ。この方向は諦め、OpenALで実装しようかと思ったが、OpenALはCで書かれていて、Cで書かれたものをSwiftで使う方法がよく分からなかった。今後の課題とする。

同じくiOS 8から使えるAVAudioFileクラスのreadIntoBuffer(_ buffer: AVAudioPCMBuffer!, error outError: NSErrorPointer)メソッドを使うと、音声データをメモリに読み込めて遅延のない再生ができそうだが、全てのデータをメモリに入れられるのか、自分でうまく管理する必要があるのかどうかは検証が必要。

音関係の情報は相変わらず少ない。Objective-Cではそこそこ見つかるのだけど、Swiftで参考になるものがほとんどない(だからこそできたら良かったんだけど)。

ということで既にそういう課題を解決してそうなライブラリの力を借りることにする。ゲームなどでよく使われているCocos2Dも検討したが(内部ではOpenALを使っているらしい)、AudioKitが目的に合ってそう。サウンドエンジンはCsoundとのこと。学生時代に触って意味不明で挫折したけどアカデミックな電子音楽ではよく使われている(はず)。

CocoaPodsを入れる

ライブラリの管理はCocoaプロジェクト用のパッケージマネージャーであるCocoaPodsを使う。

pod setup

でローカルにリポジトリを作る。これがないとpod installしようとしてもエラーになる。

pod init

でPodfileを作成し、

target 'Dropophone' do
  pod 'AudioKit', '~> 1.2'
end

と書いて

pod install

これでAudioKitを使う準備ができる。今後は.xcworkspaceからプロジェクトを開くようにする。

SwiftでObjective-Cのプログラムを使用する

AudioKitはObjective-Cで書かれているので、Swiftで使用するにはブリッジが必要。[プロジェクト名]-Bridging-Header.hというファイルに(なければ作る)、

#import "AKFoundation.h"
#import "AKTools.h"

と書く。.xcodeprojファイルの「Build Settings」に「Swift Compiler」という項目があるので、「Objective-C Bridging Header」に先ほどのヘッダファイルを設定する。これでAudioKitのクラスが使えるようになる。

音声ファイルの追加

Finder上(Terminalでもいいけど)でsoundsというフォルダに全ての音声ファイルを格納し、XcodeのSupporting Filesの中にドロップする。ラジオボタンは「Create Groups」を選ぶ。これはFinder上の階層構造とは関係なく、MainBundleに対してフラットにリソースを格納することを意味する。「Create folder references」を選ぶと、finder上の階層構造を反映するようになるので、リソースを読み込む際にファイルまでのパスや、サブディレクトリを指定する必要がある。状況に応じて使い分けたい。

音声ファイルの形式はAIFFにしておく。

AudioKitでサウンドファイルを鳴らす

AudioKitはさすがにCsoundをベースにしているだけあって、サウンドファイルを読み込むクラスにplayしたら音が鳴るみたいな単純さではない。Expamleに音声ファイルを鳴らすプロジェクトがあるが、今のところObjective-Cで、かつクラス構成も少し複雑になっているので、シンプルに音を鳴らすための手順が分かりにくい。

色々と触った結果、

  1. 楽器(AKInstrument)を定義し、出力(AKAudioOutput)に繋ぐ。
  2. ノート(AKNote)のinstrumentプロパティに楽器を設定する。
  3. オーケストラ(AKOrchestra)に楽器を追加。
  4. オーケストラをstartし、ノートをplayすると音が鳴る。

というような流れがあるようだ。Csoundを知っていると分かりやすいのかな。

コードをgistに置いたので、見比べると大体分かると思う。

GUI

もうひとつ考慮したいのは、30個のボタンを配置する方法。前回は頑張って30本のアウトレットを繋いだが、今回は手作業ではなくプログラムで何とかしたい。

経験的に、プログラムから制御したいことがあると「programmatically」という語を含めて検索する。

button1.setTranslatesAutoresizingMaskIntoConstraints(false)

とすることでこれから定義するAuto Layoutを適用することができる。Auto Layoutに関してはまだまだ分かっていないことが多いのでひとまず適当にボタンが並べた。何故か最初のボタンがでかい。

UIButtonのインスタンスにイベントを設定。tagを利用して、どのボタンが押されたのかを識別する。

これでボタンに各音源がアサインできたので、応用すればシンプルなパッドのような楽器になる。次はAuto Layoutを掘り下げながら設計を考え直す。

実機でも動作確認済み。たまに音量がクリップするが、連打や同時発音など問題なさそう。

日曜日, 2月 08, 2015

iOS開発再入門 2 サウンドファイルを鳴らす

ボタンを押すと音が鳴るサンプルを作る

まずはシンプルなところから始めることにする。新規にプロジェクトを作成し、iOS Applicationから「Single View Application」を選択する。Languageを「Swift」にしDevicesは「Universal」にする。Use Core Dataのチェックは外す。

Main.storyboardを選択状態にし、UtilitiesからButtonをドラッグし、Storyboard上のView Controllerにドロップ。

controlキーを押しながらボタンをクリックし、上(か下)に少し動かして離すとレイアウト用の設定ウィンドウがポップアップする。

Center Horizontally Container
を選ぶと、水平方向にセンタリングされる。同様に右(か左)に少し動かして離し、
Center Vertically Container
を選んで垂直方向のセンタリングを設定する。これで画面の中央にボタンが置かれる。このように設定した内容は「Constraints」としてViewに紐付いたレイアウトのルールになる。

Assistant editorを開く(Xcode右上の丸が2つ重なったアイコンをクリックする)と、ViewController.swiftが表示されるので、controlキーを押しながら先ほど追加したボタンをクリックし、ViewController.swift上にドラッグすると「Insert Outlet, Action, or Outlet Collection」と出る。Connectionを「Action」、NameをplaySound、Typeを「UIButton」、Storageを「Weak」にして「Connect」をクリックすると、このボタンを押した時に実行されるメソッドが作成される。

最終的なViewController.swiftは以下。

import UIKit
import AVFoundation

class ViewController: UIViewController {
    
    var audioPlayer: AVAudioPlayer!

    override func viewDidLoad() {
        super.viewDidLoad()

        var error: NSError?
        let fileURL: NSURL? = NSBundle.mainBundle().URLForResource("BassC1", withExtension: "caf")!
        audioPlayer = AVAudioPlayer(contentsOfURL: fileURL!, error: &error)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    @IBAction func playSound(sender: UIButton) {
        audioPlayer.stop()
        audioPlayer.play()
    }

}

AVFoundationフレームワークを利用して音声ファイルを鳴らすため、ViewController.swiftの冒頭でimportする。AVAudioPlayerのインスタンスを保持する変数audioPlayerを宣言。「!」はImplicitly Unwrapped Optional型であり、ImplicitlyUnwrappedOptional<T>の簡略化した書き方(シンタックスシュガー)である。Optional型の「?」も同様にOptional<T>のシンタックスシュガー。Tには任意の型名が入る。

Optional型から元の型を取り出すのに「!」をつけてアンラップするのだが、Implicitly Unwrapped Optional型にはその必要がない。

Viewが読み込まれた後に実行される

viewDidLoad()
の内部でAVAudioPlayerオブジェクトを作り、先ほど作った
playSound(sender: UIButton)
の内部で音を鳴らす処理を書く。音声ファイルはXcodeのSupporting Filesの中に置いておく。ファイルのパスはファイル名と拡張子を指定するだけで、どこに置いていてもうまく処理してくれるので、分かりやすいようにしておけばよい。

ビルドするとシミュレータが起動し、ボタンが画面の中央に配置され、クリックすると音が鳴る。単純に考えると、この作業を30回繰り返せばほぼできてしまいそうな感じだが、設計として美しくない(同じような処理をするメソッドが複数できてしまう)し、問題は他にもある。

おそらくAVAudioPlayerは長めの音声ファイルを再生するために作られており、Dropophoneのように短い音声ファイルを遅延なしで再生するのには向いていないかも知れないということ。複数の音を同時に鳴らすことができるかも不安だし、音量がクリップしてしまう可能性もある。

これらの問題については次回以降で考えるが、とにかくUdacityのコースが素晴らしい。Xcodeの各パネルの名称や使い方、フレームワークの構成、ヘルプやドキュメントの読み方、プログラムを組み立てるための問題の切り分け方、Googleでの検索方法やStack Overflowの読み方、Objective-CやSwiftのコードから処理を推測する方法など、実際の開発作業で必要な内容を一通り体験できる。英語も平易で聞き取りやすい。

金曜日, 2月 06, 2015

福島に行ってきた

イノベーション東北のプロジェクトのひとつ、LIFEKUの方々が募集していた観光アプリの制作に参加することになり、1月24日, 25日と週末を利用して福島に行ってきた。

震災の影響は目に見える部分ではそれほど感じられない。しかし人口の減少や、福島駅西口にある大型ショッピングモールの影響などで、東口商店街の商売は厳しい状況に立たされているようだ。彼らは生き残りをかけて世界でも戦える商品やサービスを開発しようとしている。

最初に伺ったのは「あんざい果樹園」。通称あんかじゅ。ご主人はfacebookやLINEをめちゃめちゃ使いこなしている。奥さんが集めた器や雑貨も販売していて、ここがライフクの人々の溜まり場となっているそうだ。日当たりもよく気持ちのいい建物。林檎と洋梨をいただいた。林檎は蜜が多く、洋梨はどろどろしてなくて密度が高い。とても美味しかった。敷地内で音楽イベントなども開催されていて、ハナレグミや原田郁子さん、大友良英さんなどよく知っているアーティストがライブを行った。

昼は北京料理「王芳」で食事。餃子が非常に美味しい。酢と辛子でいただく。みそそば(いわゆるジャージャー麺)を食べたが、初めて食べる味で良かった。歳を取ると新しい味に出会うことが至上の喜びになる。

うろこや」でお茶をいただきつつ、甘納豆をご馳走になる。「塩花豆」をお土産に買って帰った。

BarnS」で服や雑貨を見る。後で触れる「Pick-Up」の姉妹店で、女性向けの服や雑貨が中心。旭川の知り合いが働いているSÜNUSUの商品も置いてあった。2階がワークショップなどを行えるスペースになっている。苺をいただく。食べてばっかり。

その後「Pick-Up」へ移動。藁谷さんが働いている。「ボトムズの丈は折らずにちゃんと合わせて切る」「ニューバランスの紐はベロの後ろに入れる」とか作法があるんだよと教えてもらった。2階には社長が手書きで記している日本のファッションの歴史年表があり、沢山のカタログや、昔のファッション雑誌がある。「外国から服が輸入されてくる」という体験をした最初の世代がPick-Upの社長であり、BEAMやUnited Arrowsの創業者達なのだそう。店が狭く店員と客が向き合わざるを得ないので、うざいくらいに話しかけるらしい。店の人と仲良くなることや分かっている人にアドバイスしてもらえるのはありがたいことだと思う。コーディネート代行サービスがあるくらいだし。服を選んでくれる人、良い写真を撮ってくれる人、信頼できる美容師を見つけると人生は豊かになる。ドラゴンクエストのパーティみたいな感じ。

その後「珈琲舎 雅」でコーヒーをいただく。お洒落で落ち着いた店内。マグカップが色々あって、楽しい。「biji」でビンテージの家具を見せてもらい、駅前を歩いて「OPTICAL YABUUCHI」へ。籔内さんが店主をしている眼鏡屋で、元々は時計屋だったそうだ。籔内さんやスタッフの眼鏡に対する知識や熱意は素晴らしく、眼鏡デザイナーの話やオリジナルで作っているシリーズの話など、どんどん面白い話が出てくる。2階に個性的な植物を扱う「bloom」、レコード屋の「Little Bird」が入っていて、オシャ。籔内さんが設計し、LIFEKUの皆が手伝ってリノベーションしたそうだ。籔内さんは眼鏡作りを追求する余りRhinocerosに手を出したりもしていて、とにかく何でもやっている。

木だけで作ってるフレーム。数ヶ月待ちらしい。

ラ・セルバティカで美味しい晩御飯を食べ(その日の料理に使った肉や野菜からとった特別なあったかいスープで始まる)、福島のことやお互いのこと、アプリに対する考えなどを話した。

23時半頃にホテルに戻り、各自シャワーを浴びるなどして整えた後、0時30分から2時過ぎまでミーティング。メンバー間の面白いと思うポイントが近く、すぐに話がまとまった。ディレクターは連日の疲れにより撃沈、エンジニアはテンションが上がって夜の街に繰り出して行った(何もない道をただ歩いただけだったらしい)。少し資料に手を入れて就寝。

8時前に起き、撃沈したディレクターを置いて鯖湖湯という市が運営している共同浴場に行った。源泉が51.4度もあり、水で薄めないことにはどうやっても入れない。常連は薄めないそうだが、観光客向けに薄めてあげてくれという注意書きがある。かけ湯をしながら徐々に身体を慣らしていくのだが、それでもめちゃくちゃ熱い。建築も面白く、脱衣所と浴室がシームレス。

外に出たらちょうど雪が降っていて、非常に綺麗だった。身体は火照っているが、外気は冷たく、非常にすっきりする。サウナと水風呂のセッションに近い。サ道でいう「整う」感じ。

その後「信夫山・烏ヶ崎展望デッキ」へ。福島市内が一望できる、由緒ある山。山の中腹では何かしら工事が行われていた。除染作業だったのかも知れない。空間線量が書かれた看板があった。以前は線量が高かったらしい。山菜採りもできなくなったそうだ。

藁谷さんと籔内さんにコーヒーを淹れてもらい、地理を教わる。お年寄りの集まりが登ってきて、林檎やお菓子を分けてくれる。ビニール袋から丁寧にカットされた林檎を差し出しながら「検査してあっから大丈夫だよ〜」と笑う。

ホテルに戻ってチェックアウトを済ませ、OPTICAL YABUUCHIの3階でアプリの企画を提案。始まる前にコーヒーを淹れてもらう。これに影響を受けて帰ってからドリッパーを買った。反応は上々。アプリの方向性としては、いわゆるガイドブックではなく、僕達がしてもらったツアーをその人に合わせて組んでもらえるような仕組み。元々LIFEKUではコンシェルジュ的なサービスをしているので、それを楽にしたり、フィードバックを受けて改善につなげられるものにしようということになった。

東京の真似をするでもなく、のどかな土地でゆっくり豊かな時間を過ごせて良いですね、というのでもない。自分の周りにあるものやいる人で、できることをやっている。いいものを作る、伝える、それで生活をする。

年始に妻の実家を訪れた際、義父が天気予報の度に僕の実家の予報も見るようになったと言ってくれた。僕の両親も妻の実家の予報を見ているそうだ。

韓国のソウルに住んでいる友達、クロアチアのザグレブでお世話になった人、アメリカのニューハンプシャーでホストしてくれた家族。具体的な人の顔を浮かべられれば、その土地をリアルに感じられる。インターネット大好きっ子なので「現場」という言葉には注意してるのだけど、同じ釜の飯を食う、裸の付き合いをするといったことが、リアリティに大きく影響していることは間違いない。そういったものやことを増やしていくのが、ある時期は重苦しく感じられたが、今は楽しみになっている。

火曜日, 2月 03, 2015

iOS開発再入門 1

Dropophoneを作り直す(!)ので、iOS開発に再入門している。せっかくなのでSwiftを勉強しているが、今っぽい言語って感じで楽しい。

Safariにあるちょうど良さそうな書籍を調べつつ、Udacityで勉強している。XcodeやStoryboardの使い方を丁寧に教えてくれる上、音を録音し、加工して再生するアプリが作れるのが嬉しい。

公式のドキュメントも以前より分かりやすくなってる気がする。

  • Playgroundを使用するとすぐに実行結果が分かって良い。Terminalでxcrun swiftとタイプするとREPLも使える。
  • 変数の宣言をvar、定数の宣言をletで行う。ActionScriptみたいに:で型を指定できる。?をつけるとoptionalとなり、値がない(=nil)という値を保持できる。
  • 型安全なので、Stringの変数に間違ってIntを入れたりするとエラーになる。
  • 型推論により、型を明示しなくても初期化時に与えた値で自動的に判定してくれる。Haskellとかにある。
  • タプルが使える。Pythonとかにあって、複数の型の違う値を保持できる。

以前よりはプログラミングが分かるようになったし、聞ける人が周りにできたので、iTunesに寄せられている要望も受け入れられるようにしたい。