Hatena::ブログ(Diary)

Over&Out その後 このページをアンテナに追加 RSSフィード

2015-12-19

『Flying Tokyo #15 Rulr Workshop』受講メモ1日目 #rzmr_ws

Flying Tokyo #15 Rulr Workshop という Rhizomatiks Research 主催の2日間のワークショップを受講しています。本日1日目のメモ。


※アプリケーション自体はまだ公開じゃない気がするので、ダウンロードURL等は控えておきます。写真はOK(NGと明言されたもの以外)とのこと。


ワークショップ概要

http://www.rzm-research.com/rulr/ より)

Elliot氏について

「RULR」の開発者でありアーティストのElliot Woodsが来日し、講師をつとめます。アシスタントは画像認識、画像処理を組み合わせた作品を多く手がけるライゾマティクスリサーチのシニアエンジニア、花井裕也。二日間にわたり、インストールから実際のキャリブレーション、3Dスキャナの創作まで実践的な内容を行います。

RULRとは?

「RULR」は、カメラやキネクトなどのデバイスを用いて、リアルタイムの空間キャリブレーションを可能にする、オープンソースのツールキット。リアルタイムのグラフィックをopenFrameworksやVVVV、Processingなどを用いて生成し、物理空間との融合でインタラクティブなビジュアル体験を生み出すためのものです。例えば、キャリブレーションの結果を動く物体へのハイライトに反映したり、インスタレーションの訪問者の顔の3Dロケーションをカメラによって提供するなどの機能があります。

ちなみに「ルーラー」と発音されていました。


RULRインストール方法

Mac版はライゾマメンバーの方々が今回のワークショップにあたって作成したとのこと。たぶんまだ公開版ではないのでダウンロードリンク掲載は控えておきます。


アプリケーションの操作方法

Patch Panel
  • ノード以外のところをドラッグするとパッチパネルのスクロール
  • ズームは普通のmacの操作
  • ダブルクリックで新規ノード追加
  • ノードを繋ぐときは input pin をドラッグする
  • ノードの接続の解除は、input pin を右クリックする
World Panel
  • 普通にドラッグで回転
  • 右クリックしながらドラッグでズーム
  • Hキーを押しながらドラッグでカメラを上下左右に移動
  • Rキー長押しでリセット

カメラキャリブレーションの手順

準備
  • 1. Item::Camera ノードを追加
    • デバイスは Webcam を選択
    • Inspector Panelの "Clear device" でクリアできる
  • 2. Item::Board ノードを追加
    • 縦横のセル数とサイズを実際に使うボードを合わせる(デフォルト 10x7 0.05m)
  • 3. Procedure::Calibrate::CameraIntrinsics ノードを追加
  • 4. 3の input pin を1,2に繋ぐ
キャリブレーション
  • 5. 3を選択してアクティブにした状態で、カメラにチェッカーボードをかざす(全体が移るように)
  • 6. 認識すると赤線が出るので、スペースキーを押す
    • 「プニョ」的なSEが鳴る
    • →キャリブレーション結果が記録され、画面に粒々が出る
  • 7. 5,6をカメラの画角内のいろんな位置で最低20回ぐらい繰り返す
    • 回数は右のInspector Panelで確認できる
確認
  • 8. Demo::ARCube ノードを追加する
  • 9. 8の input pinを1,2に繋ぐ
  • 10. 8をアクティブにした状態で、カメラにチェッカーボードをかざすと、チェッカーボード上にキューブが表示される(チェッカーボードを動かしても追随する)

f:id:shu223:20151219195210j:image:w600


Coding基礎編

ここでoF実装の基礎編的な話。Rulr とは直接関係ない、oF/C++の一般的な話なので、割愛します。(個人的には普段oF/C++書いてないので大変ありがたかったです)


サンプルコードが下記にあります。


プロジェクタ & Kinect のキャリブレーション

