スペルミス

「SPRIT」じゃなくて「SPLIT」だよとご指摘いただきました。英語が苦手なのが思いっきり露呈してしまいましたねえ。Appleさんはスペルチェックはしてくれないようです。

すでに修正版は提出してますが、反映されるまでには数日かかると思います。いままでの経験からすると金曜(20日)じゃないでしょうか。一週間さらしものになるわけですね、お恥ずかしい。

さらにもうひとつの経験からすると、一ヶ月以上アップデートしない人っていうのも少なからずいるので、実質的なさらしもの期間はもっと長いんでしょうね。

時差

iTunes Connect上でのリリース日を、アプリのアップデートが公開された日に設定すると、AppStore上のリリース日もアップデートされた日に更新されるというのは開発者のみなさんなら周知の事実だと思います。ですが、今日2月14日にReady for saleが来た!と思って2月14日に設定したら、アメリカの人からUSのストアにアプリが無いよってメールが来ました。

日本やその他いくつかの国を見てもちゃんとあるのに、USに無いのはどうしてだろうと考えてみたら、アメリカはまだ2月13日だったんですね。自分の場合、審査がおわってReady for saleがくるのは日本時間の午前中なので、一つ前の日に設定するように気をつけなくちゃいけないなと思いました。

ところで、エリカさんの「iPhone デベロッパーズ クックブック」が届きました。先行発売されているのを立ち読みしたときから思っていたのですが、オーディオに関してはAudioUnitもOpenALもまったく触れられていないのが残念でした。いや、触れられていないどころか、全く存在しないような書き方です。逆に、Celestialっていう非公式のAPIを説明していたりして、かなりハックよりの内容ですね。

それより、AppleのiPhone用日本語ドキュメントにCore AudioとCore Animationに関する4つが追加されているようです。少なくともオーディオに関してはエリカ本は決して参考にせず、公式ドキュメントを参考にすることをお進めします。

Big StopWatch v2.0

Big StopWatchのバージョン2.0が公開されました。

今回の追加・変更点は、

・ラップ/スプリット機能の追加
・5秒前からのカウントダウン機能の追加
・ポートレイト表示の追加
・パフォーマンスの向上
・設定のSnapの名称をPower savingに変更

になります。

ただ、アップデートしても、そのままでは、リセットのときにアニメーションするようになったなぁ、としか思わないかもしれません。ラップやカウントダウンを使うには、設定アプリ内で「Advanced Mode」をオンにする必要があります。ポートレイトも設定アプリ内で設定できます。

ラップタイムの表示とかって、きっとみなさんが思っているのとは違う実装の仕方なんだろうなぁと思いますが、このアプリではこんな感じじゃないでしょうか。ふつうにリスト表示すると、なんというか壊れるので。まあ、Core Animation的な動きをOpenGLでやってみたかったってだけです。

Advanced Modeにするとデジタル時間の表示が60分でリセットされず、いつまでも計ることが出来ます。一応100年たっても大丈夫なはずです。ただ、1000時間を超えると10分の1秒までしか表示できません。

Snapは結局、Power savingにしちゃいました。動きを変えるのが目的ではなく、バッテリー消費を押さえるのが一番の目的なので。

このアプリは今後、バグフィックス以外でアップデートすることは無いと思います。タイマーの要望はいただくのですが、OSに常駐できないタイマーはなにかあると発動しなくなるので、作らないという判断をしています。

AudioSession その3 IOバッファサイズ

今回は、iPhoneオーディオのIOバッファサイズについてです。とりあえず、前回までのコードに、以下のようなsetIOBufferSizeメソッドを追加して、initにその呼び出しを加えてください。

- (void)setIOBufferSize
{
    Float64 sampleRate;
    UInt32 size = sizeof(sampleRate);
    AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareSampleRate, &size, &sampleRate);
    
    Float32 bufferSize = 256./sampleRate;
    size = sizeof(bufferSize);
    AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration, size, &bufferSize);
}
- (id) init
{
    self = [super init];
    if (self != nil) {
        [self setupAudioSession];
        [self setIOBufferSize];
        [self setupOutputUnit];
    }
    return self;
}

前回までのコードのコールバック内は、フレーム数に応じてサイン波の周波数がかわるようになっていましたので、変更を加えないときより高い音が再生されると思います。bufferSizeの256の所がバッファサイズのフレーム数ですので、そこを変更すれば任意のフレーム数に設定できます。コールバック内にprintfとかでinNumberFramesをログに出すようにすれば、ちゃんと変わっているのが分かると思います。

IOバッファサイズは秒数で指定しますので、今回のコードではハードウェアのサンプリング周波数を取得して、目的のフレーム数を割って値を求めています。プロパティの名前にPreferredってついているので指定した秒数の近いところに設定されるという感じでしょうか。Appleのサンプルでは、256に変更するときにちょっと少なめの0.005秒を指定していました。

