2013年12月30日月曜日

Logcatでスペースを含む文字列でフィルタする

Logcatのフィルタ入力欄において、スペースはAND(論理積)としてみなされます。
スペースを指定したい時は\sを使いましょう。

例:
Log.d("hoge", "text A" );
Log.d("hoge", "text B" );

というログを両方ヒットさせたい時

text A|text B
text\sA|text\sB


前者の場合、
"text"と、Aまたは"text"と、Bを含む文字列がフィルタされます。


2013年10月28日月曜日

OpenSL for Android で困ってること

OpenSL for Androidで困ってることをいくつか。
全部、デコードするときの問題です。直接再生する時は違うのかもしれない。

1.VBRのmp3ファイルので、シークが正確でない。
VBRのmp3ファイルのシークの精度が悪い。平気で数秒程度ずれる。
わかりやすい例であh、曲の終わりにシークさせて再生させても、平気で曲長を越えて2~3秒くらいは再生する。
OpenSLを使ってる他のプレーヤーアプリでも同じ現象が確認できるから実装の問題ではないと思う。

対策は・・・今のところみつかってない。
英語で検索してもそれらしい事を言っている人を一人も見ないのは、大した問題では無いからなのだろう。自分が作っているアプリの場合は、これが致命的になるケースがあるので困っているのだが。

2.Galaxyシリーズでのモノラル音声の再生が怪しい。
どう怪しいかというと、普通の端末ではモノラルサウンドでは1チャンネル分がデコードされるのだが、一部のGalaxy端末では、2チャンネル分のPCMが出力される(サンプリングレートが倍になってるのと等価)。
結果的に半分の速度でスロー再生しているような音声が再生されてしまう。
困ったことに全てのGalaxy端末で発生するわけではない。

解決策としては、モノラルのサンプルデータを仕込んでおいて、アプリの初回起動時にデコードサイズが想定されるサイズになるか、倍のサイズになるかを調べれば良い。
倍のサイズになった場合は、常にステレオ音声として再生させれば良い。

これでOK.

・・・・と、思うじゃないですか。
実際にこれでGalaxyS3ではこれで回避できたし、
友人のGalaxyS2( 日本で最新の4.0.3 )でもこれで回避できた。
が、4.0.4なGalaxyS2をお持ちの海外ユーザーさんからは相変わらず直らないとの声。
手動でステレオモードに切り替えてもらったら正常に再生されたそうなのだが、ログを送ってもらうと、デコードサイズは通常サイズ。
正直何がなんだかよくわからない。

4.0.4なGalaxyS2が手に入ったらもう少し追ってみたい。
か、誰か答え知ってたら教えて下さい。

2013年9月3日火曜日

「サーバーへの安定したデータ接続を確立できません」が出て困った

久々に起動したAndroid端末でGoogleアカウントを追加しようとしたら

サーバーへの安定したデータ接続を確立できません

という表示が出てアカウントの追加ができない状態に・・。
いろいろ試した結果、原因は単純に時計がずれているせいでした。
(久々に起動したもんだから時計が1980年1月1日に戻っていたのでした)
時刻を修正して試したらアカウントの追加ができた。

2013年8月30日金曜日

AndroidでのOpenSLの利用

AndroidでOpenSLを利用する際見ておくべき情報などメモ

1.公式ドキュメント

NDKインストール時に含まれているもの。
パスは<NDK-PATH>/docs/opensles/
で、内容は下記の2つ

- OpenSL_ES_Specification_1.0.1.pdf
     OpenSL ESの仕様書。これはAndroidに特化したものではない。
  なので、ここにあるもの全てが使えるわけではないので、その辺りは下記index.htmlを確認する必要がある。

 - index.html
     OpenSLのAndroid実装についての解説。
     webでも見ることができる→OpenSL ES for Android

    

