Saturday, May 31, 2014

ストリームキャッシュ機能をとりあえず実装

今日は一日休みだったのでプログラム充してました。
とりあえず、実用レベルまでキャッシュ機能を実装したので記念上げです。

現時点の実装は以下のとおりです。

・Range指定のストリーミングでない場合に限りキャッシュの作成を行う。
(シークバーなどでバッファを先読みすると、内部でRange指定でHTTPリクエストがくるんだけど、その場合はキャッシュ化はしないよっていう意味)
・キャッシュはキャッシュフォルダに作成され、再生時に利用されます。
ただし、このファイルは起動時に3日前のものを消す仕様になっています。

将来的には、Range指定されてもキャッシュ化し、再生時に最新日付を設定することで消される対象から除外するといった処理をする予定です。
あと今回はRatingを統一する予定なので、Ratingにも比例して消されにくくしたいと思っています。
あと、要望であった、キャッシュフォルダを設定で変更出来るようにするなど

https://bitbucket.org/yokmama/just-player/downloads/JUSTPLAYER3_v1.0_053102.apk

GoogleDriveすごい、、かも

今日の予定である、GoogleDriveAPIからのStreaming処理の部分をDropboxやAmpacheといった他のクラウドデータでも使える用に、Creator、Strategyパターンでリファクタリングしました。
このあとキャッシュ化処理を実装しようかとおもったんだけど、ちょっと疲れたので、GoogleDrive共有フォルダへのアクセスが可能かどうかしらべたところ、できましたね。
これは、すごい。
Dropboxとか他のクラウド・ストレージの場合共有フォルダはいくつか制限があるもんだけど、これはあんのかなぁ?
スピードも早いし快適すぎるよ。

共有フォルダ対応したバージョンをアップロードしておきました。

二段階認証時の処理対応

GoogleDrive対応がやっつけ仕事だったので、二段階認証や認証でなにか問題があってエラーがおきたときに無限ループのような状態になってました。

05-30 19:44:35.425      333-335/? D/dalvikvm﹕ GC_CONCURRENT freed 865K, 11% free 14926K/16647K, paused 1ms+5ms
05-30 19:44:35.441    4161-4172/? I/GLSUser﹕ GLS error: BadAuthentication
05-30 19:44:35.441    4161-4172/? V/GoogleLoginService﹕ Returning error intent with: ComponentInfo{com.google.android.gsf.login/com.google.android.gsf.login.LoginActivity}
05-30 19:44:35.464  17779-17905/? E/MusicStreaming﹕ Received null auth token.
    android.accounts.AuthenticatorException: Received null auth token.
            at com.google.android.music.sync.google.MusicAuthInfo.getAuthToken(MusicAuthInfo.java:42)
            at com.google.android.music.sync.api.SignupClient.checkInviteStatus(SignupClient.java:86)
            at com.google.android.music.tutorial.SignupStatus$SignupCheckService.onHandleIntent(SignupStatus.java:308)
            at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:65)
            at android.os.Handler.dispatchMessage(Handler.java:99)
            at android.os.Looper.loop(Looper.java:137)
            at android.os.HandlerThread.run(HandlerThread.java:60)
05-30 19:44:35.464  17779-17905/? I/CheckMusicStoreAvail﹕ No verified accounts found
05-30 19:44:35.496  17779-17905/? I/CheckMusicStoreAvail﹕ Starting account signup check
05-30 19:44:35.558      186-213/? I/ActivityManager﹕ Displayed com.google.android.gms/.auth.login.ShowErrorActivity: +226ms
05-30 19:44:35.574  16430-16430/? D/OpenGLRenderer﹕ Flushing caches (mode 0)
05-30 19:44:35.605  16430-16430/? D/OpenGLRenderer﹕ Flushing caches (mode 0)
05-30 19:44:35.628    4161-4172/? I/GLSUser﹕ GLS error: BadAuthentication
05-30 19:44:35.636    4161-4172/? V/GoogleLoginService﹕ Returning error intent with: ComponentInfo{com.google.android.gsf.login/com.google.android.gsf.login.LoginActivity}

