Friday, August 29, 2014

ヒストリ機能改め、プレイリスト機能を実装しました

実際に使ってみてヒストリ機能が微妙だったのでどうしたものかと悩んだ挙句、プレイリスト機能として保存されたプレイリストと自動保存される履歴をまとめてプレイリスト機能として実装しなおしました。

若干スクショ多め


Thursday, August 28, 2014

java.lang.UnsupportedOperationException: Effect library not loaded について

多分旧JUST PLAYERから引き継いでいた不具合だとおもうのですが、今日は少し進展がありました。

具体的には下記のようなエラー

java.lang.RuntimeException: Unable to create service jp.co.kayo.android.localplayer.service.MediaPlayerService: java.lang.UnsupportedOperationException: Effect library not loaded
at android.app.ActivityThread.handleCreateService(ActivityThread.java:2314)
at android.app.ActivityThread.access$1700(ActivityThread.java:129)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1228)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4475)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:792)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:559)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.UnsupportedOperationException: Effect library not loaded
at android.media.audiofx.AudioEffect.(AudioEffect.java:384)
at android.media.audiofx.Equalizer.(Equalizer.java:146)
at jp.co.kayo.android.localplayer.media.AudioEffect.attach(AudioEffect.java:120)
at jp.co.kayo.android.localplayer.media.MediaController$GaplessMediaPlayer.setTrack(MediaController.java:758)
at jp.co.kayo.android.localplayer.media.MediaController.init(MediaController.java:90)
at jp.co.kayo.android.localplayer.service.MediaPlayerService.onCreate(MediaPlayerService.java:57)
at android.app.ActivityThread.handleCreateService(ActivityThread.java:2304)
... 10 more

これは一つ前のポストでも取り扱っていた不具合ですが、単純にエラーの原因をみると、Equalizerの生成ができなくてエラーになっています。
エラーの内容が「 Effect library not loaded」なのでサポートされていない端末だからエラーになったのかな?と思ってしまうのですが、現象をみると動く場合もあるのでそういう問題ではないです。
なので、他の原因によって生成が失敗してしまったということだとおもいます。
この例外をGoogleで検索したところいくつかヒットするのですが、その中でも興味深いの次のリンクです。

http://stackoverflow.com/questions/10536170/equalizer-not-always-supported-even-when-api-9

返答は、Eqauzlierのreleaseをしていないから

多分そうだとおもいます。

だけど、EqualizerのReleaseのタイミングはけっこう難しいです。なぜなら音楽プレイヤーのほとんどはServiceで制御するように実装します。
つまりMediaPlayerのインスタンスはServiceが保持するので、必然的にそれに紐づくEqualizer等のAudioEffectはServiceが保持することになります。
しかし、Serviceの場合、onDestroyが呼ばれないケースが存在します。
だから、EqualizerのReleaseのタイミングを逃すケースが存在するのです。
おそらく、上記の不具合はそのようなケースにおいて発生するのでしょう。

多くの端末では、1度や2度Release漏れがあっても問題なく再生できるし、またほっとけば自動的にReleaseされて正常になりますが、そうでない端末もあるようです。
現に私が常時利用しているNexusではそのようなことが発生したことがないです。

今日、たまたまArrowsの端末を使ってテストをしていたら偶然上記の不具合が発生しました。
そして、その理由を考察するにあたり、「APKのインストールを繰り返す」
からではないかと考えています。
理由は、APKのインストールをすると、Serviceは強制的に終了させられるので、onDestoryが呼ばれません。

だいたい原因がわかったので対策をすることにしました。
ひとまず、2つの対策をしてみます。

1 Serviceが強制終了しreleaseが行われなかったとしてもEqualizerのインスタンスは再利用されるように、staticにして保持しておく。

2 とにかく、生成に失敗してもアプリが終了しないようにtry-cacheをしておく。
もちろん、nullになるので次回リクエストのタイミングで再度生成を行う。


                try {
                    Equalizer equalizer = new Equalizer(mPriority, audioSessionId);
                    if (supportFx(descriptor, equalizer.getDescriptor())) {
                        sEqualizer = equalizer;
                    } else {
                        equalizer.release();
                    }
                }catch (Exception e){
                    Log.e(TAG, "EqualizerのReleaseがうまくいってなくてこれ以上生成できないって言われてる。", e);
                }

これで様子を見てみようとおもいます。
手元の端末では一応動作しているのですが、、

ちなみにリリースですが今日中は無理かな、、、
プレイリストの実装がもう少しかかりそうです。

Wednesday, August 27, 2014

自動でボタンを押した感をだすボタンの作成

Android-Lだとボタンに画像を貼ってそのままでボタンをおしたときのセレクターが自動で実装されてる感じなので、これいいなとおもっていたのですが、しばらくAndroid-LのSDKが下位レベルでサポートされない可能性を考えると自前で実装したほうがよさそうです。