ちなみにAudioSessionのプロパティのゲットは、AudioSessionがアクティブになっていないと出来ないようです。セットはいつでも大丈夫みたいです。Appleのサンプルをみると、プロパティをセットして、AudioSessionをアクティブにして、プロパティをゲットするというのがセオリーっぽいです。

IOバッファサイズはデフォルトの状態では1024フレームです。ロックしてスリープすると強制的に4096フレームになります。ただしOS 3.0では、今回のサンプルのようにAudioSessionSetPropertyでIOバッファサイズを一度設定すると、ロックしてもIOバッファサイズが変わらなくなったようです。また、カテゴリをPlayAndRecordなどにするだけでも変わりません。

Big StopWatch v1.1

Big StopWatchのv1.1が公開されました。今回はAppleの中の人とちょっとしたやり取りがあってからの公開という感じでした。「スナップモード」って何が違うの?って質問だけで、特にリジェクトになるという内容では無かったんですけど。

とりあえず、今回はバッテリー消費を抑えるだけのアップデートになります。v1.0だと何もしてなくても画面を更新していたりしたのですが、計測をストップしているときには更新しないようにしてます。短い時間を計測する使い方をする場合は効果があると思いますが、ずっと動かしっぱなしだと、まったく変わりません。

目盛り単位でカクカクと動くSnapモードを設定アプリ内で設定できるようになっています。これもアニメーションを少なくしてバッテリー消費を抑えるためです。ある意味こっちのほうがふつうのストップウォッチぽいですけど。

ちなみに、v2.0として出そうと思っているバージョンを作り始めています。ラップとか5秒前からのカウントダウンとか10年後も計り続けられるとか、機能的なものはだいたいできているのですが、一番困難を極めそうなのがポートレート表示です。ランドスケープ前提で作ってしまったので、縦にするといまいちバランスが悪いんですよねぇ。あんまり先の秒数も見えないですし。

Touch the Wave v0.5.4

Touch the Waveのv0.5.4が公開されました。今回の追加・修正点は、

・設定アプリでIOバッファサイズの設定が出来る
・非対応のAIFF・WAVファイルのダウンロードや再生を無視

という2点です。

iPod touch第2世代をご利用の方は、必ずIOバッファサイズを256に変更してください。1024だと今まで通りノイズが入ります。256にすることでノイズが入らないだけでなく、波形のスクロールも60fps近くのスムーズさになって、スクラッチのレスポンスも良くなってます。

逆に、iPhoneやiPod touch第1世代でご利用の方は、デフォルト状態の1024から変更しないでください。256にするとノイズが入ります。

本当はアプリ側で勝手に判断して切り替えたかったところですが、iPodにしろiPhoneにしろ、世代を判別する方法が見つからなかったので、しょうがなく環境設定という形にしました。

2つ目の方の修正ですが、v0.5.3までだと32ビットのファイルが非対応なのにダウンロードできてしまい、一度プレイリストで選択すると他のファイルも再生できなくなってしまうという状態でしたので、ダウンロード後のプレイリストの登録をしないようにして、既に登録されている非対応ファイルの再生も無視するようにしました。

ついでに、ダウンロード時のファイルの種類の判別や保存の仕方を内部的に変えています。自分で検証した限りでは問題ないと思うのですが、もし、以前はダウンロードできたのに出来なくなったという事がありましたらバグレポートをいただければと思います。

アプリのアップデート予定

Touch the WaveのiPod touch第2世代ノイズ対策版を提出してあります。特に問題なければ今週中にはアップされるんじゃないでしょうか。

ちょっと不本意ではあるのですが、設定アプリ内で「iPhoneまたはiPod touch第1世代」か「iPod touch第2世代」を選択してもらうという形になっています。できればユーザが設定しない形で解決したかったんですけど、世代の違いを判別する方法などが見つからなかったので、あきらめました。

Big StopWatchの方は、やっぱりバッテリーの消費が気になるので、うまいこと処理を軽減する方法を模索してます。ストップ中の画面更新を止めるだけでだいぶ違いますし。

レビューとか見てるとラップをつけて欲しいという要望が多かったりしますが、もともと積算さえしなくてもいいかなと思っていたくらいシンプルに保ってはいたかったので、少なくとも今の状態はシンプルモードで残しつつ、アドバンスドモードを追加していろいろ出来るようにするという方向で考えてはいます。

あ、あと数ヶ月先になると思いますが、マニアックなオーディオアプリ作ろうと思ってます。

AudioSession その2 AudioCategory

iPhoneのアプリでオーディオを再生・録音する場合に、iPodの音楽を鳴らしたままミックスして出したり、ロックした時にも音を出せるようにするには、AudioSessionSetProperty関数を使ってAudioCategoryというのを設定します。

AudioSessionSetPropertyはこんな感じで宣言されています。

extern OSStatus
AudioSessionSetProperty(AudioSessionPropertyID inID,
                        UInt32                 inDataSize,
                        const void             *inData)

