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

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

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


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


Tuesday, August 12, 2014

夏休みに入りました

夏休みにはいったからといって、普段やっていることと大して変わらないのですが。。。
ちょっとだけ更新が速くなるかもしれないです。

今日はそれほど大きい進捗はないですが、次の不具合を修正しました。

・Notificationの終了時間が設定どおりに終了しない
 原因:設定した時間のミリ秒への変換処理が間違っていた。

・Backで前の曲にいこうとすると一曲目に移動した
 原因:前の曲の取得で並び順が昇順だったので一曲目が選択されていた。

それから、アルバムビューの表示をフェードイン、フェードアウトで画像を切替えるようにし、画像の移動をランダムにしました。

それから、APKの公開ですがダウンロードできるようにしてもインストールが容易ではないようなので、アップロードはやめることにしベータ版による公開にのみにしました。

ベータプログラムへの追加はJUST PLAYERのGoogleGroupかGoogle+へ加入が必要です。

Google+コミュニティー

GoogleGroupMailingList

登録すると下記のURLからダウンロードすることができます。
https://play.google.com/apps/testing/jp.co.kayo.android.localplayer

Sunday, August 10, 2014

Dropboxを再生してたら5秒ぐらいして、勝手に次にいくバグの原因はこれじゃないかな?

以前、JUST PLAYERのユーザからDropboxを使って再生をしているとき、いろいろ操作をすると5秒ぐらいしたら勝手に次の曲にくようになって一旦そうなると全部そうなるという報告がありました。
この現象は僕のところでは再現していなかったので、なんなんだろうとおもっていたのですが、Boxnetで長時間再生テストをしていたら、たまに上記のような現象が発生していたので調べて修正をしてみました。

台風につき作業が捗っております

台風が珍しく自宅周辺に上陸してきているため、家に閉じこもるしかありません。
外はとんでもない風でプランターがガタガタいっています。危ないのでびしょ濡れになりながら紐で結ぶとかしてました。

写真じゃわからないかもだけど、外の雰囲気

というわけで家に閉じこもっているので今日はバグを修正していました。
修正したバグの一覧

・曲を再生していると音がならなくなる
・GoogleDriveの接続設定ができない
・Dropboxの接続設定ができない
・再生中の状態をアニメーションにした
・バッファリングの状態をプログレスバーに表示した
・アーティスト名の更新がアルバム名の更新処理になっていて反映されていなかった
・再生中の曲のタイトルをBold指定にした
・アルバムアーティストの表示を灰色にし二行表示にした
・再生リストに追加する際、キャッシュファイルからタグ情報を読み込むようにした。(このせいで追加処理が遅くなってるきがしてる。あまりに問題なら、タイミングを再生時に移動するなどの工夫をするかも)


他にもこんなバグがあるよというのがありましたら報告をして頂けると助かります。


Saturday, August 9, 2014

AudioEffectつらみある

Activityで3画面で作っていたものを、Fragmentにしたため初期化のタイミングがだいぶ変わってしまいました。
そのせいで、AudioEffectへの更新処理が何度も届くバグを入れてしまったようです。

ぱっとみ使っている分には気づかないのですが、端末によっては音が鳴らなくなります。致命的

僕の端末だと数曲流してると突然音だけ聞こえない状況になります。
音が鳴らなくなったーという現象がある方はひとまず利用を停止してください。

Friday, August 8, 2014

Boxnetでもそこそこちゃんと動くようになりました。

ほっといたら動かなくなったり、長時間再生していると再生できなるといった不具合を修正しました。
それから、設定でフォルダの選択ダイアログや、詳細情報で選択中のフォルダやフォルダ使用量をサマリーで表示するように変更しました。

https://bitbucket.org/yokmama/just-player/downloads/JUSTPLAYER3_v4.0_314190008.apk

Thursday, August 7, 2014

Box.netに対応してみました

Box.net のユーザがJUST PLAYERを使っているということなので対応してみました。


Monday, August 4, 2014

Canvasに引いた線にグローエフェクトをつける

今日はメニューの整理をしていましたが、若干飽きてきたので、アルバムビューで表示している円のVisualizerにグローエフェクトをかけてみました。
わかりにくいかもしれないですが、下記の画像のようになります。


