ラベル iphone の投稿を表示しています。 すべての投稿を表示
ラベル iphone の投稿を表示しています。 すべての投稿を表示

土曜日, 12月 24, 2016

C4を使う

動画再生とゲームっぽいものが必要な納期めちゃきつ案件があって、Core Graphicsでやろうとしたら円を動かすだけで苦労したのでC4を使ってみた。

ドキュメントは親切ではないけどサンプルやチュートリアルは結構ある。習うより慣れろ、見て盗めな感じがクリエイティブコーディングっぽい。

セットアップ

インストール方法はいくつかある。ターミナルを使ったが、手順はGit for Macの場合と同じ。

GitHubからcloneしてどこかに保存しておき、Xcodeプロジェクトで「Add Files to "プロジェクト名"」からC4iOS.xcodeprojを選び、TARGETSの「Embedded Binaries」に「C4.framework」を追加して(作成したプロジェクト配下のC4.frameworkiOSを選ぶ)ビルドする。

ViewControllerに「import C4」と書いてC4をインポートし、親クラスをCanvasControllerに変更すると使えるようになる。

viewDidLoad()とdidReceiveMemoryWarning()を削除し、setup()を作る。これが最初に呼ばれる関数になる。

import UIKit
import C4

class ViewController: CanvasController {

    override func setup() {
        let circle: Circle = Circle(center: canvas.center, radius: 100)
        canvas.add(circle)
    }

}

実機で試す際にC4IOS.xcodeprojのCode Signing Identityの設定でつまづいたがDebug、Release共に「Don't Code Sign」で「Any iOS SDK」を「iOS Developer」にしたら動かせた。

メモ

  • 作成した図形などはcanvasというプロパティにadd()で追加すると表示される。
  • Rectangleの角丸を取りたい場合はcornerにSize()を代入する。
  • 線(strokeColor)を消したい場合はclearを代入する。
  • CanvasControllerはUIViewController、CanvasViewはUIViewを継承してるので、通常のiOS開発の作法に従って書けるのがいい。
  • TextShapeで日本語が表示されなかったのでUILabelを使った。
  • Processingみたいにメインのループ関数があってその中で色々処理を書くというよりはFlash(Animate)に近い。
  • Processingみたいに一定間隔でアニメーションを実行したい場合はTimerクラスを使う。

何か作ったらまた書く。

金曜日, 4月 08, 2016

iOS開発再入門 5 AutoLayoutをプログラムで制御する

iOSアプリでUIを作る場合Storyboardを使うのが普通なんだけれど、プログラムから制御できると非常に助かる。音の数が増えたり、新たな要素が追加された場合に対応しやすいロジックを組んでおきたい。旧バージョンはすべての雨粒を線で繋いだ結果Xcodeが地獄の様相を呈した。レイアウトもLandscapeだったが、iPhoneがどんどん縦に伸びたこともあり、Portraitに変更した。
iOSのレイアウトシステムには、「Manual Layout」、「Auto Resizing(Spring and Struts)」と呼ばれる従来の方式に加え、多様なサイズの画面レイアウトに対応するための「Auto Layout」が採用されている。今回はAuto Layoutを使っていく。名前から想像すると色々と自動でやってくれそうな期待をしてしまうのだけど、全然そんなことはなくて(というか期待と違う結果になって)つまづきまくってDropophoneの開発が遅れた。

制約によるレイアウト

Auto LayoutはCassowaryというレイアウトシステムから発展した。制約(Constraints)をベースに、オブジェクト同士の関係を記述するルールを組んでいく。JavaScriptにも移植され、ブラウザで動くGrid Style Sheetsというプロジェクトもある。

Auto Layoutを使うか使わないかはViewごとに選択できる。プロパティ名がわかりにくい気がするが、Auto Layoutを適用したい要素に対してtranslatesAutoresizingMaskIntoConstraintsをfalseにする。

buttonA.translatesAutoresizingMaskIntoConstraints = false

関係を記述する手法として、VFLという言語が採用されている。CSSで要素を配置する場合は基本的にマークアップが強く影響するので、「ボタンAの右に30px空けてボタンBを置く」ということをコードで表現しにくい。

VFLだとこう書ける。

