2014年6月12日木曜日

AVAudioPlayerでハマった話。システム音も聞こえなくなった話など。

環境:iOS 7.1.1, XCode 5.1.1
状況:AVAudioPlayerがシステム音量ごと沈黙

AVAudioPlayerでBGMを鳴らそうとしていたところ、ある時からBGMの鳴るはずのタイミングで鳴らなくなってしまった。
しかもどうやらiOSの他の音声ごと無音になってしまっており、ボリュームボタンを押せどもサイレントスイッチを弄れども音は出ず。(画面上の表示は反応しています。)
実機だとイヤホンを抜き差しなどしてオーディオのルートを変更すると復帰する模様。
シミュレータだとアプリを落とすと復帰するけど、それまではMacのシステム音声も巻き込んで消える。
なにこれこわい。

結論から言うと AVAudioPlayer の volume プロパティの値が不正だったというオチ。

原因 :
AVAudioPlayer の volume プロパティにゼロ除算の結果の+INFを入れてしまった

直前にSpriteKitのバグ回避のためにオーディオセッションの設定を実装していたのでそのせいかと思い、新しく呼ぶようにした関数を全部潰しても治らない。
念のため既に実装を終わらせていたオーディオの再生部分を確認したら原因判明。
BGMのフェードインの実装の中、引数で与えられたdurationからフレーム毎の音量の増加値を計算している部分でゼロ除算してました。
その計算で引数が 0 の場合に +INF になった値をそのまま AVAudioPlayer の volume にイン。
結果、音量が振り切れてシステム音声ごとオーディオが落ちる状態に。
確かに同時期に「フェードイン無しの再生って duration が 0 のフェードインと同じだから云々・・・」とか自分で弄ってたの忘れてた・・・。
まぁともかく、原因が分かってよかったよかっt・・・。
よくない。

公式ドキュメントに「このプロパティは 0.0 から 1.0 までよ」って書いてあったけど。
今回の件、volume プロパティは inf だったから異常値として代入できてしまったのか、そもそもアクセサとかで検証はしてくれていないのか。
試しに値に「2.0」を入れてみる。
音が大きくなる。

・・・。(ゴクリ。)
よーし、ちょっと思い切って大きい値入れちゃおうかなー。具体的には INT32_MAX とか。

結果:音が割れる。

・・・おいィ?
認識できる数値だったらスケールの範囲に関係なく再生しちゃうのか。
例えば音量制限の延長で極端な値だとシステムがフェールセーフで落とすとかではない模様。
なにそれこわい。
MPVolumeView の volume プロパティは非推奨にしてるくせに、このおざなり感。

結論:バリデーションは自前で。

SKActionを使ったサウンドの再生でハマった話。割り込み後に再生しなくなるなど。

環境:iOS 7.1.1, XCode 5.1.1
状況:SKActionを使用した音声がシステム割り込みからの復帰後に再生しなくなる

SpriteKitを使ったアプリでSEの再生をSKActionのサウンド機能を使って再生していました。
音量調節が出来ないのが難点だけど実装が楽で実に有難い。
と思っていたら思わぬ罠が。
着信などのシステム割り込みからの復帰後にSEが再生しなくなる。
調べたら出てきました。

症状
http://stackoverflow.com/questions/22978547/handling-interruptions-in-sprite-kit-cant-get-sound-effects-via-skaction-pla
対処
http://iknowsomething.com/ios-sdk-spritekit-sound/

ま た バ グ か 。

なんか遭遇しすぎて逆に本当にSpriteKit自体のバグなのか、自分が何か見落としてるだけじゃないかと不安です。
(もう一個、別の件で詰まって調べたら「SpriteKitのバグっぽい」と言われてた事があったんですが失念。思い出したら書きます。)

ともかく動いてくれなきゃ困るので上記の対策を参考にさせて頂くことに。
SKActionがBlocksを実行できることを利用した実装なので、末端のコードを書きなおさなくていいのが嬉しい。
有り難や有り難や。
というか、コレがアリならもうSKActionはなんでもありですね。

ひとまず盲目的に実装したところ、アプリ起動中にコントロールセンターを引っ張りだすと deactivateAudioSession でエラーの際に再帰呼び出しをかけてるところでエラーがいつまでも解消せず、再帰が止まらなくなりタイムアウトで落ちる。
Audio Session Programming Guide には「割り込み発生時にはシステム側で AudioSession を停止する」って書いてあるし、特定のアプリ以外は明示的に停止する必要はないらしい。 今回は特に必要でもないので soptAudio 以外では deactivateAudioSession を呼ばないように変更。
とりあえず期待通りの挙動になりました。

Appleさん、新言語のついでに bug fixも早急にお願いします。 せっかくのフレームワークも結局既存の複雑なコードを呼び出すんじゃ意味ないっス(泣)