2.サンプル

 - 上記OpenSL_ES_Specification_1.0.1.pdfの最後の方
   再生や録音の基本的なサンプルがある。

   →日本語訳
    これはサンプルというよりコードを交えた解説。日本語の情報としては一番内容が濃いと思う。

 PCMデータにデコードするコードが「動かないもの」として載ってる。
 読めばわかるように、バッファを2つEnqueueしてやれば動く
 こんな感じ↓
// enqueue a buffer and start playing
    CK_SL_VERIFY( (*g_playerBufferQueue)->Enqueue(g_playerBufferQueue, g_buf, k_bufSamples * sizeof(int16)) );
    CK_SL_VERIFY( (*g_playerPlay)->SetPlayState(g_playerPlay, SL_PLAYSTATE_PLAYING) );
// enqueue a buffer and start playing
    CK_SL_VERIFY( (*g_playerBufferQueue)->Enqueue(g_playerBufferQueue, g_buf, k_bufSamples * sizeof(int16)) );
    CK_SL_VERIFY( (*g_playerBufferQueue)->Enqueue(g_playerBufferQueue, g_buf, k_bufSamples * sizeof(int16)) );
    CK_SL_VERIFY( (*g_playerPlay)->SetPlayState(g_playerPlay, SL_PLAYSTATE_PLAYING) );

必要に応じてバッファを変えてあげて下さい。


エラー:Androidでのundefined reference to 'foo()'への対処

AndroidのNDKを使った開発で、undefined reference to 'foo()'が出た場合の対処例のメモ。

1.android.mkのLOCAL_SRC_FILESにfooを宣言しているファイル(例えばfoo.c)があることを確認

LOCAL_SRC_FILES := foo.c


2.(fooがcppファイルで宣言されていて、cファイルからの参照するときにエラーが出る場合のみ) foo()を宣言しているヘッダがextern "C"{}で囲われていることを確認(includeガードをしている場合は最初のincludeでこうなっていることを確認)

extern "C" {
#include "foo.h"
}

3.(別のライブラリとしてビルドしている場合のみ)android.mkのライブラリー関連の記述が正しいか確認

以下のようにfoo.cを含むライブラリーを静的ライブラリとしている場合は、
LOCAL_MODULE := bar
LOCAL_SRC_FILES := foo.c
include $(BUILD_STATIC_LIBRARY)

呼び出し側のライブラリの記述に以下を追加
LOCAL_STATIC_LIBRARIES := bar

共有ライブラリとしている場合( include $(BUILD_SHARED_LIBRARY) )は
LOCAL_SHARED_LIBRARIES := bar



2013年6月30日日曜日

ActivityUnitTestCaseでjava.lang.RuntimeException:

以下のようなコードを書いた
public class SampleTest extends ActivityUnitTestCase<MainActivity> {

 public SampleTest (
   Class<MainActivity> activityClass) {
  super(activityClass);
 }
}
このコードだと以下のようなエラーを吐いて、テストが始まる前に落ちる。
このコンストラクタは、1行目だけ書いた段階でEclipseのQuick Fixで生成されたコンストラクタ。でもこれだと動かない。
java.lang.RuntimeException: Exception during suite construction at android.test.suitebuilder.TestSuiteBuilder$FailedToCreateTests.testSuiteConstructionFailed(TestSuiteBuilder.java:239)

解決策はパラメータの無いコンストラクタを用意すること。


public class SampleTest extends ActivityUnitTestCase<MainActivity> {
 public SampleTest ( ) {
  super(MainActivity.class);
 }

}

これで動く。

2013年6月28日金曜日

ViewPagerのスライド速度変更

ViewPagerをフリックした時の画面の切り替わりが遅いと、縦方向のスクロールビュー(リストビュー等)と組み合わせると相性が悪い。
フリックした直後に縦にスクロールしようとしても、スワイプ動作がViewPagerの方に取られてしまうからだ。

スライドのアニメーションの速度を上げればこの問題は解決する。
やり方は下記に書いてあった。
http://stackoverflow.com/questions/8155257/slowing-speed-of-viewpager-controller-in-android