準備
  • 1. Item::Kinect ノードを追加
  • 2. Item::Projector ノードを追加
  • 3. System::VideoOutput ノードを追加
  • 4. Procedure::Calibrate::ProjectorFromDepthCamera ノードを追加
  • 5. 4のinput pinを1,2,3に繋ぐ
キャリブレーション
  • 6. VideoOutput をアクティブにして、Inspector Panelの Show Window ボタン(or Return キー)を押す
    • → チェッカーボードがプロジェクタから投影される
  • 7. スクリーン(会場では壁)の位置、スクリーンより少し手前でそれぞれ Capture ボタン(or Space キー)を押す
  • 8. 最後に Return キー(キャリブレーション実行)
確認
  • 9. Render::NodeThroughView ノードを追加
  • 10. 9の input pin を1,2,3に繋ぐ
  • 11. 4の input pin と 3 との接続を解除しておく

f:id:shu223:20151219195301g:image


キャリブレーション情報のエクスポート

Item::Projector を選択した状態で、Inspector Panel の Export ofxRay ボタンをクリック。(デフォルトのファイル名は Projector.ofxRayCamera)


サンプル作成

Elliotさんが基本的にWindowsユーザーで、Macですぐに動くサンプルがなく、かといって全員が一から実装していたらワークショップが終わらないので、以下の手順でサンプルを作成。

  • ofxMultiKinectV2 の example_pointcloud を複製する

- パスの設定がおかしくなるので別フォルダに移動させない

  • ofxRay を追加する

- Elliot さんによるとここの作業で projectGenerator を使わないほうがいい、とのこと

- マニュアル追加してからsrcフォルダ以外をremove

  • [Build Settings] > [Header Search Path] を追加
../../../addons/ofxRay/src
  • ofApp.cpp を改変
#include "ofxRay.h"
ofxRay::Camera projector;
    • setup() の最後に以下を追加
auto result = ofSystemLoadDialog("Select ofxRay::Camera file for this projector");
if (!result.bSuccess) {
    ofExit();
}
ifstream filein(ofToDataPath(result.filePath), ios::binary | ios::in);
filein >> this->projector;
filein.close();
    • draw() の `ecam.begin()` 〜 `ecam.end()` の部分を ofxRay::Camera オブジェクトを使ったものに置き換える。
if (mesh.getVertices().size()) {
    ofPushStyle();
    glPointSize(2);
    
    projector.beginAsCamera();
    {
        ofDrawAxis(100);
        ofPushMatrix();
        ofScale(0.01, 0.01, 0.01);
        mesh.draw();
        ofPopMatrix();
    }
    projector.endAsCamera();
    
    ofPopStyle();
}

f:id:shu223:20151219195501j:image


所感


とつぶやいた通り、行く前は本当に恐れていたのですが、Elliotさん自身はたぶんそんなに初中級者を振り落とす感じではなく、「oFはさわったことある?」「ポインタは知ってる?」とひとつひとつ丁寧に教えてくれる姿勢だったし、作業に遅れが出る人がいてもライゾマの方々が総出でトラブルシューティングにあたってくれてました(Elliotさん自身も)。


ただ作業自体にはついていけたものの、普段Kinectやプロジェクタとあまり縁がないので、「キャリブレーション」と一口に言っても、もともとどういうズレがどの位あって、どの値を、何を基準に、どのようにキャリブレーションしているのか、といった本質的な部分がパッとイメージできず、そのあたりは結構両隣の受講者の方々に質問させていただきました(助かりました!)。でもこのあたりは一度自分でキャリブレーションなしで試して、キャリブレーションしないとどうにもならないわーということを体感しないとどうにも腹落ちはしないかな・・・とも思います。


個人的には明日の「3Dスキャナの創作」というパートに興味があるので、明日もついていけるようがんばります。


関連記事


2014-10-12

【oFセミナーメモ3】プロジェクションマッピング