原因は、エラーが起きた時に、理由の含まれたExceptionを渡して再度ログインを試みるのですが、この際別のアプリに連携します。
二段階認証だとこの処理でブラウザアプリに連携しそこで認証コードを入力して戻るという処理なのですが、このとき新たにActivityが生成されるので、また認証処理を実行してしまうというループになっているようでした。
もどってきてもきちんと処理をうけとれるようにAndroidManifest.xmlでandroid:launchMode="singleInstance"にすることでとりあえずの処置をしました。
この認証処理は、OnStartとかで実装するのではなく、ボタンなどのトリガーで呼び出すように実装するのが正しのかとおもいます。

ちなみに、上記ログに関連する情報は下記にもあります。
http://stackoverflow.com/questions/12929569/error-while-trying-trying-to-use-oauth2-accountmanagerfuture-to-authenticate-w

修正したアプリはこちらにアップしました。

https://bitbucket.org/yokmama/just-player/downloads/JUSTPLAYER3_v1.0_053001.apk

いちおうGoogleDriveアプリと連携できたので、今後はStreaming処理に手を入れていく予定です。
今後のマイルストーンは以下のように進めます。

・ストリーミング処理の再構成
・ファイルキャッシュの導入
・画面フロー時の処理を見直し
・GogleDriveファイル選択画面のUIをリファクタリング
・イコライザー設定のバックアップ機能追加
・ブックマーク機能の検討
・その他クラウドへの対応

ちなみに、要望にありましたがBox.netは難しくないのでやりましょう。
SkyDrive(OneDrive)のほうはAndoridからアクセスできるかどうかから調査しないといけないので、できるとはまだ明言できないんだけども。



Wednesday, May 28, 2014

記念上げ

実はこっからが大変なんだけど、ひとまずGoogleDriveの音楽を再生できるようになったので記念上げ。

GoogleDriveのフォルダ内の音楽を再生することしかできません。
ラジオボタンでフォルダ選択、プレイリスト選択は意味ないので注意です。
あとキャッシュ化もしていないです。
これはこっちのほうがいいっていう人がいるかもしれないけど。


https://bitbucket.org/yokmama/just-player/downloads/JUSTPLAYER3_v1.0_052801.apk

クラウド対応始めました

Equalizer処理、Shuffle,Repeat、早送りの対応が終わりだいたい音楽プレイヤーとして形になってきたので、次はクラウドデータの対応をはじめることにした。

クラウド対応といっても、どのクラウドデータに対応するか悩むところです。
最終的には、Dropbox,GoogleDrive,Ampache,OwnCloud,SkyDrive(OneDrive)辺りには対応をしたいなとおもっているのですが、どれからにしようかな。

モチベーションを維持するためにはやったことないところから手をつけていきたい、そういうわけで、GoogleDriveかSkyDriveにしよう。
で、SkyDriveだけどちょっと調べたところ、最近名前がOneDriveになったようですね、OneDriveってUbuntuOneにのってるアレかとおもってました。違った。
SkyDriveは以前オープンソースのライブラリを見つけたのでそれで試したことがあるんだけど、APIが古くなっているのか動作確認がとれなかったです。そういう状況だったのでOneDriveにはとても対応してるとはおもえないし、公式サイトをみてもどうやってアクセスすればいいのかイマイチわからない感じでした。
というわけで、まずは手始めにGoogleDriveから対応することにします。

GoogleDriveは最近GooglePlayServiceからアクセス出来るようになったし、認証処理などもお手軽にできるようになったので、JUST PLAYERのクラウドデータとしては標準で装備してもよいのかなとおもっています。
これが実装できたら、インターフェースを統一して他はプラグインという形で提供できればいいなぁ。



Tuesday, May 27, 2014

Shuffleの実装

Shuffleの実装は意外と難しい。
Equalizerの実装は納得はしてないけど、とりあえずこれ以上掘ってもわからないので一旦ペンディングすることにしてRepeatとShuffleの実装にとりかかることにした。