グローエフェクトは、PaintにBlurMaskFilterを設定することで表現できます。
実際にはBlurMaskFilterでぼかしをかけることでそれっぽく見せてるだけです。
具体的には次のようなコードでPaintを宣言しておきます。

        float width = 7f;
        float blueWidth = width*2.0f;

        mPaint = new Paint();
        mPaint.setStrokeWidth(width);
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeCap(Paint.Cap.ROUND);

        mBlurPaint = new Paint();
        mBlurPaint.set(mPaint);
        mBlurPaint.setStrokeWidth(blueWidth);
        mBlurPaint.setMaskFilter(new BlurMaskFilter(blueWidth/2, BlurMaskFilter.Blur.NORMAL));

ぼかし用のPaintのほうの太さを2倍ぐらいにして、ぼかし幅はその半分ぐらいにしていますが、これは調整していてこれぐらいがちょうど良かっただけなので、もっとぼかしたいというのであれば、BlurPaintのほうの太さをもっと太くし、ぼかし幅も大きくすれば良いと思います。

これをもとに、同じPathに描画を2回してあげれば上記のような画像になります。

            canvas.drawPath(path, mPaint);
            canvas.drawPath(path, mBlurPaint);

Saturday, August 2, 2014

NavigationDrawerのOpenをジェスチャーでは開かないないようにする

NavigationDrawerのOpenがViewPagerのスワイプと被ってるので、NavigationDrawerのジェスチャー処理を無効にしようと思ったのですが、最初はやりかたがわからなくて、DrawerLayoutクラスを継承してOnTouch辺りを強制的にぐりぐりしようかとおもったけど、もっと簡単にやる方法があったので助かった。

mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);

DrawerLayoutクラスにsetDrawerLockModeでDrawerLayout.LOCK_MODE_LOCKED_CLOSEDを設定すると期待していた動きになります。

ちなみに、このフラグは他にも

デフォルトの値、ジェスチャーでOpen,Closeする。
LOCK_MODE_UNLOCKED

ジェスチャーによってOpenしない。
LOCK_MODE_LOCKED_CLOSED

ジェスチャーでOpenしない上、クローズもしない。上のやつだと外部タップで自動で閉じる。
LOCK_MODE_LOCKED_OPEN

ということらしいです。

やっぱり、NavigationDrawerのジェスチャーはOFFにしていたほうが気持ちよく操作できますね。



迷ったら作ってみる

どんな具合になるのか作って実際に使ってみないとわかんないな。
ということで、ひとまず、スワイプによる画面移動、それとNavigationDrawerを置いたままにして、使い勝手が悪いかどうか実装してみました。

apk ink: https://bitbucket.org/yokmama/just-player/downloads/JUSTPLAYER3_v4.0_314190006.apk

注)上記のAPKはKitKatでしか動作チェックをしていないので、ICS、JerryBeansでちゃんと動くか試していません。

このバージョンでは、NavigationDrawerの中にあったイコライザ、AlbumArt画面の移動をスワイプで移動するように変更し、曲の選択だけNavigatioDrawerに残しています。


使ってみておもったこと。
やっぱり、NavigationDrawerの引き出しがスワイプ画面移動と被っていて操作しにくい。
誤動作も頻発する。

解決策として、Drawerのスワイプによる引き出しは無効にして、上部のボタンでOpenするようにしようかとおもっています。
あと、それだけじゃ気付きにくいので、ActionBarにもOpenのボタンを追加しようかと。
ActionBarにボタンが増えるのは嫌ですが。

昨日、他のクラウド上の音楽ファイルの再生アプリをいくつか触ってみて、JUST PLAYERだけしかできていないことを探してみたら、次のようなことが他とは違うと思っています。

・ Cloud上にあるM3U、CUEファイルによるプレイリストの読み込み
・ Cloud上にある音楽ファイルと同じフォルダにある画像をアルバムアートとして適用する。
・ 大きめの音楽ファイルも再生できる。
・ 複数のクラウドを混ぜこぜにして再生リストを作れる。
(もちろん保存も対応するよ)
・ ギャップレス再生ができる






Friday, August 1, 2014

NvigationDrawerとSwaipとUIで悩み中

今日はGooglePlayStoreに公開されている色んな音楽アプリを使ってみました。
理由は、現在開発中のアプリのUIをどうすればよくなるのか悩んでいるからです。


Perk,RMSメーターと、FFTのVisualizerを作った

前からあったけど、ちゃんと説明していなかったし、また見た目もしっかり作りこんでなかったので報告をしていなかったのですが、ひとまずまずまずの見た目が確保できたので報告しておきます。