デジタルアートセミナー#3 2日目の最初のセッションは、プロジェクションマッピングについて。


講師 : 藤本直明、他

openFrameworksでのプロジェクションマッピングの基礎を学び、実際に数名ずつのグループで簡単な制作を行います。 また、エッジブレンディングやメッシュワープなど、実践的なプロジェクションマッピングを行う上でのテクニックについても紹介します。


過去のセッションのレポートはこちら。


プロジェクションマッピングとは

プロジェクションマッピングじゃないもの・・・マッピングしてなくて、ただプロジェクションしてるだけのものとか

プロジェクションマッピング事例

3D Projection Mapping promoting The Tourist in Dallas - YouTube


マッピングソフト

ofxQuadWarp

シンプルなマッピングのアドオン。4つの頂点を動かすとその矩形に合わせて映像をwarp処理してくれる。


f:id:shu223:20141012125640j:image:w600

(頂点調整前)


f:id:shu223:20141012125651j:image:w600

(頂点調整後)


使い方
// 画像読み込み
img.loadImage("quad_warp_kittens.png");

// 位置とサイズを取得
int x = (ofGetWidth() - img.width) * 0.5;       // center on screen.
int y = (ofGetHeight() - img.height) * 0.5;     // center on screen.
int w = img.width;
int h = img.height;

// FBO確保
fbo.allocate(w, h);

// 画像のrectを ofxQuadWarp にセット
warper.setSourceRect(ofRectangle(0, 0, w, h));              // this is the source rectangle which is the size of the image and located at ( 0, 0 )

// 4つの頂点の初期座標を ofxQuadWarp にセット
warper.setTopLeftCornerPosition(ofPoint(x, y));             // this is position of the quad warp corners, centering the image on the screen.
warper.setTopRightCornerPosition(ofPoint(x + w, y));        // this is position of the quad warp corners, centering the image on the screen.
warper.setBottomLeftCornerPosition(ofPoint(x, y + h));      // this is position of the quad warp corners, centering the image on the screen.
warper.setBottomRightCornerPosition(ofPoint(x + w, y + h)); // this is position of the quad warp corners, centering the image on the screen.

// セットアップ&ロード
warper.setup();
warper.load(); // reload last saved changes.

Quad Warp事例

トラッキングしてプロジェクション

YouTube


講義資料: no title

車に再帰性反射材のマーカーを付けて、車の位置をトラッキングしながらその周りで映像が展開するという演出です。車のトラッキングには、6台のoptiTrackを15mの高さから吊り下げてセンシングをしています。


optiTrackとは赤外線による光学式モーションキャプチャで、複数台を連携させて高い精度とfpsでマーカーの位置が取得できるデバイスです。


車にマッピングしたくないので、車には白い光をあてて、プロジェクションを逃がしてやる。

再帰性反射材のマーカーを3つ載せて、OptiTrack でトラッキング

  • ラジコンでテスト
  • マーカー付きステッキでテスト

OptiTrackの処理が速く、プロジェクターの処理が追いつかない ので、進行方向にちょっと進ませた場所に投影(講義資料の、『レイテンシ補完』参照)


ジオラマに投影

https://www.youtube.com/watch?v=SRCVKciy-zI


複数のプロジェクタで投影する

エッジブレンディング

プロジェクタ間のキャリブレーション

Mac の [設定] > [ディスプレイ] > [カラー] > [補正]

ディスプレイキャリブレータ・アシスタントが起動する

薄目で林檎マークが溶け込むポイントで、全プロジェクタを調整する


メッシュワープ

Quad Warp みたいに4点だけじゃなくて、もっと多くのポイントでwarpできる・・・曲面への投影


https://www.youtube.com/watch?v=xqrJrqcqFBQ


  • 使用したoFアドオンは非公開
  • OpenGL の命令を直叩きで実装してある

