2017の前夜祭で「Objective-C++を使ってMRCで快適に開発する」という発表をしてきました。Objective-Cに関するCfpは少ないだろうから選ばれる可能性が高そうだったのと、マニアックな話題にすると前夜祭に選ばれて発表が終わった後にスッキリとした気持ちで本編を見ることができるだろうという見込みがピッタリはまって、前夜祭で発表することとなりました。「すごく参考になりました!早速使ってみます!」みたいなコメントは僕の知る限りひとつもいただけなかったのですが(それは当然)、すでにObjective-C++を使っている方が少なからずいらっしゃることもわかりましたし、MRCで書くかどうかはさておいてObjective-C++はパフォーマンスを追求するような分野では細々と使われていくのではないかなという印象です。発表の内容に関して興味のある方は、以下のスライドやサンプルコードをご参照ください。スライドhttps://speakerdeck.com/objectiveaudio/objective-c-plus-plus-woshi-tutemrcdekuai-shi-nikai-fa-suruサンプルコードhttps://github.com/objective-audio/objc_ptr_sample来年も開催されるようでしたら、またマニアックなネタで前夜祭を狙っていきたいと思います。yukiAVAudioEngine7件の返信iOS8からAVAudioEngineという新たなオーディオの機能がAVFoundationに追加されました。AVAudioEngineというのはAUGraphのObjective-C版のような感じのものです。オーディオのプレイヤーやエフェクトやミキサーなどがNodeと呼ばれる部品で用意されていて、それらを繋いで音を鳴らす事が出来ます。AUGraphよりもかなり簡単に使えるようになっているのですが、その反面、AUGraphを使うときのように細かい設定が出来るわけではなかったり、AUGraphで出来た事が出来なかったりする部分もあるので、いままでAUGraphを使っていた人にとっては物足りないかもしれません。そんなに複雑な事をやらないのであれば、簡単にエフェクトをかけられたりするので便利だと思います。AVAudioEngineに関してはWWDC2014でも解説されていますので、ちゃんとしたApple公式の解説を聞きたい・読みたいという方はそちらをご覧ください。Practice他だと、@shu223さんのiOS8-SamplerというgitリポジトリにもAVAudioEngineのサンプルコードが入っているようですので、参考にされると良いと思います。https://github.com/shu223/iOS8-Samplerサンプルコードでは簡単に、AVAudioEngineでオーディオファイルを鳴らして見たいと思います。Applicationなどで適当なiOSのプロジェクトを作ったら、ViewController.mを以下のコードに差し替えてください。completionHandler:nil];}@endまた、何かオーディオファイルをXcodeのプロジェクトに追加して”Rhythm.wav”と名前を付けてください。逆に、コード上の”Rhythm.wav”の部分を、用意したオーディオのファイルの名前に書き換えてもかまいません。拡張子はwavにしていますが、mp3でも何でも、iOSで普通にならせるオーディオのフォーマットであれば何でも構いません。オーディオファイルを追加したら、ビルドして実行すると1回オーディオファイルが再生されると思います。UIButtonとかをplay:メソッドに接続すればタップして何度も鳴らす事が出来ます。上記のコードでは以下の図のような構成でAudioEngineをセットアップしています。音を鳴らすだけではAVAudioEngineを使う意味が無いので、オーディオファイルの音にリバーブをかけて再生しています。セットアップの全体の流れは、「AudioEngineを生成」→「Nodeを生成」→「必要があればNodeのプロパティなどを設定(だいたい後でも可)」→「Node同士を接続」→「AudioEngineをスタート」という感じです。Nodeの接続がうまくいってない場合はAudioEngineをスタートする(startAndReturnError:メソッドを呼ぶ)時点でエラーが返ってきます。クラスAVFoundationにAVAudioEngine関連で追加されたクラスには以下のものがあります。AVAudioEngine(オーディオグラフ全体の管理)AVAudioNodeとそのサブクラス(グラフ内のオーディオの部品)AVAudioBuffer・AVAudioPCMBuffer(オーディオのバッファ)AVAudioFile(オーディオファイル)AVAudioFormat(オーディオのフォーマット。AudioStreamBasicDescription+αの情報)AVAudioTime(オーディオ時間。以前からAVFoundationにあったクラスと結構名前が似通ったものがあるので注意してください(AVAudioPlayerとAVAudioPlayerNodeや、AVAudioMixとAVAudioMixingなど)。あとAudioUnitEffectとそのサブクラスはAVAudioNodeのサブクラスではあるのですが、クラス名にNodeと付いていないのでちょっと分かりにくいかもしれません。ノード同士の接続Node同士を接続するにはconnect:to:format:メソッドを使います。「接続元のNode」と「接続先のNode」と「接続間のフォーマット」を引数に渡します。フォーマットに関しては、今回みたいにオーディオファイルを鳴らすならAVAudioFileのprocessingFormatにいい感じのフォーマットが入っているのでそれを渡せばOKです。そんなのが無いという場合はAVAudioFormatのinitStandardFormat〜という初期化メソッドがあるのでそれで生成すれば、いい感じのやつを作ってくれます。AVAudioEngineではAudioStreamBasicDescriptionの全要素を自分で埋める場面というのはほとんど無いと思います。セットアップ図にある「AVAudioMixerNode」と「AVAudioOutputNode」というのは最初から「mainMixerNode」と「outputNode」というプロパティでAVAudioEngineに用意されていて、なおかつmainMixerNodeとoutputNodeは最初から接続もされています。最初からAVAudioEngineに用意されているものはほかにも、inputNodeやmusicSequenceなどがあります。サンプルでは使っていませんがNodeのBusを指定して接続するメソッド(connect:to:fromBus:toBus:format:)もあって、ミキサーに複数接続する場合は別々のBusを指定して接続すれば同時に複数のNodeを繋げて鳴らす事が出来ます。オーディオプレイヤーオーディオファイルの再生には「AVAudioPlayerNode」というNodeを使っています。これはAVFoundationのAVAudioPlayerとは使い勝手が違って、AudioUnitのAudioFilePlayerがベースになっています。AVAudioPlayerのようにオーディオファイルのオブジェクトがあってそれを再生するというのではなく、プレイヤーがあって、そこにいろいろなオーディオファイルの鳴らしたいタイミングを登録していくという感じです。サンプルではatTimeのところの引数がnilなのですぐに鳴りますが、ここにAVAudioTimeで時間を指定すれば何秒後に鳴らすといったことが可能です。複数登録しておく事も出来ますが、ひとつのプレイヤーからは同時にひとつの音源しか鳴らせないので、複数のオーディオファイルを同時に鳴らしたいという場合はプレイヤーを複数作る必要があります。AVAudioPlayerNodeではオーディオファイルを直接ならす以外にも、AVAudioPCMBufferにオーディオデータを書き込んでおいて、そこから再生する事も出来ます。その場合にはループ再生をするオプションも指定できます。ボリュームAVAudioEngineの特徴であるのですが、ミキサーのインプット側のボリュームの設定はミキサーには無く、接続元の音源であるNode側にあります。ミキサーでボリューム調整ができるNodeはAVAudioMixingというプロトコルに準拠していてvolumeプロパティを持っています。ミキサーがインプットに接続されているNodeを辿っていって、AVAudioMixingに準拠しているNodeがあったらそのvolumeを見てレベル調整をしているようです。まとめAVAudioEngineの利点は、サンプルのコードを見てもらうとわかるとおり、ちょっとエフェクトをかけたいというときにはすごく少ないコード量で実現できるというところです。Swiftからも特に制限無くAVAudioEngineの機能にアクセスできるので、Swift推進派な人には良いかもしれません。逆に欠点としては、ProcessingTap的なものが無くて独自の処理をオーディオグラフの途中に挟み込めないということと、SplitterやConverterのNodeがなくて接続の自由度が低いということです。この辺りの機能が追加されるなら、完全にAUGraphからAVAudioEngineに乗り換えられると思うので、今後に期待したいですね。iPadのv2.0.3がリリースされました。yukimediaServiceWereResetNotificationが呼ばれるタイミングコメントを残すAVAudioSessionのmediaServiceWereResetNotificationですが、たまたま確実に呼ばれるタイミングを見つけられたのでメモしておきます。1、AudioSessionのCategoryをAVAudioSessionCategoryMultiRouteにする。2、iPad本体にヘッドホンを接続し、Lightning端子に外部出力を接続する。3、ヘッドホンとLightningケーブルを同時に素早く抜き取る。LightningやiPadと書きましたが、たぶんDockケーブルやiPhoneでもMultiRouteな状態からケーブルを一気に引き抜けば同じく呼ばれると思います。呼ばれたときにはAudioSessionがリセットされていたりしますので、オーディオ関連のオブジェクトの再生成とAudioSessionの再セットアップを行いましょう。QA1749なかなか普通はMultiRouteを使ったりはしないと思いますのでこのパターンで遭遇する事は無いと思います。他に呼ばれるタイミングをご存知な方がいらっしゃいましたら教えていただけるとうれしいです。STARとbigstopwatchをアップデートしました。両方ともiOS4くらいの時から放っていて、4インチのディスプレイに対応していなかったので対応しました。ついでに対応OSをiOS7以降に上げています。STARは、タップテンポの機能を追加しました。エディット画面のとき(メトロノーム音が停止中のとき)にテンポの文字あたりを連続でタップするとテンポが計れます。yukiWordPressへ移行しましたコメントを残すいままでObjective-AudioではMovableTypeを使っていたのですが、思う所ありましてWordpressに乗り換えました。パーマリンクを引き継いだりリダイレクトしたりと、リンクが切れないようにがんばったつもりですが、もしリンクが切れてしまった所がありましたらすみません。個別の記事に関しては無くなっているような事はないと思いますので、改めてお探しいただければ幸いです。今後ともどうぞよろしくお願いします。Audioの使い方をまとめておこうと思います。AudioとはUSBオーディオインターフェースやHDMIなどの外部出力を接続したときに、ヘッドホンへ別系統の音を出力できる機能です。外部出力2chとヘッドホン2chの計4ch出力する事が出来ます。残念ながらマルチイン・アウトが搭載されているオーディオインターフェースをマルチで使えるという機能ではなく、USBオーディオインターフェースはイン・アウト2chのみで、それとは別にヘッドホンにも別の音をだせるというだけの機能です。ちなみにUSBオーディオインターフェースの繋げ方を知らない方に説明しておきますと、カメラコネクションキットまたはUSBカメラアダプタを使ってUSBオーディオインターフェースを接続すると、内蔵スピーカーではなくオーディオインターフェースから音を出す事が出来ます。市販されているたいていのUSBオーディオインターフェースは対応しているはずです。USB接続に対応しているのはiPadのみで、HDMI接続ならiPhone・iPad両方大丈夫です。バスパワーで動作するオーディオインターフェースはほぼ電源供給が足りないので、電源付きのUSBハブを介して接続する必要があります。入力が2系統受けられる機能に関してはiOS5から搭載されているようですが、出力に関してはiOS6からの新機能です。今回のエントリでは出力のみ扱います。505でも解説されていますので、英語に抵抗無い方はApple純正の解説をご覧いただくのも良いと思います。音を出す前の準備として大まかに以下のような作業が必要になります。Audioが使えるようになります。実際に音を出すのには、RemoteIOを使う方法と、AVAudioPlayerを使う方法の2つがあります。基本的にRemoteIOの方法を解説します。AVAudioPlayerは個人的に使わないので簡単に触れる程度にします。AudioSessionのCategoryをMultiRouteにするAVAudioSessionのCategoryにAVAudioSessionCategoryMultiRouteをセットし、通常オーディオを鳴らす前の準備と同じくAudioSessionをアクティブにしてください。error:&error];RouteChangeの通知を受け取る通知センターからAVAudioSessionRouteChangeNotificationの通知を受け取るように登録します。これで、オーディオデバイスやヘッドホンを抜き差ししたときに通知が飛んできます。通知を受け取った後の処理}出力ポートの情報を取得する通知を受け取った後や、一番最初にセットアップをするときに出力ポートの情報を取得します。AVAudioSessionChannelDescription}AudioSessionのcurrentRouteから現在のオーディオ接続状態をAVAudioSessionRouteDescriptionで取得できます。そのAVAudioSessionRouteDescriptionのoutputsから現在の出力ポートの情報がAVAudioSessionPortDescriptionのNSArrayで取得できます。ポートというのは基本的に接続されている機器です。Audioであったり、ヘッドホンであったり内蔵スピーカーの事です。出力であればだいたい2chずつチャンネルを持っています。outputsが2つあればマルチで出力できるという事ですし、1つであれば1系統しか出力が無いという事です。AudioSessionでMultiRouteにCategoryがセットされていなければ、常に1つだけになります。CategoryがMultiRouteでかつマルチで出力できる接続があるときにoutputsが2つになります。AVAudioPlayerで出力を選択するには、AVAudioSessionPortDescriptionのchannelsで取得できるAVAudioSessionChannelDescriptionをchannelAssignmentsにセットすれば良いようです。Routeの状態であることが確認できたら、RemoteIOからどの出力へアサインするかをChannelMapで設定します。size);重要な注意点として、RemoteIOはひとつにつき2chしか出力する事が出来ません。Audioとヘッドホンの合計4chへバラバラの音を出力したい場合は、RemoteIOを2つ作成する必要があります。同じ音を複数の出力へ割り当てる事は出来ますので、外部出力とヘッドホン出力で左右を入れ替えるというだけだったり(そんなことはしないと思いますが...)、モノラルを2系統出力するという用途であればRemoteIOひとつでも大丈夫です。ChannelMapはUInt32型の配列で設定します。4chの出力がある状態ならば、要素数が4つの配列にRemoteIOのアウトのどのチャンネルを割り当てるのかを記述します。0と1にすればRemoteIOのそれぞれのチャンネルの音が出力されますが、-1にすれば出力されません。(符号無しの型なのに-1を記述するのはちょっと気持ち悪いですが、-1で設定するように指定されています)。MultiRouteの状態でなければChannelMapの設定は無視されるようです。ChannelMap配列の出力の順番は、AudioSessionのcurrentRouteのoutputsの順番と一致しています。接続するオーディオデバイスによって順番が変わったりしますので、ちゃんとポートの中身を判断して設定したり、ユーザーに設定をしてもらう必要があります。USBでしか試していないと「本線(USB)・ヘッドホン」の順番で固定だと思ってしまいますが、HDMIだと「ヘッドホン・本線(HDMI)」となるので、注意が必要です。データの保存コメントを残すUnityでストップウォッチを作るシリーズ半年ぶりの更新で、最後になります。今回はアプリを終了しても継続して計測できるようにデータを保存したいと思います。Stopwatch.csに以下のコードを追加してください。ChangeState関数に関しては、最後に1行だけの追加になります。保存したいデータは3つだけですので、簡単に保存が出来るPlayerPrefsを使っています。PlayerPrefsに保存できる型は「int」「float」「string」の3つだけです。Save()関数でデータの保存をしています。今回のコードで保存しているのはStopwatchクラスの変数の「state」「lastStopTime」および「startDateTime」の3つです。stateはintで大丈夫なのですが、他の2つはTicksというlong(リファレンスを見ると64bit)の値を保存したいので、intを使うのはちょっと不安です。文字列に変換して、stringで保存をしています。Load()関数では保存したデータから読み込みをしています。long.Parseで文字列から数値に変換していますが、このParse関数は文字列から数値を探せなかったときに例外を発生させます。今回のコードのように何もしていない状態で例外が飛んでくると問題が起きますので、念のためにtryで囲って例外をキャッチしたら無視するなり、エラーダイアログを出すなりした方がよいと思います。サンプルプロジェクトこれで最後という事で、プロジェクトをgithubにアップしてあります。iPadのv2.0.2がリリースされました。基本的に細かいバグ修正です。iPadのv2.0.1がリリースされました。変更点は、以下になります。Audioの問題というのは、出力を2つにわけるときにSplitterを使っていたのですけど、HDMIをつなげたときにデバイスが48kHz固定になってしまい、RemoteIOでサンプリングレートの変換が入って倍速かつ変な音になっていたものです。Splitter〜RemoteIOの間はデバイスのサンプリングレートと一緒にして、変換が必要な場合はSplitterの前に入れる事で解決しました。