"V:|-[buttonA]-|"
"V:|-[buttonB]-|"
"H:|-[buttonA]-30-[buttonB]-|"

分かりやすい。アスキーアートっぽいのも面白い。"|"はSuperview(親View)、"-"はスペーシング、"[]"にはViewを記述する。Viewは変数名とインスタンスをまとめてDictionaryで渡す。

H(水平方向)の指定だけでなくV(垂直方向)の指定も必要。ボタンが横に2つ並ぶ場合など、カラムを取り扱うには同じような内容を繰り返すことになる。

let views: Dictionary = Dictionary(dictionaryLiteral:("buttonA", buttonA),("buttonB", buttonB))
let horizontalConstraints: [NSLayoutConstraint] = NSLayoutConstraint.constraintsWithVisualFormat(
  "H:|-[buttonA]-30-[buttonB]-|", options: [], metrics: nil, views: views)
let verticalConstraintsA: [NSLayoutConstraint] = NSLayoutConstraint.constraintsWithVisualFormat(
  "V:|-[buttonA]-|", options: [], metrics: nil, views: views)
let verticalConstraintsB: [NSLayoutConstraint] = NSLayoutConstraint.constraintsWithVisualFormat(
  "V:|-[buttonB]-|", options: [], metrics: nil, views: views)
        
view.addConstraints(horizontalConstraints)
view.addConstraints(verticalConstraintsA)
view.addConstraints(verticalConstraintsB)

上のコードを実行した画面がこれ。

確かにボタンAとボタンBの間は30px空いているのだけど、AとBのサイズは違うし、どう考えても縦に長過ぎる。レイアウトを詰めるには、ボタンのサイズやマージンなどを細かく指定していく必要があるのだが、どの設定が足りてなくてこの結果になっているのかが分からず、かなりややこしい。

VFLが解釈される前にViewの親子関係などが明らかになっている必要があるので、addSubViewなどの記述の順番には気をつける。Viewの初期化と制約の追加は関数を分けておいた方が良い。

最初は便利と思ったVFLだったが、全然理想通りにレイアウトできず完全に行き詰まってしまった。何か良い方法はないものかと調べていたら、Storyboard上で設定するようにaddConstraintで一つずつ設定していく手法を見つけた。基準となる要素を決めて、それに対してどのくらい離すとかを決めていく。これを覚えたら、イメージと実行結果の差がなくなり、一気に開発が進んだ。

view.addConstraint(
  NSLayoutConstraint(item: buttonA,
    attribute: .Top, relatedBy: .Equal, 
    toItem: view, attribute: .Top, 
    multiplier: 1.0, constant: 10
  )
)
view.addConstraint(
  NSLayoutConstraint(item: buttonA, 
    attribute: .Left, relatedBy: .Equal, 
    toItem: view, attribute: .Left, 
    multiplier: 1.0, constant: 10
  )
)
view.addConstraint(
  NSLayoutConstraint(item: buttonB,
    attribute: .Top, relatedBy: .Equal, 
    toItem: buttonA, attribute: .Top, 
    multiplier: 1.0, constant: 0
  )
)
view.addConstraint(
  NSLayoutConstraint(item: buttonB, 
    attribute: .Left, relatedBy: .Equal, 
    toItem: buttonA, attribute: .Right, 
    multiplier: 1.0, constant: 30
  )
)

buttonAのTopを親ViewのTopから10px、Leftを親ViewのLeftから10pxと設定し、buttonBはTopをbuttonAのTopと合わせ、LeftをbuttonAのRightから30pxと設定している。こうするとbuttonAのTopやLeftを変更した際に、buttonBがそのまま追従してくれる。

月曜日, 6月 29, 2015

iOS開発再入門 4 pd-for-iosをSwiftで使う

AudioKitやめる

AudioKit、忙しさにかまけているうちに2.1までバージョンが進み、軽い気持ちでアップデートしたらAPIが変わっていて、最新版に合わせて書き直したんだけど音が変に割れてしまうようになったので、使うのをやめた。

Pure Data for iOS