Repeatはそんなに難しくない、1曲リピートはOnCompleteのときにSeekで0に戻せばいいだけだし、RepeatAllは最終の曲が再生され次の曲がないとき一曲目から再生するようにするだけでよい。
ただ、JUST PLAYER3にはBookmark機能があるのでその分実装が少し複雑になるけども。
まぁ、これはいいとして問題はShuffle。
Shuffleは、ShuffleをOnにしているときに再生順がランダムになるようにする機能だ。
このランダムというのが先読み機能と相性が悪くて、次の曲が何を再生するのかわからないようなShuffleは実装できない。
それに、一度再生された曲が何度もでてくるのもいただけない。
なので、再生済みフラグを設定し、フラグのたっていない曲から次の曲をランダムに選ぶというのが簡単に思いつく実装だとおもう。
前バージョンのJUST PLAYERはこの方法を取り入れていた。
しかし、この方法には欠点があり、プレイリストの数が数千曲もあると、再生後に実行されるシャッフルでもたつきが発生し、折角ギャップレス再生をしていても意味がないものになってしまう。
なので、これをなんとか解決したいと思っていた。
JUST PLAYER3ではそのため、プレイリストの曲順とは別に再生順用の曲順をプレイリストに設定し、シャッフルでそれを一遍に変更することにした。
このシャッフルの設定は曲の登録後に毎回設定することで見かけじょうShuffleされているようにみえるとおもう。
ただ、これでも問題があるとおもう。
Shuffle自体のアルゴリズムだ、既ににあるプレリストに曲を追加した場合、再生中の位置から以降の曲をシャッフルするのが自然だし、まだ一度も再生をしていない状態のプレイリストをShuffleすると一曲目からシャッフルされるべきだし。
数万曲とかのShuffleをしても高速に曲順を設定できるべきだし。
そう考えるとけっこう複雑になってしまう。
実際にShuffleの実装は思った以上に実装ステップ数が多くなってしまったのでバグが潜んでいるのではないかとちょっと心配。

https://bitbucket.org/yokmama/just-player/downloads/JUSTPLAYER3_v1.0_052701.apk






Monday, May 26, 2014

BassBoostとEquqalizerは同時に有効にできない

もしかしたらこれはNexus4が悪いのかもしれない。
もしかしたらこれは、僕のNexus4だけが悪いのかもしれない。

とにかく、BassBoostとEqualizerは両立できないようなので、Equalizerを有効にする場合は一旦BassBoostを無効にするようにした。そして逆も然り
これをするようにしてから、安定して動作をしているようにみえますので、ひとまず開発途中のバージョンをアップロードしました。

現在のところm3uファイルもしくは、cueFile、あとフォルダ指定による音楽の再生しかできません。
AudioEffectは右上の再生ボタンあるいはアルバムアートをクリックすると移動するプレイ画面から、右下のボタンで移動できます。
(添付のスクリーンショットはEqualizer変更画面です。)

 ですが動作確認はNexus4(日本語)でしか実施していません。
他の端末とか、日本語以外の環境ではなにか不都合がおきるかもしれませんが、動作に問題があればご連絡ください。

あぁ、それと、JUST PLAYERのソースコードをPlayStoreにアップロードしている輩がいます。
このアプリに関しては僕は一切関知しておりません。
きちんとバグを修正しているのであればよいのですが、、もしウィルスでも混入しているのでしたら問題です。

下記のアプリは許可なく無断で登録されたものなので、くれぐれもご注意ください。
https://play.google.com/store/apps/details?id=com.illusion.localplayer


PresetReverbは特別なんですか。そうですか。

Visualizerのリスナー内でgetEnabledを実行される前にreleaseをしてしまうと、リスナー内でgetEnabledをした際に意味不明なダンプを出力して落ちてしまう。
とくにこれ、OnDestoryやOnPauseなどでやっていると注意、リスナーモードをNONEに設定しても飛んでくるので。
ということなので、Visualizerのインスタンスにnullを設定することで、リスナー内の処理を実施しないようにしました。
いちおう、これで落ちにくくはなった。その後調子にのって、他のAudioEffectも実装し、PresetReverbまで実装したところで、またまた例のダンプが頻繁に発生するようになってしまいました。
内心またかぁ、まったく。。AudioEffect難しい、、もうこれEquqlizerだけ使えるよう程度にしようかなとおもったりもします。