AudioSessionSetPropertyの第一引数にはAudioSessionPropertyID、第二引数はプロパティのサイズ、第三引数には設定する値をポインタで渡します。CoreAudioではおなじみの構成です。

AudioSessionSetPropertyは色々とオーディオ関係の設定できますが、とりあえず今回はAudioCategoryの設定だけをやってみます。前回のコードのsetupAudioSessionメソッドを以下のように書き換えてください。

- (void)setupAudioSession
{
    AudioSessionInitialize(NULL, NULL, InterruptionListener, &outputUnit);
    
    UInt32 category = kAudioSessionCategory_AmbientSound;
    AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(category), &category);
    
    AudioSessionSetActive(true);
}

AudioCategoryのプロパティの型はUInt32と決まっていますので、その変数を作ってポインタで渡すという感じになります。プロパティの型の種類はものによってバラバラですが、ヘッダを辿るとコメントに書いてあります。

これで実行すると、iPodの音楽を鳴らした状態でアプリを起動しても音楽が止まらず、サイン波がミックスして再生されると思います。

プロパティのkAudioSessionCategory_AmbientSoundの部分をkAudioSessionCategory_MediaPlaybackに変えてみてアプリを実行すると、iPodの音楽はフェードアウトしてしまいますが、ロックしてスリープするときにサイン波は鳴りっぱなしになります(でも、このコードではピッチは下がってしまいますが)。

カテゴリの動作一覧はAudioSession Programing Guide内にありまして、日本語に置き換えてみるとこんな感じでしょうか。

PropertyID サイレントスイッチの効果(およびスクリーンロック時の消音) 他アプリのオーディオ再生 使用例
UserInterfaceSoundEffects ある する ユーザーインターフェースなど
AmbientSound ある する ゲーム効果音やバーチャル楽器など。iPodの音楽を再生しながら鳴らすもの
SoloAmbientSound ある しない デフォルト。ゲームサウンドなど。他アプリの音を再生しないもの
MediaPlayback ない しない オーディオファイル再生
LiveAudio ない しない ライブパフォーマンス系。バーチャル楽器など
RecordAudio ない しない 録音のみ
PlayAndRecord ない しない 再生と録音

何もカテゴリーを設定しないとSoloAmbientSoundになってしまいます。それと残念ながら、iPodの音楽を鳴らしながらスクリーンロック中に音を出すってことは出来ないみたいです。

※2009/10/7追記
OS3.0以降、MediaPlaybackなどのカテゴリでも、kAudioSessionProperty_OverrideCategoryMixWithOthersを使う事でiPodの音楽を流しながら音を出す事が出来るようになっています。また、LiveAudioとUserInterfaceSoundEffectsはDeprecatedになってます。

また、ちょっと分からない部分なんですが、RecordAudioにしていたからといって再生が出来なくなるという訳ではなかったりするので、結局のところAudioCategoryの設定は3パターンしかないようです。もし今の段階で違いが無かったとしても、今後のOSのバージョンアップで何か変化があるかもしれませんけど。

※追記 2009/1/25
AudioCategoryの種類によってルートチェンジのときに違いがあるみたいですね。それはまたちゃんと調べたらルートチェンジで1つエントリを書きます。

といったところでAudioCategoryはこの辺で。次回は、ずっと悩まされ続けてきたIOバッファあたりについて書いてみようかと思います。

BigStopWatch v1.0 公開しました

去年の7月にAppStoreへの提出を試すためにストップウォッチのアプリを作ってReady for Saleまで行っていたのですけど、あまりにも工夫も無くビジュアル的にもいまいちなやつだったので自らリジェクトしていまして、でもOpenGLもある程度使えるようになってきたのでゼロから作り直して公開しました。

bsw1.jpg

緑色の針が秒、赤色の針が分を指し示しています。一周はそれぞれ60秒と60分です。60分しか計れません。60分を過ぎると0分0秒に戻ります。

画面全体のどこかをタップするとスタートし、もう一回タップするとストップします。ストップしているときに右上に現れるRESETの文字をタップすれば0秒に戻り、それ以外の部分をタップするとそのまま続きを計測しはじめます。

一番の特徴は、画面下のアナログ的な表示をでっかくしているってことですね。もともとは画面にちょうど収まるように盤を表示していたのですが、0.1秒の目盛りなんて細かすぎて良くわからないので、こんな感じにしました。逆に右上に小さくして全体が見られるようにしています。

あと、Apple標準のストップウォッチでもそうなので当たり前ですが、ロックしようがアプリを終了しようが電源を切ろうが、それによって計測がリセットされるという事はありません。これは、計測開始のNSDateをユーザデフォルトにそのまま保存すれば簡単にできる事ですね。

味も素っ気も無い見た目のくせにアニメーションしまくってバッテリーの消耗が激しいアプリで、どんだけ実用的か分かりませんが、興味があればどうぞ。

ダウンロードは
こちら
から。