MappaMok

  • カメラで撮影した画像と3Dモデルを合わせる
    • さまざまなパターンをプロジェクタから投影 → カメラで撮影

参考リンク: Kyle McDonaldの「光の演習」ワークショップ@SUPER FLYING TOKYOに参加してきました - Over&Out その後


2014-10-11

【oFセミナーメモ1】 boostライブラリの使い方

デジタルアートセミナー#3 openFrameworksで学ぶ、クリエイティブ・コーディング』という一泊二日のセミナーに参加しています。


最終的なまとめは最後に書くとして、とりいそぎ本日受けたセッションのメモを載せていきます。


セッション1 : C++テクニック

講師 : 堀口淳史、藤本直明

openFrameworksを本格的に使う上で避けて通れないC++のテクニックを学びます。

今回は、boostライブラリの使い方について学びます。


環境

  • MacOS X 10.9.5
  • Xcode 6.1 GMAIL.COM seed 2
  • oF osx 0.8.4
  • boost 1.56.0

boostとは

  • C++の高度で便利なライブラリ
    • STLを拡張
  • oFにpocoってのがもともと入っている
    • pocoとは設計思想が違う
    • boostはテンプレートを駆使
    • STLと違ってC++の開発環境に始めから入っていない
  • boostで書かれた過去の資産を利用できるようになる
  • ヘッダだけインクルードして使うライトな使い方もある
    • 正規表現とかはコンパイルしないと使えない
  • 自分でコンパイルしてMac環境で動かすのがなかなかハードルが高い

課題

  • oFは32ビットバイナリ、boostは普通にインストールすると64ビットバイナリ
  • oFとboostを同時に利用しようとすると、それぞれlibstdc++, libc++を使おうとしててこのあたりがリンクエラーとかの問題になる

「oFで動くMacのboostバイナリ」をどう作るか?


解決策

  • oFを64bitバイナリとしてコンパイルするのは簡単ではない

→ boost のコンパイル時に x86 を address-modelに `32_64` を指定する


  • oFはlibstdc++利用前提、libc++を利用するとコンパイルできない
  • boost はlibstdc++でもlibc++でもコンパイルできる

→ boost のコンパイル時に cxxflags と linflags に `-stdlib=libstdc++` を指定する


libstdc++とlibc++

  • libstdc++は GCCと共に開発される古くから使われている標準ライブラリ
    • GPLライセンス
  • libc++は LLVM/Clangと共に開発された新しい標準ライブラリ
    • MIT ライセンスと UIUC ライセンス

(2014.10.13追記)libstdc++のライセンスについては、コメント欄より下記のようにご指摘いただきました。

libstdc++のライセンスがGPLというのは少し誤解を招いてしまいかねないです。

というのも、libstdc++は特別な条項(GCC Runtime Library Exception)が追加されているからです。これにより、libstdc++を使用するアプリケーションを作成しても、それを公開する際にGPLを適用する必要がありません。その点で、通常のGPLとは大きく異なります。


oFで手っ取り早く使う

  • poco/include/ 配下に boost フォルダのヘッダを丸ごとつっこんじゃえば、パスが通ってるので、ヘッダだけならすぐに使えるようになる(行儀悪い)
  • shared pointer とかはそのまま使える

スマートポインタ

普通のポインタ
ofImage * mTestImage;
mTestImage = new ofImage( "test.jpg" );
delete mTestImage;

new したら delete が必要。


スマートポインタの場合(ofPtr は oF のスマートポインタ)

ofPtr < ofImage > mTestImageSP;
mTestImage = ofPtr< ofImage >( new ofImage( "test.jpg" ) );

delete不要。


boostだと、

boost::shared_ptr< ofImage >

って感じでスマートポインタを使える


スマートポインタに NULL 代入はできないので、

mTestImageSP.reset();

で内部でデストラクタが呼ばれて NULL と同じ状態になる。( `if (mTestImageSP)` でfalseになる)