調べていくと、どうやらPresetReverbはちょっと特別らしい。
たしかに、以前からこれ全然効果ないしおかしいなぁとと思っていました。

公式のリファレンスをみると、下記の様に書いてあります。


The PresetReverb is an output mix auxiliary effect and should be created on Audio session 0. In order for a MediaPlayer or AudioTrack to be fed into this effect, they must be explicitely attached to it and a send level must be specified. Use the effect ID returned by getId() method to designate this particular effect when attaching it to the MediaPlayer or AudioTrack.

Creating a reverb on the output mix (audio session 0) requires permission MODIFY_AUDIO_SETTINGS
これみると、PresetReverbはAudioSessionを0で生成しないとだめって言ってる、しかも、その場合、MODIFY_AUDIO_SETTINGSのパーミッションも有効にしないといけないよって。 たしか、ほかのAudioEffectはAudioSessionを0で使うのはDuplicatedになっているので盲点でした。ようするに、PresetReverbだけ特別扱いしないといけないと、、そういうわけです。 現在、ServiceとActivityで処理をしているので、AudioSessionの使い分けはめんどくさいことになりそう。やっぱり、これがまた後々新たな面倒の種になるとと厄介なので、せっかく前回Activity側でもAudioEffectを持つようにしたけど、それはやめて、Service側が持つものだけにし、アクセスはAIDL経由で行うようにしたほうが管理しやすいきがしてきました。 あぁ、めんどくさい。また修正か。。

Friday, May 23, 2014

Equalizerのもつ位置をもとに戻した

ずっと頭をいためていた例の不具合に関して解決の糸口がみえてきた。
つまり、まだ解決はしていないけど、どうしてこのようなログが吐出されるのかという点で類似する不具合から想像しているだけにすぎないのだけど。

昨日分かったことを並べていくと、
まず、現状問題となっているであろうと予測した「MediaPlayerの生成を何度もしないようにする。」という修正をやってみた。
これまで、曲の切り替えなどで毎回MediaPlayerを初期化していたのは、Resetして再利用するときに下手してStatusエラーがでちゃうのが不安だったので、面倒くさいからっていう理由でした。やはり、そういうことを面倒くさがらずに、一回使い終わったらちゃんと状態をみてResetをし、その後曲を設定するというように変更。
プログラム側からそのような処理を隠蔽するため、中間クラスとしてGaplessMediaPlayerというクラスと、その中で保持される、MediaPlayerHolderというMediaPlayerへの機能の移譲を行うクラスをもつようにした。
こうすることで、MediaConrollerクラスはスッキリした実装になったとおもう。
その後、きちんと動作するようにテストを実施したのですが、ここで新しいバグが発生した。
いままで以上に簡単にEqualizerの設定画面から戻るときに落ちるようになったのです。
しかも出力されるLogCatのパターンも同じ。
前よりひどくなったので、これはなにか変なことになってないかとジィっとプログラムをみてみたところ。一つ見つけました。
それは、Visualizerの解放の順番

間違ってる例
onPause()にて
mVisualizer.setMeasurementMode(Visualizer.MEASUREMENT_MODE_NONE);
mVisualizer.setEnabled(false);
mVisualizer.release();
mVisualizer = null;


正しい例
onPause()にて
mVisualizer.setEnabled(false);
mVisualizer.setMeasurementMode(Visualizer.MEASUREMENT_MODE_NONE);
mVisualizer.release();
mVisualizer = null;

そう、setEnabledの設定順番が間違っていた。
これのせいで、Visualizerのリスナー処理のなかで、visualizer.getEnabled()を呼んでしまい例のダンプが発生していました。
LogCatの状況ではそれが原因にみえなかったのでわかりにくい。
すでに、Releaseで解放されてしまったVisualizerクラスに対してgetEnabled()をしちゃてているので、あのようなダンプにつながったのだとおもいます。
ということは、これまで悩ませていた不具合も、MediaPlayerに関連するVisualizer,Equalizer等でRelease済のものに対してなにか処理をしているのかもしれない。

しかし、おかしいのは上記の実装は、MediaPlayerの初期化の修正の前からのものです。
なので、発生するなら以前からずっと同じように発生しているはずなのですが、、