ポイントは
1.自前のScrollerを定義して、startScrollをOverrideし速度を好きな値にする
2.ViewPagerのmScrollerに1で定義したScrollerを使う。
※ただしViewPager::mScrollerはprivateなのでリフレクションを使う必要がある。

ソースコードは上記ページを参照のこと。

2013年4月17日水曜日

objective-cのswitch文でexpected expressionエラー

objective-cのswitch文でcase文の直後に変数宣言をするとexpected expressionというエラーが発生する。
switch( hoge ){
    case 0:
       int i; // expected expression
       break;
}

解決方法は簡単で、case文内でスコープを定義すれば良い。
switch( hoge ){
    case 0:
       {
           int i;
           break;
      }
}

理由はObjective-C勉強室というサイトに載っていた。以下引用。
 古いCの場合、ローカルに新規の変数を使う場合は必ず新しいスコープの先頭(宣言文以外が出てくるまで)に宣言しなければならなかったが、 Objective-Cではこれがかなり緩くなる。本当は最近のCの規格ではそういうものらしい。 ただし、一部例外がある。
(中略)switch文のcase文の直後に宣言するときのみ、新規スコープの宣言が必要

だそうです。

2013年2月28日木曜日

YOUR PHONE MAY NOT BE PROVISIONED FOR DATA SERVICES

アカウントのセットアップ時にYOUR PHONE MAY NOT BE PROVISIONED FOR DATA SERVICESというエラーが出た。

原因は日付・時刻が正しく無かったためであった。
( しばらく起動してなかったので時刻は1970/1/1 09:00に・・・ )

これを現在時刻に直してから試したら成功した。


2013年1月29日火曜日

(iOS) UISlider::setValueでsliderが再描画されない。

UISlider::setValueはUIスレッドから呼び出さないと再描画処理が行われません。
別スレッドから読んでいる場合はUIスレッドから呼ぶようにしましょう。

[self performSelectorOnMainThread:@selector(updateSlider) withObject:nil waitUntilDone:NO];

ExtAudioFileSeek→ExtAudioFileReadでError -50

iOSで音楽の読み込みを行うAPIにExtAudioFileRead( fileReference , size,  buffer )がある。

また、シークするAPIとしてExtAudioFileSeek( fileReference, seekTo )がある。

ExtAudioFileSeekを呼び出すことで読み出し位置を変更できるのだが、ファイルを最後まで読みだした後にExtAudioFileSeekで適当な位置にシークし、再びExtAudioFileReadで読みだしても、error -50が返され読み出しに失敗する。(読み出しバイト数は0となっている。)

全然原因がわからなくて調べていたら全く同じ内容で困ってる人がいた

なんと、バッファのサイズを示すbuffer->mBuffers[i].mDataByteSizeが、ExtAudioFileReadで読み出されたデータのバイト数で上書きされてしまうとのこと。

なので、ファイル終端まで再生すると、ファイル終端では読み出されるデータのバイト数が0になるため、buffer->mBuffers[i].mDataByteSizeも0になってしまう。

で、その後シークして読み出そうとしても、サイズ0のバッファをExtAudioFileReadに渡した事になり、データの読みだしが1byteも行われないのである。

というわけで、解決策はExtAudioFileReadを実行したら必ずbuffer->mBuffers[i].mDataByteSizeを実際に確保しているバッファサイズで上書きすること。

ExtAudioFileRead(fileReference, &size, buffer);
for(UInt32 i=0 ; isourceBuffer->mNumberBuffers ; i++){
   buffer->mBuffers[i].mDataByteSize = BUFF_SIZE;
}

ExtAudioFileReadの2つ目の引数sizeが、読みだすサイズの指定をしながら、実際に読み出されたbyte数で上書きされる、っていう仕様はまぁよくある話だけど、さらに他の引数で与えた構造体のメンバも同じデータで上書きするってのはちょっと仕様としてどうなの?これはExtAudioFileReadのリファレンスに何か書いてくれなきゃわかんないよう。