基本的にAndroidのボタンは、なにもしていない状態、押した時の状態と画像を別々に準備し、それをselectorという画像切替用のXMLファイルを別途定義する必要があります。
これが地味に面倒くさい。
Photoshopであらかじめ彩度をおとしたりした画像を準備していました。

IOSだとこの辺りが自動だから楽なのでAndorid面倒くさいなぁとおもわれる点でもあるのですが、ちょっとこれも工夫をすると少しは楽になります。

public class AutoEffectImageButton extends ImageButton implements View.OnTouchListener{
    private ColorFilter mFilter;
    public AutoEffectImageButton(Context context) {
        super(context);
        setup();
    }

    public AutoEffectImageButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        setup();
    }

    public AutoEffectImageButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setup();
    }

    private void setup(){
        mFilter = new PorterDuffColorFilter(0x64FFFFFF, PorterDuff.Mode.DARKEN);
        setOnTouchListener(this);
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                getDrawable().setColorFilter(mFilter);
                v.invalidate();
                break;
            }
            case MotionEvent.ACTION_UP: {
                getDrawable().clearColorFilter();
                v.invalidate();
                break;
            }
        }
        return false;
    }
}

上記のクラスのように押したときに、ボタンの色を変更する処理を実装するとプログラムでボタンをおしたときに彩度を落としてボタンを押した感じに表現することができます。
フィルターにはいろいろあるのでアプリに合うエフェクトを使うといいでしょう。
ちなみに、下記のサイトにPorterの違いを一覧にしてくれているサイトがあったので重宝しました。

http://d.hatena.ne.jp/gae+eyo/20111123/1322026548


Tuesday, August 26, 2014

ファイル選択ダイアログでフォルダを複数選択できるようにしました

「フォルダを選択したら、そのサブフォルダも検索してほしい」
という要望があるのだけど、これは非常に難しく、実装はたやすいけど、そのあとのフォローが難しいとおもっています。
なぜなら、SDカードならフォルダ内の一覧を取得するのは一瞬だけど、クラウド上のフォルダはその取得に10数秒かかる場合があります、それを再帰的に取得するとファイルの取得にすごく時間がかかってしまう。

プログレスバーは表示しないといけないし、途中で停止も実装しないといけないし、失敗した場合の例外処理もしないといけないしで手間だけ増えてくる。

これは、旧JUST PLAYERの仕様であり、悩みのたねでした。
旧JUST PLAYERではこれを何度もしないように一番最初に痛みをお願いするという形でフォルダのスキャンを行いインデックスを作ることで逃げましたが、この手法では、フォルダ状態が変わったときに再スキャンをしないといけないという欠点がありました。
クラウドサービスによってはフォルダの変更日付を取得できるものもあるのでそのようなサービスだけは起動時にチェックをすることも可能なのですが、取得できないものもあります。
なので、統一的なインターフェースを実装する場合、最小のサービスに合わせるしかないのが悩ましい点です。

新JUST PLAYERでは、これはもう面倒だってんでもう毎回フォルダを選択してファイルをいれてくださいという仕様にしたのですが、やっぱりいちいち選択するのが面倒臭いんですよね。

というわけで冒頭の要望がでているのです。

今回のバージョンでは、そのサブフォルダを自動で追加する機能はひとまずペンディングにし、複数のフォルダをまとめて選択し、追加できるようにしました。

これなら、アーティストのフォルダにあるアルバムを全選択するという手順だけど、いっぺんに曲を追加できることになります。
これでも不便なのかどうか、ちょっと様子を見たいとおもっています。

あと、今日アップロードしたバージョンで、AQUOSPADで落ちる不具合が治っているといいな。
いちおうそれっぽいところを修正してみたのだけど。
手元で再現をしないので、よくわからないです。

GooglePlaySeviceのDevelopderConsoleに下記のエラー報告がありました。
これは、AquosPadのエラーだったので多分これだとおもう。

java.lang.RuntimeException: Unable to create service jp.co.kayo.android.localplayer.service.MediaPlayerService: java.lang.UnsupportedOperationException: Effect library not loaded
at android.app.ActivityThread.handleCreateService(ActivityThread.java:2627)
at android.app.ActivityThread.access$1700(ActivityThread.java:150)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1351)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:5159)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:810)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:577)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.UnsupportedOperationException: Effect library not loaded
at android.media.audiofx.AudioEffect.(AudioEffect.java:384)
at android.media.audiofx.Equalizer.(Equalizer.java:146)
at jp.co.kayo.android.localplayer.media.AudioEffect.attach(AudioEffect.java:120)
at jp.co.kayo.android.localplayer.media.MediaController$GaplessMediaPlayer.setTrack(MediaController.java:736)
at jp.co.kayo.android.localplayer.media.MediaController.init(MediaController.java:90)
at jp.co.kayo.android.localplayer.service.MediaPlayerService.onCreate(MediaPlayerService.java:57)
at android.app.ActivityThread.handleCreateService(ActivityThread.java:2617)
... 10 more