とにかく、同様のことをやっていないかプログラムを見直すことにします。




Thursday, May 22, 2014

Equalizerをもつ位置について

長いこと問題として認識しつつも放置していたEqualizer使用時の問題について。
問題をまず整理しておくと、

MediaPlayerを管理するServiceと、それにEqualizerの設定を変更する為、Equalizerを別にもつActivityクラス。

EqualizerはMediaPlayerにAttachする必要があるため、MediaPlayerのAudioSessionIDが必要になる。

最初の実装では、ServiceからAIDLによってMediaPlayerのAudioSessionIDを取得し、Activity側が保持するEqualizerにAttachをしていた。
このとき、優先度を上げるためPriorityをService側が保持するEqualizerよりも高くすることで一旦はうまく動作するようなった、ようにみえていた。
しかし、テストでActivity側で設定したEqualizerをsetEnabledでTrue,Falseを繰り返し行うと、稀に落ちる不具合が発生することに気づく。

当初この問題は、Equalizerのノイズに関連する問題なのではと思っていたため、原因の追求は後回しにし、まずはノイズ対策をしていたのだが、ノイズ対策をした後にもこの問題が残っているため。この問題は関連性のないものだと再認識することになった。

昨日の時点で、とりあえず問題をややこしくしているEqualizerをServiceとActivityで別々のものを使うという点に着目し、これを一旦やめて、Service側でもつEqualizerだけにし、Activity側はEqualizerの状態をAIDL経由で取得する方法に変更してみた。

結果、落ちる不具合はかなり改善された、通常使いではほとんど落ちることはないと思われる。
しかし、やはり落ちた。
setEnabledで繰り返しTrue,Falseの切り替えを行い、その後曲の切り替えを行うと落ちる症状が確認された。
狙ってテストをするとかなりの確度で発生する。
この落ちたときに出力されるログは、修正前のログと酷似しているためおそらくこの問題にはEqualizerが関連していると考えられる。

この問題を考察すると、曲の切り替え処理に問題があるのではと考えらえる。
現在の実装では曲の切り替え時にMediaPlayerを一旦破棄し、再生成をしている。
このときAudioSessionIDが新しく生成されるので、ひょっとしたらこれが問題なのかもしれない。
AudioSessionIDが再生成されないように、工夫してみようとおもう。
そうなると、この修正の前に行ったEqualizerのもつ位置の変更はあまり関係がなかったのかもしれない。
一旦この修正を行い落ち着いたら、再度EqualizerをActivity側に移動してみようとおもう。








Wednesday, May 21, 2014

ノイズは除去できただけど

大きい音楽ファイルをシークし、その後再生時に”ジッ”というノイズが入る問題。
Equalizerを設定している場合に限り発生していたが、結局のところSeekが完了するまで、setEnableでFalseにしておくことで回避することにした。
根本的な解決ではなく回避をした理由であるが、いくつかテストをしてみた結果、大きいファイルの場合はノイズが発生してしまうようだ、これは端末に依存する問題なのかもしれない。しかし、他の端末でテストをしていないのでその点に関しては報告できる情報はもっていない。

これに関しては一応解決したことにする、しかしまた新たな問題が発生している。
発生しているというと、今回対処をしていたノイズの不具合の後に発覚していたように思われるかもしれないが、実は以前から発生していて、こちらも原因を調査しないといけない不具合の一つだった。
説明が後回しになってしまったが、不具合の内容というのは、何度もイコライザーをOn,Offをしているとアプリが強制終了してしまうという、致命的な問題である。

次のログは、強制終了時に発行されるログを抜粋したものだ