pd-for-iosはご存知Pure DataをiOSで使えるようにしたもので、Pure Dataの資産がそのままiOSで活かせるめちゃめちゃGame changingなライブラリ。デスクトップで使えるPd-extendedとは違うので、gateがないとか諸々注意する点はあるけれども、試した範囲ではパフォーマンスも問題なく、非常に便利。音の生成は全部Pure Dataに任せてしまって、XcodeではUIの部分だけ書けばいい。

  1. githubからcloneなりダウンロードなりで入手。
  2. Xcodeプロジェクトを作成(言語はSwift)し、プロジェクトファイルにFinderからlibpd.xcodeprojをドラッグ&ドロップ。
  3. プロジェクトファイルを選択し「TARGETS」の「Build Settings」にある「Search Paths」の「User Header Search Paths」にlibpdまでのパスを追加(検索窓で「user header」と入力すると早い)。$(SRCROOT)がプロジェクトファイルがあるフォルダを表す変数なので、そこから相対パスで指定するといい。ドロップダウンは「recursive」にする。
  4. 「Build Phases」にある「Link Binary With Libraries」の+ボタンを押して「libpd-ios.a」を追加(赤いままだが問題ないらしい)。

pd-for-iosはObjective-Cで書かれているので、Swiftで使うにはブリッジが必要。(プロジェクト名)-Bridging-Header.hというファイルを作成し、

#import "PdAudioController.h"
#import "PdDispatcher.h"
と書く。

「TARGETS」の「Build Settings」に「Objective-C Bridging Header」という項目があるので、ブリッジングヘッダファイルのパスを追記する。これ忘れがち。

正しく設定するとオブジェクト名やメソッド名がSwiftに変換された形で補完候補に出てくるようになる。enumがそのままだと受け取れないみたいだけど、いったん放置。

クラス変数として

var audioController: PdAudioController!
を宣言し、

AppDelegate.swiftの最初に呼ばれるメソッド内でサンプリングレートなどを設定。

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    self.audioController = PdAudioController()
    self.audioController.configureAmbient(withSampleRate: 44100, numberChannels: 2, mixingEnabled: true)
    
    return true
}

アプリがアクティブでなくなった場合と、

func applicationWillResignActive(application: UIApplication) {
    self.audioController.isActive = false
}
アクティブになった場合の状態を切り替える。
func applicationDidBecomeActive(application: UIApplication) {
    self.audioController.isActive = true
}

ViewController.swiftでは、viewDidLoadメソッド内などで

    let dispatcher = PdDispatcher()
    PdBase.setDelegate(dispatcher)
    PdBase.openFile("dropophone.pd", path: NSBundle.mainBundle().resourcePath)
    PdBase.sendBangToReceiver("start")
とすれば、pd側のstartという名前のreceiveオブジェクトでBangを受け取れる。

お互いのやり取りはここを参考にする。

「readsf~」などでサウンドファイルを読み込む場合は、Xcodeでのリソースパスに準じる。wavじゃないと駄目だったり、afconvertで作ったwavだとヘッダが不正とか言われて再生できなかったのでiTunesで作り直したりして、情弱な感じのはまり方をした。

Dropophoneの音を鳴らす仕組み自体はpdで簡単に実装できた。あとはレイアウトとアニメーションなんだけど、これがまた曲者。

参考

Making Musical Appsという本を読んだ。Safari Books Onlineにもある。

YouTubeでLearning libPD for iOSというチュートリアルがあって、親切。インストールの手順など多少違うので後で確認する。

水曜日, 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月 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に寄せられている要望も受け入れられるようにしたい。

水曜日, 6月 16, 2010

Dropophone


縁あってLullatoneと一緒にiPhoneアプリを作りました。
Raindrop Melody MakerのiPhone版という感じですね。音数を減らして選び直したり、マルチタッチができたり、色々と調整はしてあります。iTunesにリンクして音源が買えたり!

こういう感じでミュージシャンのアイディアを実現するシステムやソフトウェアを作る、ということをずっとやりたかったのですが、ようやく一つ目標を達成した感じです。今後の動向も楽しみにしていて下さい。

アプリのダウンロードはこちらから。
Dropophone for iPhone, iPod touch, and iPad on the iTunes App Store

LullatoneのDropophone紹介ページ。
Dropophone iPhone App « Lullatone