このエラーの原因自体はサポートされていないAudioEffectを使ったからおちていますがエラーの内容と、原因がずれているとおもいます。
プログラムのバグではスタックトレースと、エラーの原因が直結していないことはよくあることです。
多分ですが、EqualizerのAudioEffectのReleaseが正常にされていないのに、生成がされたためだと思われますので、終了処理の箇所を見なおしてみました。
これで治ってもらえると助かるんですが。。。



Monday, August 25, 2014

履歴(簡易プレイリスト)を追加しました

History(履歴)機能を追加しました。

この機能は、SDカード内の音楽や、クラウド上の音楽を選択し追加した際に、そのときに追加した状態をプレイリストとして履歴に保存する機能です。

履歴にはタイトルと説明、それと作成日付と更新日付が登録されています。
タイトルは、追加したときのフォルダ名が設定されます。
説明には追加した曲のタイトルを、区切りで64文字まで設定しています。
ただし、表示領域の関係でスマホだと25文字程度しか表示されていません。

Historyには上記の名前で表示されるのでそれを選択すれば、クラウドに接続しなくてもすぐに曲を入れ替えができます。
オフラインキャッシュが作成されているならこちらのほうが素早いとおもいます。

ずっと消えないように保存するのであれば、別途プレイリストをファイルに保存しておくことをお薦めします。

あと、Historyから再生すると更新日時が新しくなるので履歴としては上にあがってきます。
まだ未実装ですが、リリース迄には100件(この数字は要望があれば変更しますがひとまず100件で)以上の履歴は自動で削除するような仕組みになるので、よく聴くリスト的な位置づけになるとおもっています。

あと、まだHistoryの名前の変更などは実装していません。
履歴だけに変更するものなのかも悩みどころ。

既知のバグとして、
・MediaStoreの履歴名が日付になっています。
・アルバムアート表示画面が再生停止の状態だと画像が小さいままでみっともないです。
・イコライザの保存とかがうまく動いてないです。

LMP(通称:LemonMerengePie)が噂になっています。
JUST PLAYER2はMaterialDesign対応が主目的なのでLMPがリリースされ、SDKが正式になった際にリリースする予定です。
おそらく、10月頃だとおもっているのですが、もし遅れる場合はリリース日は再検討します。
それまではベータをご利用ください。




Tuesday, August 19, 2014

プレイリストを保存できるようにしました

プレイリストを保存できるようにしました。
だけど、まだ不十分です。

ひとまずできるようになったというところかな。

現在再生中(PlayQueue)に入っている曲の一覧を右上のEditボタンから編集できるようになりました。
編集は、ドラッグで曲の移動、削除ボタンで削除できます。
編集後、Menuからto playlist queueで、現在生成中のリストに反映します。

また、Saveでも生成中のリストに反映します。
それから、New Saveで新しいファイルにリストを保存できます。
この際ファイル選択ダイアログが表示されますので、任意のフォルダ、またはそのままファイル名をつけると保存されます。

この初期のフォルダですが、現在アプリケーションのフォルダになっているので、ちょっとわかりにくいと思います。
デフォルトのフォルダは設定で変更できるので、必要であれば変更してください。

しかし、ここまで書いていたこれは面倒くさい。。

実際のところ何も考えなくても簡単に保存、読込が利用できるように設計中です。

これからの実装
・自動プレイリスト保存
 これは、曲を選択し再生中のリストに追加すると自動でそのリストを日付で保存する機能です。これは、ある意味履歴のようなものなので、自動で生成されて、数が多くなったら古いものから消されていきます。このリストはDBに保存されるので、他のプレイリストとは異なります。


・任意のプレイリストファイルを選択し編集
現在再生中のリストだけでなく、ファイルで保存されたプレイリストを開いて編集できるようにします。

・所定の場所のプレイリストのフォルダを開く
NavigationMenuに保存されたプレイリストを開くためのショートカットを追加します。


あと、Boxnetの最大取得ファイル数を1000件にしました。



Wednesday, August 13, 2014

ネットで噂になってたスピーカー作ったけど期待した程音はよくなかった

夏休みはだらだらと過ごすものである。
結局僕は外に一歩もでないでコーディングをしながら音楽をきいて時を過ごしてしまいました。

Bebop最高!

不具合を少し修正し、リスト画面のレイアウトをいじりました。

・Backボタンを押すと、一曲目に移動する不具合を修正
・再生中のマークのアニメーションが再生に連動していない不具合を修正
・リピート設定にしていないのにリピートしてします不具合を修正

・リストビューのレイアウトを変更
・コントロール部のレイアウトを変更
・ポップアップメニューを追加(削除しか実装してません)
・クリアボタンを機能するようにした

あと、のんびりだけど、プレイリストの編集機能の実装にとりかかろうと思っています。
今日はそのレイアウトの検討で時間くっちゃいました。


それから、タイトルにもあるけど紙コップを作ったスピーカを作ってみた。
音が大きくなったなぁ(小並感)