05-21 10:37:23.013    2221-2221/jp.co.kayo.android.localplayer3 D/MediaController﹕ AudioSessionId = 856
05-21 10:37:23.063  23929-23961/? D/audio_hw_primary﹕ out_set_parameters: enter: usecase(0: deep-buffer-playback) kvpairs: routing=2
05-21 10:37:23.853      183-183/? W/SurfaceFlinger﹕ couldn't log to binary event log: overflow.
05-21 10:37:24.483    2221-2221/jp.co.kayo.android.localplayer3 A/libc﹕ Fatal signal 11 (SIGSEGV) at 0x00000002 (code=1), thread 2221 (id.localplayer3)
05-21 10:37:24.583      181-181/? I/DEBUG﹕ *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
05-21 10:37:24.583      181-181/? I/DEBUG﹕ Build fingerprint: 'google/hammerhead/hammerhead:4.4.2/KOT49H/937116:user/release-keys'
05-21 10:37:24.583      181-181/? I/DEBUG﹕ Revision: '11'

<中略>

05-21 10:37:24.843      764-849/? W/InputDispatcher﹕ channel '42675ea8 jp.co.kayo.android.localplayer3/jp.co.kayo.android.localplayer3.MainActivity (server)' ~ Consumer closed input channel or an error occurred.  events=0xd
05-21 10:37:24.843      764-849/? E/InputDispatcher﹕ channel '42675ea8 jp.co.kayo.android.localplayer3/jp.co.kayo.android.localplayer3.MainActivity (server)' ~ Channel is unrecoverably broken and will be disposed!
05-21 10:37:24.843      764-962/? W/MediaFocusControl﹕ RemoteControlClient died
05-21 10:37:24.843    1822-1897/? V/Avrcp﹕ New genId = 111, clearing = 1
05-21 10:37:24.843      764-962/? W/MediaFocusControl﹕ AudioFocus   audio focus client died
05-21 10:37:24.843      764-962/? I/MediaFocusControl﹕ AudioFocus  removeFocusStackEntry(): removing entry for android.os.BinderProxy@42c7ea90
05-21 10:37:24.843     764-2017/? W/InputDispatcher﹕ Attempted to unregister already unregistered input channel '42675ea8 jp.co.kayo.android.localplayer3/jp.co.kayo.android.localplayer3.MainActivity (server)'
05-21 10:37:24.843    1822-1897/? V/Avrcp﹕ New genId = 112, clearing = 1
05-21 10:37:24.843  23929-23963/? W/AudioPolicyManagerBase﹕ unregisterEffect() unknown effect ID 857
05-21 10:37:24.843     764-1054/? I/ActivityManager﹕ Process jp.co.kayo.android.localplayer3 (pid 2221) has died.
05-21 10:37:24.843     764-2017/? I/WindowState﹕ WIN DEATH: Window{42675ea8 u0 jp.co.kayo.android.localplayer3/jp.co.kayo.android.localplayer3.MainActivity}
05-21 10:37:24.853     764-1054/? W/ActivityManager﹕ Force removing ActivityRecord{4268c988 u0 jp.co.kayo.android.localplayer3/.MainActivity t51}: app died, no saved state
05-21 10:37:24.873      186-186/? D/Zygote﹕ Process 2221 terminated by signal (11)
05-21 10:37:26.083      764-962/? W/InputMethodManagerService﹕ Got RemoteException sending setActive(false) notification to pid 2221 uid 10138
05-21 10:37:26.093      976-987/? W/Binder﹕ Caught a RuntimeException from the binder stub implementation.
    java.lang.NullPointerException
            at android.inputmethodservice.IInputMethodWrapper.setSessionEnabled(IInputMethodWrapper.java:280)
            at com.android.internal.view.IInputMethod$Stub.onTransact(IInputMethod.java:129)
            at android.os.Binder.execTransact(Binder.java:404)
            at dalvik.system.NativeStart.run(Native Method)


あまりに長いので途中は略したが、中身はダンプログになっている。

現時点の考えでは、MediaPlayerを2つもち、それを交互の使用していることと、Equalizerに設定するaudioSessionIdに問題が起因しているのではないかと考えている。
今日の作業はまず状況を整理してみようとおもう。


Tuesday, May 20, 2014

僕とFlacとの格闘

Flacのような長い音楽ファイルの再生にAndoridは特化しているわけではない。とくにFlacの場合CueSheetにも対応していないので、自前で頭出しを実装する必要がある。
これは、僕とFlacファイルとの格闘を、今後この問題が解決し後に振り返るためのメモである。