boostインストール

https://github.com/toolbits/boost_1_56_0_xcode610_universal_binary

boost_libstdc++.dmgを解凍

  • ヘッダだけを使う場合は、includeのフォルダをパス通ってるとこにコピーする
  • libはコンパイル済みのバイナリ
    • Xcode6, MacOS X 10.9で動くようにコンパイルしたもの
  • 一番よく入れるのが、`/usr/local`
    • OS標準以外のあとから追加したライブラリとかを置く場所として(macでは)よく使われる
cd /user/local
open .

この配下にコピー


サンプルプロジェクト

サンプルを動かしつつ、boostの機能を紹介。

https://github.com/toolbits/of_plus_boost_2014seminar


サンプル1: boosted

両端のスペースをカットする文字列処理。ヘッダの機能だけ使用。

boost::algorithm::trim()
void ofApp::setup(){
    std::string str;
    
    str = " Hello boost ";
    std::cout << str << std::endl;
    
    boost::algorithm::trim(str);
    std::cout << str << std::endl;
}

サンプル2: boost_algorithm

文字列処理のサンプル。これらもヘッダだけで可能。


両端の空白を削除

boost::algorithm::trim(str);

カンマで文字列を分割

boost::algorithm::split(vec, str, boost::is_any_of(","));
for (it = vec.begin(); it != vec.end(); ++it) {
    std::cout << *it << std::endl;
}

分割した各文字列の両端の空白を削除

for (it = vec.begin(); it != vec.end(); ++it) {
    boost::algorithm::trim(*it);
}
for (it = vec.begin(); it != vec.end(); ++it) {
    std::cout << *it << std::endl;
}

各文字列を|を区切りにして連結

str = boost::algorithm::join(vec, "|");

文字列の置き換え

boost::algorithm::replace_all(str, "|", " / ");

文字列を置き換えた結果を返す

str = boost::algorithm::replace_first_copy(std::string("C++ source code"), "C++", "boooooooooooooooost");

サンプル3: boost_regex

正規表現のサンプル。これは要バイナリ。このサンプルが動けばboostの全機能が使えるということ。

※プロジェクトに libboost_regex.a が追加されている


boost::regex regex("[^/]+?\\.o$");
boost::match_results<std::string::const_iterator> result;

見つかった項目をすべて表示

std::string::const_iterator bit;
std::string::const_iterator eit;
bit = str.begin();
eit = str.end();
while (boost::regex_search(bit, eit, result, regex)) {
    std::cout << "match = " << result.str() << std::endl;
    bit = result[0].second;
}

見つかった項目をすべて置換

str = boost::regex_replace(str, regex, "********.o");

※ofUtilsに同様の文字列処理機能もあるので一度眺めておくと良い


サンプル4: boost_format_lexical_cast

数値を文字列にキャスト。ヘッダだけでOK。


番号で指定された通りに値を文字列化

str = (boost::format("%1% %2% %3%") % 1 % "abc" % 3.14).str();

printf のフォーマット文も利用可能

str = (boost::format("%06X (hex)") % 12648430).str();
str = (boost::format("%d (dec)") % 0xDEADBEEF).str();

lexical_cast を利用した整数から文字列への変換

str = boost::lexical_cast<std::string>(141421356);
std::cout << str << " (string)" << std::endl;
std::cout << "---- ---- ---- ----" << std::endl;

lexical_cast を利用した文字列から整数への変換

ival = boost::lexical_cast<int>(str);

lexical_cast を利用した文字列から実数への変換

str = "0.12345";
dval = boost::lexical_cast<double>(str);

lexical_cast を利用した不正な文字列から実数への変換

str = "0.12???";
try {
    dval = boost::lexical_cast<double>(str);
}
catch (boost::bad_lexical_cast& e) {
    dval = NAN;
}

サンプル5: boost_thread

スレッドをつくる

  • libboost_system.a
  • libboost_thread.a

サンプル6: boost_mutex

変数へのアクセスを排他にするためのmutex

  • libboost_system.a
  • libboost_thread.a
boost::mutex _mutex;
// カウント変数を増加
_mutex.lock();
++_count;
_mutex.unlock();
// カウント値を取得
_mutex.lock();
count = _count;
_mutex.unlock();
// lock() / unlock() の替わりに lock_guard を利用しても同じ
boost::lock_guard<boost::mutex> guard(_mutex);

// カウント変数を減少
--_count;

サンプル7: boost_lock

shared_mutex を使うと、マルチスレッド処理において、read/writeをいい感じにロックしてくれる。

boost::shared_lock<boost::shared_mutex> rlock(_mutex);

書き込みロック(unique_lock)は1つのスレッドからだけ取れる

// カウント変数を増加
{
    boost::unique_lock<boost::shared_mutex> wlock(_mutex);
    
    ++_count;
}

読みこみロック(shared_lock)は何スレッドからでもとれる

boost::shared_lock<boost::shared_mutex> rlock(_mutex);
ofColor color;

// カウント値を表示
color.setHsb(abs(_count) % 255, 255, 255);
ofBackground(color);
ofDrawBitmapString((boost::format("%1%") % _count).str(), 10, 50);

2014-06-13

Markdownでプレゼン用スライドをつくる方法いろいろ

明日は 「yidev第十五回勉強会」、来週はクックパッドさん主催の 「WWDC2014 振り返り勉強会」で発表をさせていただく *1 のですが、まずMarkdownで話そうと思うことを適当にメモしてるうちに、なんか発表資料このままで良くない?と思えてきました。別にレイアウトや配色のセンスに自信があるわけでもなし


で、最初 "markdown pagination" でググってみるとあまりパッとしたのがなかったのですが、"markdown presentation" にしてみると出るわ出るわ。しかもどれも結構よさげ。


というわけで出てきたものをメモがてら書き留めておきます。結論からいうとググって一番上に出てきた "Remark" が今回の要件に(ほぼ)マッチしていたので、他はまだ使ってません。


Remark

http://remarkjs.com/

  • GitHubでソース公開されてて気軽に使える
    • 無料
    • ログイン不要
    • 不満があれば自分で修正してpull request送れる
  • Markdown が html ファイル内にあるので、Markdown エディタで編集しづらい

Swipe

http://beta.swipe.to/markdown/

  • テンプレートやフォントが複数用意されてる(っぽい)
  • 要ログイン
    • スライドを公開したい場合には長所でもある

Docset

http://www.decksetapp.com/

  • Mac アプリ
  • 19.99ドル

Reveal.js

https://github.com/hakimel/reveal.js/

  • HTMLやMarkdown等、テキストベースのスライドをつくれる
  • たぶんこの中では一番メジャー。日本語での紹介記事もたくさん出てる

Slidify

http://slidify.org/

  • サイトが10ブックマークされてるので国内でちょっと話題になったことがある?

*1:発表内容は使い回しではなく、どちらも全く別々の話をする予定です

2014-05-06

Audio Unit 再入門

Core Audio においてもっとも低レベルに位置する Audio Unit。リアルタイムで高度なオーディオ波形処理を行いたい場合や複雑なルーティングによるオーディオ処理を実現したい場合、これを使用する必要が出てきます。


が、このフレームワーク、個人的には使用頻度があまり高くない *1 ので、ひさびさに触ってみた際にとっつきにくさを感じました。


慣れてしまえば 全体的なコンセプトはシンプル なのですが、関数の引数がやたら多かったり、構造体の要素がやたら多かったり、慣れてないC言語APIだったりするので、久しぶりに触るとそのあたりが複雑に感じてしまうのかなと。


そんなわけで、次に久しぶりに Audio Unit をいじるときに、 そのあたりの「シンプルな全体感」と、「複雑に感じてしまう部分」を切り分けて見ることができるよう、メモっておきます。