Day1-Day8
Day1〜Day8としたけど、実際何日たったかなんて覚えてない。
メモをしないといけないと気づいたからだ。そしてそれが今日である。
事の発端は、ある長い音楽ファイルである。
この音楽ファイルはファイルフォーマットはMP3だが、ファイルサイズが1G近くもあり、再生時間も6時間ほどもある。本来このファイルは垂れ流しで聞くものなので、用途を間違えなければ問題はない。
しかし、途中まで聞いていて次に再生するときに頭から聞くのはつらいし、またお気に入りの曲からはじめるといったことも難しい。
そこで、インデックスを別途準備し、分割されてないにも関わらず分割されているような操作性を提供できないかと思った。

調べると、同様のことをFlacは既に実現しているらしい、フォーマットはCueSheetというファイルのようだ、独自フォーマットを作るのもやぶさかではないが、まずはCueSheetを調査し、良い部分を取り込む方向で作業をすすめることにした。
つまり、遠回りにはなるけど、まずはFlacCueSheet対応をしてみることにした。

結果、なんとかCueSheet対応ができた。
同様のアプリは既にPlayStoreにもあるため、新規性はないが容量の大きいファイルでもストレスなく再生できる点で他のアプリより優位性があるように思える。

同じ手法で、暫定的なフォーマットを作成し、MP3でも再生できることを確認した。

実用性が出てきた為、実装はこのあとさらに加速していく。
プレイリスト、フォルダ選択だけに限るが、曲の選択ができるように実装した。
再生中の画面としてビジュアライザーを実装した。
次はイコライザの実装にとりかかる。

そして、現在躓いている。

容量の大きい音楽ファイルの再生でイコライザーを設定すると、曲の頭出し時に”ジッ”というノイズがはいる。

これの除去がうまくいかない。

ここまで書いて、考えがまとまっていないことに気づいた。
まずは状況を分析することからはじめようとおもう。

・MediaPlayer1とMediaPlayer2
曲のギャップを減らすために、この2つに音楽ファイルを設定する、
ただし、次の曲が同じファイルの場合は2つ同じファイルを読むのは無駄なので、MediaPlayer2はPositionだけを保持した空のMediaPlayerになる。
これを実現するためにMediaPlayerをラップしている。

・AudioEffect
AudioEffectはMediaPlayerと1:1である必要はないため、MediaPlayer1とMediaPlayer2で共有している。つまりAudioSessionIDで一個つくっている。
これはサービス側の話。 イコライザーの設定画面でも一個もっている、こちらはAudioSessionIDをサービスから取得し生成している。
この際、イコライザー設定画面のイコライザーの優先順位を高くするためプライオリティーを高く設定している。

・Config
イコライザー設定画面からイコライザーの設定を変更したら、一旦Preferenceに値を保存する。
その後、Service側に変更を通知し、再読み込みを行う。


現在は上記のような実装になっている、ここまでくるのに、いくつかのTryErrorがあった、結局上記の手法でもノイズがはいるのだが。

現時点までの工夫として
・AudioEffectは何度も生成しないようにする。
・onPreparedのほかに、onSeekCompleteも実装し、曲が再生可能な状態になってからstartを行う。

以前、イコライザーのEnableをStartの直後にTrueにするようにしてみると、上手くいっていたことがあった、しかしこの場合、意図的に初期読み込み時はEnableをFalseしておく必要があるため状態管理に苦労した。動作が不安定になったのもこの処理が複雑になりすぎたためであるため、現在他の方法を模索している。

曲の読み込み時の一回目だけジッというのノイズがはいる、その後は曲の移動(Seek)ではノイズがはいらない、ロジックにどのような違いがあるのかもう一度見なおしてみる。










Tuesday, May 13, 2014

ようやく開発を始めました

JUST PLAYERの開発を始めました。
今の進捗とこれからの方針を報告します。

まず、いまの進捗ですが、少しづつですが開発は進んでいます。
4月中旬まで仕事で忙しく、こちらに手を付けることができませんでしたが、後半から手を付け、現在は半月ぐらい開発ができています。

とりあえず、報告できるぐらい開発が進んできましたのでここらへんでスクリーンショットも交えて紹介してみます。