基本的な考え方

Audio Unit の基本コンセプトは、「いろいろなユニットを複数接続し、オーディオ処理を実現する」というもの。


で、そのひとつひとつのユニットがノード(AUNode)、それらが繋がっている全体がグラフ(AUGraph)。この考え方や呼称は直観的にもわかりやすいです。


そして実際の実装手順としても、大まかにいうと下記のように非常にわかりやすいです。この流れさえ把握しておけば、後述する「一見複雑そう」な諸々が出てきても全体感は見失わずにすむかと。


基本的な実装の流れ

1. グラフ(AUGraph)作成

NewAUGraph(&graph);
AUGraphOpen(graph);

2. ノードをグラフに追加する

AUGraphAddNode(graph,
               &ioUnitDescription,
               &ioNode);

3. ノードを接続する

AUGraphConnectNodeInput(graph, ioNode, 1, ioNode, 0);

4. グラフを初期化して処理開始

AUGraphInitialize(graph);
AUGraphStart(graph);


で、これに、下記要素が絡んできます。

  • AudioComponentDescription
  • Audio Unit のプロパティ設定
  • AudioStreamBasicDescription (ASBD)
  • コールバック
  • Audio Converter Sevices や Extended Audio File Services 等の関連サービス

このあたりが絡んでくることによって、コードも長く複雑になり、オーディオフォーマットC言語の知識も必要になってきて、そのあたりの知識の乏しい自分が久々に Audio Unit に触るとうわー難しいってなるのかなと。そんなわけで、整理のため以下にひとつひとつ紐解いておきます。


AudioComponentDescription

前述した基本手順の「ノードをグラフに追加する」手順において、 「どのユニットをノードとして追加するか」 を指定するものが AudioComponentDescription という構造体。


たとえば Remote IO ユニットの場合は下記のようになります。

AudioComponentDescription cd;
cd.componentType            = kAudioUnitType_Output;
cd.componentSubType         = kAudioUnitSubType_RemoteIO;
cd.componentManufacturer    = kAudioUnitManufacturer_Apple;
cd.componentFlags           = 0;
cd.componentFlagsMask       = 0;

AUGraphAddNode(graph, &cd, &ioNode);

こうやって書いてしまうと全然難しいものではないのですが、構造体の要素の数が多く、各定義名も長いので、パッと見のコードの複雑さを助長している気がします。


そんなわけで TTMAudioUnitHelper という Audio Unit のヘルパーライブラリでは、下記のように サブタイプだけ指定すれば AudioComponentDescription を取得できる ラッパーメソッドを用意してあります *2

+ (AudioComponentDescription)audioComponentDescriptionForSubType:(OSType)subType;

Audio Unit のプロパティ設定

グラフに追加した各ノード(のユニット)にプロパティをセットするには、まずノードから AudioUnit 構造体を取得します。

AudioUnit ioUnit;
AUGraphNodeInfo(graph, ioNode, &cd, &ioUnit);

で、取得した AudioUnit 構造体を引数に渡しつつ、プロパティをセットします。

AudioUnitSetProperty(ioUnit,
                     kAudioOutputUnitProperty_EnableIO,
                     kAudioUnitScope_Input,
                     1,
                     &flag,
                     sizeof(flag));

引数が多いですが、関数の定義を見ればわかるかと。

AudioUnitSetProperty(AudioUnit              inUnit,
                    AudioUnitPropertyID     inID,
                    AudioUnitScope          inScope,
                    AudioUnitElement        inElement,
                    const void *            inData,
                    UInt32                  inDataSize)

プロパティのgetも同様です。

AudioUnitGetProperty(AudioUnit              inUnit,
                    AudioUnitPropertyID     inID,
                    AudioUnitScope          inScope,
                    AudioUnitElement        inElement,
                    void *                  outData,
                    UInt32 *                ioDataSize);

プロパティを set / get する実装は、引数が多いので何となく難しい感じに見えてしまうところがありますが、上記の通りやってることはシンプルです。


AudioStreamBasicDescription (ASBD)

オーディオデータフォーマットを表現するための構造体。

struct AudioStreamBasicDescription
{
    Float64 mSampleRate;
    UInt32  mFormatID;
    UInt32  mFormatFlags;
    UInt32  mBytesPerPacket;
    UInt32  mFramesPerPacket;
    UInt32  mBytesPerFrame;
    UInt32  mChannelsPerFrame;
    UInt32  mBitsPerChannel;
    UInt32  mReserved;
};
typedef struct AudioStreamBasicDescription  AudioStreamBasicDescription;

ユニットごとにサポートしているオーディオデータフォーマットが違うため、ノード間で滞りなくオーディオデータを流すためにこれをプロパティからセットしてやる必要があります。


この ASBD、以下の点を個人的に整理できてないので、また別途記事を書こうと思っています。

  • どのノード間において明示的に get / set する必要があるのか
  • どのノード間において AUConverter ユニットや後述する Audio Converter Sevices で ASBD を変換する必要があるのか
  • ASBD が合ってなければ AUGraphInitialize 実行時にエラーを返してくれる?

コールバック

再生するにしても録音するにしてもこのコールバックの実装は不可欠だし、リアルタイム波形処理もここで行うことになるので、Audio Unit のキモになる部分といえます。が、下記理由により個人的にはややこしく感じてしまいます。

  • いろいろなコールバックの登録方法がある
  • いろいろなコールバックの種類がある
    • 後述する Audio Converter Sevices もコールバック内で処理を行う
  • 引数それぞれの役割を把握してないとAudioUnitのキモである波形処理を書けない
  • C言語的な知識がしっかりと要求される

ここでは、コールバックの何がややこしいのか、という把握だけにとどめておいて、コールバックの詳しい話は別記事で行いたいと思います。


Audio Converter Sevices や Extended Audio File Services 等の関連サービス

サウンドファイルの再生は、AVAudioPlayer を使用すれば恐ろしく簡単にできるのですが、Audio Unit を使う場合、オーディオデータを RemoteIO で再生できるフォーマットに変換するために、Audio Converter Sevices や Extended Audio File Services を利用する必要があります。


高レベルAPIに慣れてしまった僕のようなゆとりiOSエンジニアからすると「たかがファイル再生」と油断しているところに、Audio Converter Sevices では変換用コールバックが再生用とは別途必要だったり、マジッククッキーなるよくわからない概念が登場したりするので、「Audio Unitこわい」という印象を持ってしまう要因のひとつになってしまっている気がします。


また Audio Converter Sevices を使うにしろ Extended Audio File Services を使うにしろ、この手の実装はファイルから読み込んだオーディオデータを保持しておくために独自の構造体を定義して取り扱うことが多く、他人のサンプルコードを参考にしようとしても、「パッと把握しづらい」というつらさもあります。


このあたり、AUConverter ユニットや AUAudioFilePlayer ユニットを使用すればもっとシンプルにできるのかなと期待しつつ、また別途記事を書こうと思います。


参考記事

Audio Unit を含む、iOSのオーディオ処理に役立つ参考書籍を下記記事にまとめています。


Audio Unit のユニット種別は AudioComponentDescription 構造体の componentSubType によって規定されますが、その一覧を下記記事にまとめています。


参考になるサンプルコードのまとめ。使用されているユニットも付記してあるので手前味噌ながら便利です。

*1:自分の場合は1年3ヵ月ぶり2回目

*2:まだ全てのサブタイプをサポートしていませんし、オプションは考慮できていません。pull request大歓迎です。

2009 | 08 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2012 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2013 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2014 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2015 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2016 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 11 | 12 |
2017 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2018 | 02 |