2014-12-22

Medoly Ver. 1.8.0

1ヶ月ぶりぐらいのバージョンアップ。

2014-12-22 Ver. 1.8.0
- ピンチイン・ピンチアウトによる画面表示サイズ変更の追加
- スワイプ操作による画面切換の追加
- 音声フォーカスの一時喪失の処理が誤っていた問題修正


ピンチイン・ピンチアウトによる画面表示サイズ変更の追加


これはユーザ様からの要望です。
メイン画面のタブ枠内で、縦方向のピンチアウト・ピンチイン操作を行うと、タブの表示領域を拡大・縮小します。以前の設定オプションの表示拡大を拡張し、段階的に出来るようにした形です。
Android画面の、上方向のステータスバー及び下方向のナビゲーションバーまで非表示になります。なお、Android 4.4より前のバージョンではナビゲーションバーを非表示にしても、画面をタップするとナビゲーションバーは復活しますが、これは仕様です。また、画面を横した場合はナビゲーションバーが横になりますが、その場合も操作は変わらず下方向の操作となります。…一々操作を切り替えるのは、作る側も操作する側も面倒臭いですから。

なお、このピンチイン・ピンチアウト動作については少し変わった動作をします。2本以上タップした状態で、上側の指を上方向に動かすと、上側のアクションバー及びステータスバーが非表示になり、下側の指を下方向に動かすと、下側のGUI及びナビゲーションバーが非表示になります。非表示になる順番を以下の図に示します。

ピンチズームで拡大・縮小する順序



ちなみに、最大まで拡大すると、以下のような感じになります。





スワイプ操作による画面切換の追加


これは、上記のピンチズーム動作の追加に伴うもので、メイン画面タブ内をスワイプすると、タブの切換が行われるようになりました。これは、ピンチズームで画面を最大まで引き伸ばすとタブが隠れるため、その際にタブ切換が行えるようにする救済措置的な意味があります。
…実はこのスワイプ操作、最初の公開前の開発段階で実装するつもりではいたのですが、作り方がよく分からず後回しにした経緯があり、やっと実装できたという感じです。実際、スワイプやスクロール、クリック、ピンチズームといった各種操作を細かく制御する必要があり、ちょっと難しかったのですが、何とかそれっぽく動くようにはできたと思います。

なお、この実装にはAndroidのサポートライブラリが必要だったため、このバージョンより「android-support-v13.jar」を追加しています。そのため、実行APKのサイズが350KBほど増加しています。
今まで使ってなかったのかと言われそうですが、使わなくてもさほど困らなかったもので。そもそも、どうしてViewPagerクラスがサポートライブラリにしか含まれていないのか、その辺の理由がよく分かりません。

余談ですが、スワイプ操作ができるようになった事で、何か凄くスマートフォンアプリっぽくなったなぁ、と個人的に思ってます。今までは何となく、Windowsアプリケーションの延長線ぐらいの感覚だったのですが。


音声フォーカスの一時喪失の処理が誤っていた問題修正


アプリの標準状態で、他のアプリで一時的な音声が発生した場合、例えばメール通知やナビゲーション音声が発生した場合、音楽再生が中断していたと思いますが、これはバグでした。本来、一時的な音声フォーカスの喪失が発生した場合、音量を下げる処理を入れるつもりだったのですが、これが永続的な音声フォーカスの喪失の処理となってしまっていました。
そのため、今までの状態はメールの通知等で音楽が中断される状態となっていたと思いますが、これが音量を下げるようになりました。


その他


現在、アプリに別の処理を組み込んでいます。実はもう9割ぐらい実装済みなのですが、公開はもう少し先になると思います。とりあえず現在は埋め殺し状態です。現状でも、別途アプリを入れると動くようになりますが…。
その辺の兼ね合いでバグが発生する可能性もありますが、問題があれば報告していただけると幸いです。


2014-12-09

Medolyの仕様書

1週年なので小話を一つ。

Medolyには仕様書があります、一応。作り始める前に、イメージを固めるために、画面のイメージを描いてみたものです。描いた時期は多分、2013年の8月頃だと思います。






…仕様書です。仕様書ですったら仕様書です。
見ての通り、適当な代物です。ちなみに、その時手元にあったやや広めの付箋紙に描いてあります。1枚目はメイン画面(下)と検索画面(上)、2枚目は検索画面の検索欄です。この時点で大体今のアプリのイメージは固まってました。
当時は検索画面のタブを下側に置くつもりでいたと記憶しています。
あと文字が読めないので分からないと思いますが、この時点ではメイン画面の再生キューが右側のタブ、プロパティが左側のタブになってます。色々あって左右逆転して今の形になってます。UIの設計は色々考えながら今の形になっているのですが、時間があればその辺もここで書いてみたいと思ってます。

そんな感じで、1周年の小話でした。

Medoly 1周年

Medolyは2013/12/8にVer.1.0がリリースされたので、昨日で公開から丁度1年経ちました。


Androidで始めて作成したアプリだったので、1年前のリリース時はかなり色々問題がありましたが、この1年で機能追加やら改善やらで大分自分の理想 のアプリに近づいてきたように思います。なお、現在のバージョンは1.7.6。内部的なバージョンコードは50で、1年間で49回バージョンアップさせて います。



お使い頂いてる方には何となく分かるかと思いますが、とにかく歌詞の表示に注力しています。理由は、そういう機能を重視したアプリがさっぱり見られないからです。無いなら自分で作ってしまおう、というのが開発を始めた動機だったりします。

近年、音楽の販売はどんどんCD販売からネットワーク販売に移行していますが、CDは必ずジャケット等に必ず歌詞がついてました。ところが、音楽配信サービスは多くの場合、歌詞がついてない。それが当たり前になっている。歌詞を見たいなら、広告がベタベタ貼ってあって、コピー&ペーストも出来ない歌詞サイトとやらを利用することになります。

もうアホかとバカかと。なんでそれで満足してんの?自分で買った曲の歌詞ぐらい自由に利用させろ。そもそも、最初から従来の歌詞カードの代用になるデータを寄越せよ。ってのが個人的な本音です。

そういう不満タラタラな状態だったのですが、以前より歌詞データの仕様については色々調べていたこともあり、とりあえずそれを実現可能な形にしてみたのがMedolyです。多分、現状Google Playストアにあるどんな音楽プレイヤーよりも対応してる歌詞の種類は多いと思います。あと、ライブラリ依存ですが文字コード判定もそこそこ高い精度でできていると思うので、英語しか出せないようなアプリが多い中、比較的多くの言語で利用できると思います。要するに、言語・種別問わず多くの歌詞が表示できるのではないかと思います。

そんなこんなで、本アプリはまだまだ色々追加したい機能や改善したい部分が多数あるので、これからものんびり開発を続けていきたいと思います。もしご意見・ご要望等ありましたら、遠慮せずに連絡いただけると嬉しいです。

ちなみに、現状で考えてる追加機能は、
  • プラグインによる機能追加
  • 歌詞の表示レイアウト調整
  • プレイリストのエクスポート
など。もう少し色々ありますが、大きなところはそんな所です。
プラグインは、歌詞やアルバムアートの自動取得等の機能も込みで。その辺はまだまだ先の話ですが。どう実装したものかと手探りなところがありますが、温かい目で見て頂けると幸いです。

そんなこんなで、これからもよろしくお願いします。

2014-11-25

Medoly Ver. 1.7.5, 1.7.6

2014-11-02 Ver. 1.7.5
- MP3 内蔵歌詞 Lyrics3 v1, Lyrics3 v2 形式対応
- 以下の外部字幕ファイル対応
 * SRT (SubRip) フォーマット [.srt]
 * STL (Spruce subtitle) フォーマット [.stl]
 * SCC (Scenarist Closed Caption) フォーマット [.scc]
 * SSA/ASS (SubStation Alpha) フォーマット [.ssa/.ass]
 * TTML (Timed Text Markup Language) フォーマット [.xml]
- プロパティ情報のファイルパスをフォルダとファイルに分割
- フォルダの共有を追加

MP3 内蔵歌詞 Lyrics3 v1, Lyrics3 v2 形式対応


MP3は規格的に古い事もあり、長い歴史の中でいくつものタグ種別があります。
歌詞の内蔵方法にもいくつかの種類があり、主に以下のようなものがあります
・ID3v2 USLT
・ID3v2 SYLT
・Lyrics3 (Version1, Version2)

USLTは非同期歌詞を保存するタグです。単純なテキストが格納され、MP3で「内蔵非同期歌詞」と呼ばれるのはこれです。この歌詞タイプの場合、比較的多くのプレイヤーで読み取りができると思います。
SYLTは同期歌詞を保存するタグです。歌詞は単純なテキストではなく、テキストとタイムコードが交互に登場するような形となっています(参考: id3v2.3.0 - ID3.org )。この内容を独自に解析しているので、表示結果が怪しいところがあるかもしれません。Windows Media Playerのバージョンによっては、これが読み取れる場合があるようです。
Lyrics3は、ID3v1、ID3v2とも独立したタグです。名前の通り歌詞が保存されますが、タイトルやアーティスト情報も保存できるようです。Version1とVersion2がありますが、Version1は古い規格なので、Version2さえ使っておけば良いかと思います。歌詞はテキストで作られますが、その内容はLRCファイルと同一のフォーマットとなるようです。というか、LRCファイルがLyrics3の内容を外出ししているのでしょうか(その辺の経緯はイマイチ分かってません)。Medoly内部でも、Lyrics3の内容をLRCファイルの解析処理にそのまま流して処理しています。いくつかの同期歌詞対応ソフトは、この歌詞を利用しています。

これらは以前からずっと対応しようと思ってましたが、ようやく対応させることができました。これで、大体の内蔵歌詞は読み取れるようになったと思います。読み取り結果に怪しい部分があるかもしれませんが、その際は連絡頂けると幸いです。

なお、これを作っていて分かったのですが、海外のアプリは概ね「文字コード?何それ美味しいの?」レベルの対応しかしていないようです。そんなわけで、Shift_JISで歌詞を保存した場合、まともに表示できるアプリは非常に限られてくると思います。歌詞やタグをMP3に保存する場合は、Unicodeを用いるようにしましょう。Medolyはなにぶん日本人が作ってますので、Shift_JISでも何とかなるかと思いますが。

余談ですが、カラオケタグについて調べてるうちに、ルビの規格について考えてる記述を見つけました。余力があれば、この辺も何とかしてみたいところ。

タイムタグ規格ルビ拡張規格 [翔星 Be ランド| BeOS 特集]



以下の外部字幕ファイル対応


世の中には歌詞ファイル以外に、動画の字幕データを歌詞として用いる文化があるようです。私はほとんど見たこと無いですが。とりあえず「Mediaplayer for lyrics」に名前負けしないよう対応させてみました。対応に当たり、以下のライブラリを利用しています。

JDaren/subtitleConverter · GitHub

一応こういう事をやってる方もいるようです。

春日 - カラオケ字幕制作ソフト プロジェクト日本語トップページ - SourceForge.JP

これも余談ですが、対応させたフォーマットのうち、TTMLフォーマットというXMLファイルがあります。w3cで策定された、由緒正しいフォーマットです。

Timed Text Markup Language 1 (TTML1) (Second Edition)

これは字幕等の時間に合わてテキストを表示させたりするためのものですが、歌詞データにも利用できるようです。今は文字読み取りぐらいしかできませんが、文字修飾等も定義しやすいため、時間があればこの辺を色々弄り回して色々表示できるようにしてみたいところ。


プロパティ情報のファイルパスをフォルダとファイルに分割


プロパティリストで「ファイルパス」となっていた所を、「フォルダパス」と「ファイル名」に分割しました。理由は、そちらの方が利用しやすいからです。
主な利用は、次の項目を参照。

フォルダの共有を追加


上記で追加したフォルダパスについて「共有」アクションを行うことができます。対応するアプリに共有すると、該当のフォルダパスを閲覧することができます。現在対応を確認しているアプリは、「ES ファイルエクスプローラー 」と、「Solid Explorer File Manager 」です。
フォルダを共有する際、MIME Typeに「resource/folder」を設定しています。上記以外にこのMIME Typeに対応したアプリであれば、共有を受け取る事ができるはずです。

余談ですが RFC2425 によると、フォルダ(ディレクトリ)のMIME Typeは「text/directory」らしいのですが、これを設定しても上記のアプリは受け取ってくれませんでした。Linuxだと「inode/directory」の模様。Androidだと、特定の機能ディレクトリを開くために「vnd.android.cursor.dir/*」というMIME Typeを利用するようですが、ストレージフォルダを開くための使い方がよく分かりません。

上記のように、正しいディレクトリのMIME Typeが特定できなかったため、設定からMIME Typeの変更ができるようにしています。

2014-11-20 Ver. 1.7.6

- プレイリストのダイアログ修正 (一部機種で開けない問題に恐らく対応)
- スリープ復帰時にダイアログのボタンを押すとエラーとなる問題の対応追加
- 再生キューのドラッグ&ドロップ処理修正
- SDカード未認識時のエラー処理修正


プレイリストのダイアログ修正 (一部機種で開けない問題に恐らく対応)


これは、アプリのエラーレポートにあがっていました。
プレイリストを開こうとすると落ちるとのこと。エラー内容を見ると、どうもプレイリストの一覧を開く時にエラーが発生しているような様子なのですが、私の端末では発生しないため、どうも特定の機種で発生する事象のような感じです。調べてみると、同様の事象がヒット。

技なれど波高し 【解決】android ListViewのonTouchにてNullPointerException

要するに、きちんとリストを実装しなさい…と。確かに、ここについては標準のリストをそのまま使っており、特別な実装を何もしていなかった。そんなわけで、ここもきちんと実装を行うように修正。

ただ、この問題についてはこちらで修正されたかどうかの確認ができないため、このエラー報告をあげて頂いた SoftBank 201M (MOTOROLA RAZR M) を利用されている方、 もしエラーが直ったという場合は御一報いただけると幸いです。もし、まだ直っていない場合は再度エラーレポートをあげていただけると助かります。


追記

報告された方から連絡があり、プレイリストはきちんと開けたきちんと読み込めたそうです。
というわけで、リストはAndroidのデフォルトのやり方( android.R.layout.simple_list_item_1 等を使うやり方 )でやると危険なようです。アダプタのgetViewをオーバーライドして、自分で処理を書くようにした方が無難かと思います。


スリープ復帰時にダイアログのボタンを押すとエラーとなる問題の対応追加


これはAndroidの仕様なのですが、ダイアログを開きっぱなしの状態でスリープに入って、復帰した際に、ダイアログ上に保持していた情報が欠落してしまいます。その状態で処理を続けると、NullPointerException等のエラーが発生します。
正攻法としては、そうならないようにダイアログを作るのが筋なのですが、正直物凄く面倒臭いので、「情報が欠落したらその時はメッセージ出すから再操作してくれ」というスタンスで作ってます。その処理に漏れがあったため、エラーが発生していました。


再生キューのドラッグ&ドロップ処理修正


再生キューを「編集モード」にした際、順番を入れ替える事ができますが、ここの描画更新処理がおかしかったので修正しました。単純に、ライブラリの使い方を間違ってました。

SDカード未認識時のエラー処理修正


SDカードがアンマウントされた状態でエラーが発生すると、エラーを繰り返してしまい、物凄い勢いでバッテリーが消費されていくという最悪のバグがあったので、修正しました。…多分。
以前直したはずなのですが、ギャップレス再生対応した際に復活してしまった模様です。

Medoly Ver. 1.7.0, 1.7.1

2014-10-17 Ver. 1.7.0

- 歌詞の表示スタイル設定追加
- 設定画面を画面分割化
- 表示タブのメニュー一部修正
- 表示タブのボタン配置一部修
- 再生・停止ボタン連続押しの挙動変更
- スリープ時にCPUに負荷がかかる問題修正
- 停止時のオフセット調整時に歌詞が追従しない問題修正
- メディアエラー時の処理修正
- 未選択状態の曲送りボタン押下時動作修正
- 設定の文言修正

歌詞の表示スタイル設定追加


歌詞の表示について、以下の項目を設定できるようにしました。これらは設定画面から変更できます。
  • 背景色
  • 歌詞テキストの影色
  • 未再生歌詞のテキスト色
  • 再生済歌詞のテキスト色
  • 再生中歌詞のテキスト色
  • 再生中歌詞の背景色
  • 再生中歌詞のボーダー色
  • アルバムアートの透明度
  • フォント種別(Sans-serif, Serif, Monospace)
  • フォントスタイル(太字、斜体)
  • 表示位置(左寄せ、中央寄せ、右寄せ)
また、スタイルのプリセットを選択することで、初期表示に戻すことができます。
なお、フォント種別、フォントスタイルについては、使用するフォントが対応していない場合は変化しません。特に日本語フォントについては、対応していない場合が多いと思います。というか、太字以外に変化してるのを見たことないです。恐らく、ほとんどの人がそうだと思います。そんなわけで、実質英語歌詞向けの設定になってます。

この設定、本当は各自でプリセットを保存できるようにして、メイン画面のタブメニューからプリセットを一発で切り替えられるような機能を持たせたかったのですが、作るのが大変だったので今のところはこの辺で妥協。そのうち作るんじゃないかと思います。

なお、これに伴い以下のライブラリを追加しています。

attenzione/android-ColorPickerPreference · GitHub

設定画面を画面分割化


歌詞スタイル関連の設定が増えすぎたので、流石に1画面で表示させるのは辛くなってきました。そんなわけで、従来の1画面から種別ごとに画面分割するようにしました。


表示タブのメニュー一部修正


表示タブをタップした時に表示されるメニューのうち、歌詞文字サイズの変更と歌詞同期オフセットの変更について、メニューを修正しています。


表示タブのボタン配置一部修


表示タブをタップした時に表示されるボタンのうち、文字サイズのアイコンの左右を入れ替えてま
す。
今まで、 左側がサイズ拡大、右側がサイズ縮小だったのですが、これが逆転しています。理由は、通常は右側が+方向、左側が-方向に値が増減する方が一般的であると考えられるため、それに倣うようにしました。
表示タブのメニューを修正した際に色々ボタン配置を見直した結果を反映しています。


再生・停止ボタン連続押しの挙動変更


再生・停止ボタン(再生と停止を兼ねる単一のボタン)を押した際、ヘッドセットのボタンと同じ動作をさせていたのですが、これを切り分けるようにしました。理由は、再生・停止ボタンの連続押しで曲送り・曲戻し動作が発生してしまうためです。
ロック画面の再生・停止ボタンで曲送り・曲戻し動作が発生してしまう事象への対策です。


スリープ時にCPUに負荷がかかる問題修正


これは昔から存在していた問題。何も弄ってない夜中に突然CPU使用率が跳ね上がり、ずっとCPUリソースを消費し続けるという問題がありました。これは毎日起こるわけではなく、たまに発生する上に、どのタイミングで起こるか分からない、充電中しか発生せずバッテリー稼働の際は起こらないといった症状があり、原因がよく掴めてませんでした。


これについて完全に把握したわけではないのですが、この事象はアクティビティのonResumeイベントにあった、歌詞更新ループ開始処理をはじめとした各種処理を、onStartイベントに持ってきたところ、(恐らく)発生しなくなりました。
恐らく、意図しないタイミングで歌詞更新のループ処理が走っていたものと思います。また、ループ処理も一部見直して、起動してない場合は確実に停止させるようにしました。
とりあえず、onResumeイベントを使うのは最小限に抑えた方が良さそう、という事は分かりました…。


停止時のオフセット調整時に歌詞が追従しない問題修正


以前の修正で、歌詞の更新処理を見直していたのですが、オフセット調整時に歌詞スクロール位置が反映されなくなっていたので修正しました。

メディアエラー時の処理修正


メディアエラーが発生した際の処理を修正しました。これは、ギャップレス再生を追加したことに伴う修正漏れです。


未選択状態の曲送りボタン押下時動作修正


曲が未選択状態で曲送りボタンを押した場合、本来は先頭の曲が選択されるのですが、ギャップレス再生処理を追加した際にその動作が消えてしまい、メディアが存在しないと判定されていました。



設定の文言修正


設定画面を画面分割化に伴い、メニュー項目や分類を一部変更しています。
ちなみに、表には出てこないので利用者には全く関係無い話ですが、今まで設定項目の見出しや説明文の文字列を設定XMLに直接書いていたのですが、これだと 日本語・英語で同じXMLを2つ用意しないといけないという問題があり、設定項目の食い違いに気付きにくいという問題があったため、文字列は全部別の XMLに外だしするように変更しました。これが結構な大仕事でしたが、あくまでもユーザーには全く関係無い部分。




2014-10-26 Ver. 1.7.1

- メイン画面に再生キューの合計曲数、合計時間、再生済み曲数、再生済み合計時間の表示を追加

メイン画面に再生キューの合計曲数、合計時間、再生済み曲数、再生済み合計時間の表示を追加


タブの下側に、現在の再生曲No、再生済曲数、再生キュー曲数、再生済合計時間、再生キュー合計時間を表示するようにしました。ふと思いついて、適当に実装してみました。これに伴い、コントロール部分のレイアウトを多少詰めています。



曲数、再生時間の表示




2014-11-24

Medoly Ver. 1.6.8

久々に変更内容の詳細でも…。

2014-10-01 Ver. 1.6.8

- 音声フォーカス取得時にエラーとなる場合がある問題修正
- アプリ再開時に歌詞再生位置が先頭に戻される問題修正


音声フォーカス取得時にエラーとなる場合がある問題修正

Ver.1.6.5でオーディオフォーカスの処理を変更しましたが、その際にnullチェックが漏れていたため、NullPointerExceptionが発生してました。
メディアがセットされていない状態でオーディオフォーカスの変更が発生した場合、存在しないメディアプレイヤーオブジェクトにアクセスしようとしていたためです。

 

アプリ再開時に歌詞再生位置が先頭に戻される問題修正

再生を中断した状態からアプリを再開した時に歌詞のスクロール位置が先頭に戻されてしまい、シークバー上は再生途中になっていても歌詞が先頭位置になるという状態が発生していたため、その修正です。

2014-09-27

総ダウンロード数が500を越えた

Medolyは現在の所、総ダウンロード数が528、総インストール数が151。



アプリ情報




インストール数



総ダウンロード数

ちなみに、総ダウンロード数が500を越えると、Google Playに表示されるアプリの「インストール」項目が100~500 → 500~1000に切り替わるので、記念してここで書いてみた次第。

Google Play 情報


まぁ、ダウンロード数はさておき、これからものんびり作っていきます。

Medoly Ver. 1.6.5, 1.6.6, 1.6.7

ここ最近の変更について解説

2014-09-22 Ver. 1.6.5

- ギャップレス再生対応 (Android 4.1 以降)
- メイン画面点灯の設定追加(デフォルトで再生時のみ点灯とする)
- オーディオフォーカス処理の設定追加
- 曲戻し時に再生キューを未再生状態とする(設定で切換可)
- 本バージョンより、 android.permission.WAKE_LOCK パーミッションが追加されています

ギャップレス再生対応 (Android 4.1 以降)

このバージョンから、ギャップレス再生に対応しました。ライブ音源収録CD等の、連続した音楽がトラックで分割されている曲について、切れ目無く再生することができます。対応はAndroid 4.1以降となります。ただし、端末によっては対応されていない場合があります。AndroidのMediaPlayer標準のギャップレス再生方式を使用していますので、他のアプリでギャップレス再生できなければ、本アプリでも出来ないと思います、多分。
ギャップレス再生に対応するに当たり、内部の処理をかなり大きく見直しています。今までは、曲の切換をした際にMediaPlayerオブジェクトに対してファイルをセットし直すという処理を行っていました。今回の変更では、現在再生しているMediaPlayerオブジェクトに加え、次の曲をセットしたMediaPlayerオブジェクトの2種類を用意し、曲の切換時にMediaPlayerオブジェクトを入れ替えるという処理を行っています(入れ替えた時に、更に次のMediaPlayerオブジェクトを作成しています)。
これは、ギャップレス再生に対応する上で必要な変更なのですが、この結果として内部の処理を大幅に見直す必要がありました。
そんなわけで、かなり入念に確認をしたつもりなのですが、今回の変更で問題が発生する可能性は高いです。何かあれば、ご報告頂けると幸いです。


テストに使ってた曲


メイン画面点灯の設定追加(デフォルトで再生時のみ点灯とする)

今まで、メイン画面の点灯にチェックを入れると、メイン画面を表示している時は常に画面が点灯する(消灯しない)動作となっていましたが、これを設定画面で「常に点灯」「再生中のみ点灯」「歌詞表示中のみ点灯」「歌詞再生中のみ点灯」という4種類から選択できるようにしました。デフォルトは「再生中のみ点灯」となります。
これは、点灯状態で携帯を放置すると画面が表示されたままバッテリーを大きく消耗してしまうためです。これにより、電池が尽きてしまう状況に何度か遭遇したため、再生中のみ点灯するオプションを追加した次第。

オーディオフォーカス処理の設定追加

設定画面に、オーディオフォーカス(音声フォーカス)についてのオプションを追加しました(これに伴い、一部設定を削除しました)。オーディオフォーカスとは、アプリが音声についての各種操作を受け付けるために必要な設定です。これは、Android上で動作する何れか一つのアプリが持っており、アプリがオーディオフォーカスを取得すると、別のアプリのオーディオフォーカスが外れる、という動作をします。リモコンのボタンや、ロック画面のコントロールを受け付けるのは、オーディオフォーカスを持つアプリとなります。
今回、本アプリがオーディオフォーカスを取得するタイミングと、オーディオフォーカスが外れた場合の動作についての設定を追加しました。
オーディオフォーカスを取得するタイミングは、「取得しない」「アプリ起動時」「再生開始時」「アプリ起動時&再生開始時」の4種類から選択します。デフォルトは「アプリ起動時」です。「取得しない」とすると、オーディオフォーカスを取得しないためリモコン関係の設定は無効になります。「アプリ起動」とすると、アプリを起動した時にオーディオフォーカスを取得しますが、音楽再生中に別のアプリにフォーカスを奪われると、アプリを再起動するまでリモコン操作を受け付けなくなります。
オーディオフォーカスが外れる場合、一時的に外れる場合と、永続的に外れる場合があり、それぞれについての動作を設定できます。動作は、「再生を停止する」「音量を下げる」「何もしない」の3種類から選択します。それぞれ、メッセージを表示することができます。

曲戻し時に再生キューを未再生状態とする(設定で切換可)

本アプリは、曲を再生すると曲が「再生済」状態となります。従来はこの状態から曲戻しをすると、再生済状態のままとなっていました。今回の変更では、曲戻しをすると未再生状態に戻されます。
この理由は、一度「曲戻し」をした後に「曲送り」をすると、再生済みの曲がスキップされるため、「曲戻し」前の曲には戻る事ができなかったためです。聞きたい曲を探して曲送りを連打する→行きすぎたので曲戻しをする→やっぱり行きすぎた先の曲も聞きたい、という事がしょっちゅうあったので、このような動作に変更しました。


本バージョンより、 android.permission.WAKE_LOCK パーミッションが追加されています

今まで、MediaPlayerオブジェクトに setWakeModeでWakeModeを設定していなかったのですが、今回、PowerManager.PARTIAL_WAKE_LOCK を設定するようにしました。この変更に伴い、android.permission.WAKE_LOCKパーミッションが必要となっています。
正直、これの効果はよく分かっていません。ただ、これが無いと音楽再生中にアプリが中断してしまうことがあるようです(私の環境では発生していないのですが)。再生を維持させるための設定のようなのですが、効果を確認する方法がわかりません。まぁ、おまじないみたいなものと考えています。


2014-09-24 Ver. 1.6.6

- リモート操作が復帰しない場合がある問題修正
- 曲切換時に再生キューがスクロールしない問題修正
- 終了時にメディア状態が保存されない問題修正
- 設定文言一部修正


リモート操作が復帰しない場合がある問題修正

音声フォーカスの取得処理がおかしかったバグの修正です。

曲切換時に再生キューがスクロールしない問題修正

曲が切り替わった際に、再生キューの自動スクロールをしてもスクロールが発生しないバグの修正。ギャップレス再生の対応に伴い、処理を入れ忘れました。

終了時にメディア状態が保存されない問題修正

メニューの「終了」から終了させた場合、メディアの再生位置等が保存されない問題があったので修正。これは既存バグ。

設定文言一部修正    

設定画面の文言を一部修正しています。


2014-09-26 Ver. 1.6.7

- 単体再生時の曲送り・曲戻し動作を修正


単体再生時の曲送り・曲戻し動作を修正

単体再生時、曲送りを押した際に次メディアがあっても「メディアがありません」と言われてしまう問題修正。ギャップレス再生対応に伴うバグです。

2014-09-16

再生履歴の日付表示について

再生履歴に表示される「x日前」という日付表示について、言い訳っぽい話。

再生履歴の一覧は、日付毎に見出しがつきます。2日前なら「2日前」、1週間以上前なら「x月x日」という日付で表現されます。
ところが、本日再生分と1日前再生分のタイトルは、恐らくAndroidのバージョンによって変わってきます。 どこから変わってくるのかよく分かりませんが、Android 4.4 (CyanogenMod 11)では、「今日」「昨日」と表示され、Android 4.1 (SOL22)、Android 4.0.4 (ISW13HT) の場合は、「0日前」「1日前」と表示されてしまいます。
これは、DateUtilsのgetRelativeTimeSpanStringメソッドの挙動です。「今日」「昨日」と表示されるのが、本来正しい表示かと思います。とりあえず、バージョンや端末によって違ってくるかと思いますが、「0日前」と表示されたところで動作に影響があるわけではないので、この辺の表示はとりあえずこのままの方向で。



SOL22 (Android 4.1)

CyanogenMod11 (Android 4.4)

2014-09-14

Medoly Ver. 1.6.0, 1.6.1, 1.6.2


Ver. 1.6.0 変更履歴


2014-08-07 Ver. 1.6.0
- ロック画面コントロール追加 (設定でON/OFF切換)
- 通知バーからコントロール可能に変更
- 再生履歴のディスク番号が正しく表示されない問題修正
- 再生履歴が正しくチェックされない問題修正


ロック画面コントロール追加 (設定でON/OFF切換)


ロック画面へのコントロールを追加しまいた。Android 4.0以降の対応となります。
設定画面でON/OFFできます。ロック画面コントロールのデザインはこちらからは制御できず、端末の機能に依存します。「ロック画面をこうしたい」という場合は、端末メーカーまでご要望をお伝え頂ければ…。




通知バーからコントロール可能に変更

シンプルな通知バーだったものを、コントロール可能な通知バーに変更しました。Android 4.0以降の対応となります。



再生履歴のディスク番号が正しく表示されない問題修正

再生履歴のトラック番号が正しく表示されなかった問題を修正しました。昔からある潜在的な問題でした。

再生履歴が正しくチェックされない問題修正

チェック項目の表示処理が間違っていました。



Ver. 1.6.1 変更履歴

2014-08-07 Ver. 1.6.1
- 通知バーで文字が重なる問題修正

通知バーで文字が重なる問題修正

通知バーのコントロールを作成した際、レイアウトの問題で文字が長いと文字同士が重なる問題があったため、修正しました。

Ver. 1.6.2 変更履歴

2014-08-07 Ver. 1.6.2
- 歌詞のスクロール処理を修正

歌詞のスクロール処理を修正

歌詞が正しくスクロールされない問題を修正しました。

Medoly Ver. 1.5.5, 1.5.6, 1.5.7, 1.5.8


Ver. 1.5.5 変更履歴

2014-08-01 Ver. 1.5.5
- メイン画面拡大機能追加
- ヘッドセットボタンの複数回押しによるメディア切換機能追加
- 歌詞の行末尾にタイムタグがある場合のアクティブ化処理を変更
- 共有されたAndroid DB未登録メディアを再生キュー登録可能に変更
- アルバムアート背景を白に設定できなくなっていた問題修正
- 再生履歴のスクロールで落ちる場合がある問題修正
- 再生履歴にトラック番号表示するよう修正
- サムネイル読込み処理修正
- アプリ起動時に再生キューが現在メディアにスクロールしない問題修正


メイン画面拡大機能追加

表示タブのメニューに「メイン画面の拡大」項目を追加しました。メイン画面下部のコントロール領域と、Androidの通知バーを非表示にします。とにかく表示領域だけを大きく取りたい用途向けです。どのような用途を想定しているかと言うと、歌詞やアルバムアートだけを表示させたり、HDMI等で外部出力した際に、操作部分を隠して表示部分だけを大きくしたい場合等です。
まぁ、思いつきで追加した機能なので、もう少し色々検討するかもしれません。こういう事もできますよ、という実験みたいなものなので。


ヘッドセットボタンの複数回押しによるメディア切換機能追加

今までもヘッドセットのボタンを押すと、再生・停止を切り替えることができましたが、2回押しで次の曲、3回押しで前の曲に切り替える機能を追加しました。
iPhoneのように、「2回押し+押しっぱなし」で早送り、「3回押し+押しっぱなし」で巻き戻し…という機能はありません。ボタンを押しっぱなしにするとAndroid標準の音声検索が起動してしまうため、実装することができませんでした。キーを横取りできるかもしれませんが、そこまで頑張る必要はないかなぁ…と。
この機能を利用するには、設定画面よりONにします。複数回押しを検出させるために、ボタンを押した際の反応を少し遅らせていますので、使わない場合はOFFの方が良いでしょう。



歌詞の行末尾にタイムタグがある場合のアクティブ化処理を変更

歌詞の表示について。細かい話かもしれないけど、こだわる人には多分重要な変更。
タイムタグが設定された同期歌詞において、1行の末尾にタイムタグがある場合。例えば以下のような感じ。

[00:00:00]
[00:10:00]あ[00:11:00]い[00:12:00]う[00:13:00]え[00:14:00]お[00:15:00]
[00:20:00]

この場合、今までは開始10秒で2行目の歌詞がアクティブ状態になり、20秒で3行目がアクティブになるまで、2行目がアクティブ状態となっていました。
今回の変更で、 15秒で2行目が非アクティブ状態となり、3行目がアクティブになる20秒まではアクティブ行が存在しない状態になります。
これにより、ボーカルの切れ目等を表現できるようになります。

 

共有されたAndroid DB未登録メディアを再生キュー登録可能に変更

今までは、Android DBに登録された曲しか再生することができなかったのですが、未登録曲も再生できるようになりました。未登録曲は、他のアプリから本アプリに対して「共有」させることで再生キューに登録され、再生できるようになります。ただし、この曲はプレイリストに登録することはできません。Androidのプレイリスト仕様上不可能です。一応、独自プレイリストも作れるようにしてはあるので、時間がある時にでも実装してみます。


アルバムアート背景を白に設定できなくなっていた問題修正

アルバムアートの背景は白・黒で切り替えられるようになっていますが、白くできなくなっていたので修正しました。ボタンの背景画像を弄った際に誤って修正していました。






再生履歴のスクロールで落ちる場合がある問題修正

再生履歴のリストについての問題です。サムネイルの取得に失敗していました。


再生履歴にトラック番号表示するよう修正

再生履歴のアルバム名にトラック番号を表示するようにしました。


サムネイル読込み処理修正

サムネイル読込み処理を修正しました…が、これは後のバージョンで再度修正されています。


アプリ起動時に再生キューが現在メディアにスクロールしない問題修正

アプリ起動時に、再生キューがスクロールしない問題を修正…したつもりが、あまり修正されていません。調査中です。


Ver. 1.5.6 変更履歴

2014-08-02 Ver. 1.5.6
- サムネイル表示にアニメーション追加
- 再生履歴の表示が遅くなる問題修正
- 余分なスクロール処理を省略するように修正


サムネイル表示にアニメーション追加

サムネイル表示時に、短時間のフェードインアニメーションを追加しています。
サムネイル表示の際に、キャッシュから取得する場合と、アルバムアートから取得する場合があります。この2パターンの取得は当然表示までの時間差があります。スクロールしていった場合、キャッシュ画像は画面に入った段階で表示済みになりますが、画像は一旦サムネイルに落とし込むため、表示に時間がかかります。そのため、表示タイミングに差が生まれて違和感がありました。そのため、一律で短時間のフェードインアニメーションを追加することで差異を少なくして、違和感を緩和させています。
…この違和感は、私だけかもしれません。


再生履歴の表示が遅くなる問題修正


再生履歴ダイアログにリストを表示した場合、サムネイルが1枚読込まれる度に画面全体のレイアウトが再読込され、全ての表示項目にgetViewメソッドが呼ばれ、表示が非常に遅くなっていました。これはリストの高さを固定していないことで発生する事象です。

参考: [Android] ListViewのlayoutをwrap_contentで指定しない | Developers.IO

通常のActivity上で行う分には発生しにくいと思いますが、ダイアログに表示した場合、高さにmatch_parentを指定するだけではダメで、ダイアログに表示するビューの高さを固定しないと割と簡単に発生します。 これの原因を突き止めるのは割と苦労しました…。



余分なスクロール処理を省略するように修正

スクロール処理が複数回発生していたポイントがあるので、それを修正しました。



Ver. 1.5.7 変更履歴

2014-08-03 Ver. 1.5.7
- サムネイルの更新処理を修正
- 再生履歴ダイアログの末尾が見えない問題修正
- シャッフル再生において正しくシャッフルされていなかった問題修正

サムネイルの更新処理を修正

サムネイルの取得・更新処理を修正しました。バグ修正です。


再生履歴ダイアログの末尾が見えない問題修正

Ver.1.5.6でダイアログの高さを固定しましたが、固定した高さがおかしく、最後の行が途中で切れてしまう問題があったので、固定の高さを修正しました。

シャッフル再生において正しくシャッフルされていなかった問題修正


シャッフルの処理が、ずっと処理を間違ってました。再生キューをクリアせず、曲の追加・削除を繰り返すとシャッフルがどんどんズレていくという問題があったため、修正しました。


Ver. 1.5.8 変更履歴


2014-08-03 Ver. 1.5.8

- リストのサムネイル読込み時に落ちる問題修正


リストのサムネイル読込み時に落ちる問題修正

タイトルの通り。バグ修正です。

Medoly ver. 1.5.0

1.5.0という、ちょっと節目っぽいバージョンだったので、この時は結構更新を頑張りました。

変更内容



2014-07-19 Ver. 1.5.0
- 再生履歴機能追加
- タブメニューのデザイン変更
- 再生順メニューのデザイン及び挙動変更
- 通常再生/シャッフル再生切換時の処理を高速化
- タブタップ時の色替え追加
- ハードウェアキーの早送り・巻き戻し対応
- サムネイルの非表示設定追加
- 設定項目一部修正
- 内蔵アルバムアートが共有できなくなっていた問題修正
- キューを削除した場合に落ちる場合がある問題修正
- 歌詞の先頭空白行でスクロールしない問題修正
- ボタン背景やアイコンデザインをいくつか修正
- 内部の処理をいくつか修正



再生履歴機能追加

再生キュータブのメニューから呼び出します。再生済み曲のリストを表示し、再生順と逆順で表示されます。
一瞬でも再生すると追加されます。再生履歴上に存在する曲を再生した場合、過去の履歴は削除され、新たに先頭に追加されます。 
再生履歴の曲を選択し、「開く」ボタンを押すと再生キューに追加します。再生キューに曲が存在する場合は、再生キューの末尾に追加するか、再生キューをクリアして新たに追加するかの確認ダイアログが表示されます。(プレイリストを開く場合と同様)
設定画面で、保存履歴数を0~999で変更を行うことができます。多くすると、リストが開くまで時間がかかるかもしれません。 0にすると、タブメニューの項目自体表示されなくなります。
とりあえず簡易的なもので、それほど高機能なものではありません。




タブメニューのデザイン変更

タブのメニューを丸みを帯びた吹き出しのようなデザインに変更しました。機能的な変更はありません。今まではAndroid標準の「PopupMenu」というクラスを利用していたのですが、これは表示位置やデザインの調整ができないため、メニューが下に表示されてしまう等の問題がありました。これを「PopupWindow」クラスに変更することで、表示位置を調整できるようにすると共に、メニューを自由にデザインできるように修正しました。 (逆に自分で色々作り込まないといけなくなるので、ちょっと面倒でした。)


再生順メニューのデザイン及び挙動変更

再生順メニューも、タブメニュー同様に変更しました。また、ここのメニューはチェックを切り替えてもメニューが閉じません。閉じるには、枠外をタップします。この挙動の変更は、再生順「通常」・再生完了「停止」から、再生順「単体」・再生完了「リピート」に切り替えたいと思う場合、複数のチェック切換が必要になるため、一々ポップアップメニューが閉じられてしまうと作業が繁雑になってしまうためです。

通常再生/シャッフル再生切換時の処理を高速化

本アプリは、曲の再生順を全て自前で制御しています。
そのため、再生順の「通常」と「シャッフル」を切り替えるとDB上の再生キューの再生シーケンス番号を更新する処理が走ります。 この時の処理を変更(無駄な処理を排除)して、更新を高速化しました。
再生キューに多数の曲が登録されていると、流石に処理が一瞬止まってしまうのはご了承ください。(もしかしたら改善策はあるかもしれません。)



タブタップ時の色替え追加

メイン画面のタブをタップした際に、タブをハイライトするようにしました。操作に対する視覚的なフィードバックを与えるという、UIの改善です。

ハードウェアキーの早送り・巻き戻し対応

早送り・巻き戻しのハードウェアキー(リモコンのキー)に対応しました。
…対応してるはずです。「はず」というのは、早送り・巻き戻しのボタンを持ってるリモコンを持ち合わせていないので、テストしていないためです。 早送り・巻き戻しを押した時と同様の処理をしているので、動くはずです。…理論上は。

サムネイルの非表示設定追加

サムネイルを非表示に出来るようにしました。設定画面から変更します。設定を変更すると、再生キュー、検索画面、再生履歴のサムネイルを非表示にします。非表示にすると、スクロールが高速になります。もちろん、サムネイルが表示されなくなるので、曲を探す際の視認性は悪くなります。何はともあれ動作速度が欲しい人向け。

設定項目一部修正

設定項目を一部見直しました。
細かい内容は忘れました…。

内蔵アルバムアートが共有できなくなっていた問題修正

プロパティタブから内蔵アルバムアートを「共有」できるようにしたつもりできたが、出来ていませんでした。バグ修正です。

キューを削除した場合に落ちる場合がある問題修正

バグ修正です。

歌詞の先頭空白行でスクロールしない問題修正

同期歌詞で歌詞の先頭が空白だった場合、初期位置が先頭に来ない問題があったため、修正しました。

ボタン背景やアイコンデザインをいくつか修正

歌詞・アルバムアート表示タブに表示されるボタンの背景の修正です。
このボタンは元々曰く付きで、Androidでデフォルトで用意されている「Holo」というテーマ標準のボタンは、透明度が高すぎて単純に配置しただけでは背景のアルバムアート画像が透けすぎてボタンが見えにくいという難点がありました。これを変更する方法が分からず、ボタンの後ろにもう1枚オブジェクトを重ねて不透明化するという荒技をやっていました。
…が、いい加減何とかしようと思って調べたもののやっぱり分からない…というか、多分設定から変更するような方法は無いと思うので、 ボタン背景画像の差し替えを行いました。
以下のサイトから、Holoと同様のイメージを生成できます。




内部の処理をいくつか修正

細々したバグや挙動の修正です。
何やったかは忘れましたが、変更履歴に書いたので、多分何かやったのでしょう。


Medoly ver. 1.4.7

ちょっと時間が出来たので、凄い久しぶりに過去の更新内容詳細についてまとめていきたいと思います…。

変更内容


2014-07-01 Ver. 1.4.7
- ウィジェットに再生アイコンを表示するよう変更
- 画像読込み処理修正
- 検索画面の状態保存処理一部修正
- 再生中の曲情報が更新されない場合がある問題修正


ウィジェットに再生アイコンを表示するよう変更

これ。



画像読込み処理修正

画像の読込み処理を直して、大きな画像を読込んだ時に余分なメモリを消費しないようにしました。

検索画面の状態保存処理一部修正

検索画面を再表示した際に、閉じる前の状態に戻す処理が一部おかしかったので修正。

再生中の曲情報が更新されない場合がある問題修正

タイトルの通り。バグの修正です。


2014-06-24

Medoly ver.1.3.5 ~ 1.4.6

物凄い勢いで更新をサボってるので、最近の大雑把に変更点を…。



再生キュー処理の高速化


表面的には変わってませんが、再生キューの動きを高速化しています。いくつか変更を施していますが、主に以下のような変更になります。以前は、再生キューに5000曲ぐらい入れるとまったく使い物になりませんでしたが、これで大分マシになりました。(そんな使い方をする人がいるかどうかはともかく。)
  • 同期的に読込んでいたサムネイル画像を非同期で読込むように変更しました。そのため、スクロールの引っかかりが少なくなっています。
    またそれに伴い、以前は再生キュー登録時にサムネイルの縮小率の計算を行っていましたが、それを削除しました。そのため、再生キューへの登録が大幅に高速化されています。端末のスペックにもよりますが、100曲程度の登録なら一瞬で終わります。
  • タップした再生キュー項目のインデックスを取得する処理を見直してます。これに伴い、タップしてから再生開始までの間隔が短くなっています。
  • 再生キューテーブルの余計なデータを削除し、テーブルを少し修正しました。また、インデックスを追加しました。これにともない、読込処理が少し高速化されています。


早送り・巻き戻し追加

今さらながら、早送り・巻き戻しボタンを追加。設定で表示・非表示可能。
1回のタッチで設定秒数だけシーク、押しっぱなしで早送り・巻き戻しという動作になります。早送り・巻き戻しとは言っても、何かそれっぽく動かしているだけで、実際のところは断続的なシークの繰り返しで擬似的に早送り・巻き戻しをやってるだけです。200ms毎にシークし、押す時間が長いと段々シーク秒数が増えていくような動作になります。一般的な動作がよく分からないので、感覚的に適当に実装しました。








再生キューの削除をダイアログ方式に変更


従来メニューにあった再生キューの削除アクションを、ダイアログ方式にしました。削除までに、メニュー表示→ダイアログ表示→コマンド選択の3アクションになったので、やや煩わしくなったかもしれません。その代わり、いくつかの削除方法を追加しました。また、再生完了メディアの自動削除、エラーメディアの自動削除オプションを追加しています(設定画面にあったものを移動)。




プロパティタブの機能追加

  • プロパティリストにファイル共有処理を追加しています。音楽ファイル、アルバムアート、歌詞ファイルの「共有」ができます。また、歌詞についてはクリップボードへのコピーも可能となっています。 プロパティをタップした時に表示されるメニューから実行できます。
  • プロパティの一部項目から直接Web検索可能としました。
  • 上記アクションを追加したため、分かりやすくするためにプロパティリストに実行可能なアクションを示すアイコンを追加しました。




検索画面のソートアイコン追加

検索画面のソートアイコンを追加しました。(従来、設定画面にあったものと同等です。)
このアイコンをタップすることで、検索内容のソート順を変更できます。ただし、「ストレージ」タブの内容は変化しません。


検索画面の長押しメニュー追加

検索リスト項目を長押した時にメニューを表示し、再生キュー登録の細かい動作を指定することができます。とりあえず、簡単な動作を設定することができます。 このダイアログの設定は、このダイアログ以外の再生キュー追加動作に影響を与えません。





検索画面のストレージ表示状態で、ホームアイコン長押しによるホームパス設定追加

検索画面のストレージタブでホームタブを長押しすると、現在タブに表示されているパスをホームパスに設定できます。ただのオマケ機能です。

英語対応

…とりあえず英語対応させてみましたが、多分酷い英語です。とりあえずツッコミ待ちです。
この変更を行った後に英語でメールを頂いたので、 多少意味があったような気がします。


その他

非同期歌詞のスクロール位置を保持するようにするなど、細かい変更はいくつもありますが、とりあえず省略。多分これからも細かい修正は色々行っていきたいと思います。


今回、設定画面の項目をいくつかをメイン画面や検索画面に移動させています。これは、個人的に設定項目は可能な限り設定画面に置かず、操作する対象の近くに置きたいという方針のためです。今後もこうした変更はちょくちょく加えていくと思います。









2014-05-06

Medoly ver.1.3.1 & 1.3.2 & 1.3.3

Medoly


ちょっと変更。

Ver. 1.3.1

 

検索画面の検索条件やスクロール位置をなるべく保持させる処理を追加

アプリの再起動時や検索画面に戻った時、検索条件を切り替えた時に、検索条件やスクロール位置を可能な限り保持するようにしました。
「可能な限り」というのは、曲が追加されたり変更された際に、どうしても位置を保持することが難しくなるため、そういった場合はズレが生じてきます。
保持させるタイミングや復帰させるタイミングがかなり入り組んでいて、色々面倒臭い処理になっているため、もしかしたらリセットされる場合があるかもしれません…。

再生キューのトラック・年が隠れないようにレイアウトを変更

再生キューには、アルバム名にディスク/トラック番号、アーティスト名に年が併記されていますが、アルバム名・アーティスト名が長い場合は ディスク/トラック番号・年が枠外に隠れてしまっていました。そんなわけで、 ディスク/トラック番号・年が隠れないようにレイアウトを修正しました。(アルバム名・アーティスト名のみが省略されます。)

Ver. 1.3.2

 

検索画面のアクティビティを再表示させた際、タイトル表示になってしまう問題を修正

Ver.1.3.1の修正ミスです。アクティビティ再表示が上手くいかないバグがありました。

Ver. 1.3.3

 

歌詞の多段表示対応

同じ時間で2行以上の歌詞がアクティブの場合、表示上も同時にアクティブにするようにしました。
2行以上が同時にアクティブになる場合は、行内にタイムタグが設定されている場合です。
例えば、以下のようなKRAファイルがあるとします。

[00:01:00] あ[00:02:00]い[00:03:00]う[00:04:00]え[00:05:00]お[00:06:00]
[00:04:00] か[00:05:00]き[00:06:00]く[00:07:00]け[00:08:00]こ[00:09:00]

上記の場合、再生開始から4~6秒の間は、1行目と2行目が両方アクティブな状態です。
文字単位で見ると、5秒時点では、1行目は「あ、い、う、え、お」が、2行目は「か、き」がアクティブな状態になります。
これは例えば、曲の中でメインボーカルとコーラスが被っている部分を表現する、といった利用を想定しています。

KRA -> LRC, LRC -> KRA 変換スクリプト

歌詞定義フォーマットの、LRCファイルKRAファイル(タイムタグフォーマット)の変換用WSHスクリプト(JScript)を作成したので公開。KRA -> LRC変換と、LRC -> KRA変換の2つ。WSHスクリプトなので、Windows専用。

[使い方]
  1. KRA -> LRC変換スクリプトの場合はKRAファイル(*.kra)をドラッグ&ドロップします。
    LRC -> KRA変換スクリプトの場合はLRCファイル(*.lrc)をドラッグ&ドロップします。
  2. KRAファイルの場合は、同じフォルダ上にLRCファイル(KRAファイル名.lrc)が作成されます。
    LRCファイルの場合は、同様にKRAファイルが作成されます。
[注意事項]
  • 埋め込まれているタグのうち、 タイトル、アーティスト、アルバム、作詞者、曲の長さ、ファイル作成者以外の情報は削除されます。
  • UnicodeのBOMが不要な場合は、コメントアウトしてある「// delete BOM」の辺りをコメント解除すると、BOMが削除されます。
  • あんまり真面目にテストしてないので、きちんと動かない場合があるかもしれません。 
  • とりあえず拡張子固定なので、拡張子lrcでKRAフォーマットにしている人や、拡張子txtでフォーマット定義してる人は知ったこっちゃないです。


KRA -> LRC変換WSHスクリプト


/*==== 設定 ==================================================================*/

var inputExt = /\.kra$/i;
var outputExt = ".lrc";
var inputEncoding = "_autodetect_all";
var outputEncoding = "UTF-8";

/*============================================================================*/



/*==== 定数 ==================================================================*/

// 保存データの種類
// StreamTypeEnum
// http://msdn.microsoft.com/ja-jp/library/cc389884.aspx
var adTypeBinary = 1; // バイナリ
var adTypeText   = 2; // テキスト

// 読み込み方法
// StreamReadEnum
// http://msdn.microsoft.com/ja-jp/library/cc389881.aspx
var adReadAll  = -1; // 全行
var adReadLine = -2; // 一行ごと

// 書き込み方法
// StreamWriteEnum
// http://msdn.microsoft.com/ja-jp/library/cc389886.aspx
var adWriteChar = 0; // 改行なし
var adWriteLine = 1; // 改行あり

// ファイルの保存方法
// SaveOptionsEnum 
// http://msdn.microsoft.com/ja-jp/library/cc389870.aspx
var adSaveCreateNotExist  = 1; // ない場合は新規作成
var adSaveCreateOverWrite = 2; // ある場合は上書き

/*============================================================================*/



var args = WScript.Arguments;
if (args.length == 0) {
 WScript.Echo("引数を指定するか、ファイルをドラッグ&ドロップしてください。");
 WScript.Quit();
}

for (var i = 0; i < args.length; i++) {
 var path = args(i);
 if (path.match(inputExt) != null) {
  writeLyrics(path);
 }
}

function writeLyrics(path) {
 var streamReader = new ActiveXObject("ADODB.Stream");
 var streamWriter = new ActiveXObject("ADODB.Stream");
 try {
  // read
  streamReader.Open();
  streamReader.Type = adTypeText;
  streamReader.Position = 0;
  streamReader.Charset = inputEncoding;
  streamReader.LoadFromFile( path );
  var text = streamReader.ReadText( adReadAll );
  if (text.charCodeAt(0) == 0xFEFF || text.charCodeAt(0) == 0xFFFE) {
   text = text.substr(1); // delete BOM
  }
  
  // replace
  text = text.replace(/(^\s+)|(\s+$)/g, "");
  text = text.replace(/(\r\n|\n|\r|^)\[(\d\d:\d\d)\:(\d\d)\]/g, "$1[$2.$3]");
  text = text.replace(/\[(\d\d:\d\d)\:(\d\d)\]/g, "<$1.$2>");
  
  text = text.replace(/@Title= *(.*?)(\r\n|\n|\r|$)/g, "[ti:$1]$2");
  text = text.replace(/@Artist= *(.*?)(\r\n|\n|\r|$)/g, "[ar:$1]$2");
  text = text.replace(/@Album= *(.*?)(\r\n|\n|\r|$)/g, "[al:$1]$2");
  text = text.replace(/@Lyricist= *(.*?)(\r\n|\n|\r|$)/g, "[au:$1]$2");
  text = text.replace(/@Length= *(.*?)(\r\n|\n|\r|$)/g, "[length:$1]$2");
  text = text.replace(/@TaggingBy= *(.*?)(\r\n|\n|\r|$)/g, "[by:$1]$2");
  
  var offset = text.match(/\@Offset= *(.*?)(\r\n|\n|\r|$)/);
  if (offset && offset.length >= 2) {
   text = text.replace(/\@Offset= *(.*?)(\r\n|\n|\r|$)/, "[offset:" + (offset[1] * -1) + "]$2");
  }
  
  text = text.replace(/(\@.*=.*?)(\r\n|\n|\r|$)/g, "");
  
  // write
  streamWriter.Type = adTypeText;
  streamWriter.Charset = outputEncoding;
  streamWriter.Open();
  streamWriter.WriteText(text, adWriteChar);
  
  /*
  // delete BOM
  var bomSize = 0;
  if (outputEncoding == "UTF-8") {
   bomSize = 3;
  } else if (outputEncoding.indexOf("UTF-16") == 0) {
   bomSize = 2;
  }
  streamWriter.Position = 0;
  streamWriter.Type = adTypeBinary;
  streamWriter.Position = bomSize;
  var buffer =  streamWriter.Read();
  streamWriter.Position = 0;
  streamWriter.Write(buffer);
  streamWriter.SetEOS();
  */
  
  streamWriter.SaveToFile( path.replace(inputExt, outputExt), adSaveCreateOverWrite );
 } finally {
  if (streamReader != null) streamReader.Close();
  if (streamWriter != null) streamWriter.Close();
 }
}

LRC -> KRA変換WSHスクリプト


/*==== 設定 ==================================================================*/

var inputExt = /\.lrc$/i;
var outputExt = ".kra";
var inputEncoding = "_autodetect_all";
var outputEncoding = "UTF-8";

/*============================================================================*/



/*==== 定数 ==================================================================*/

// 保存データの種類
// StreamTypeEnum
// http://msdn.microsoft.com/ja-jp/library/cc389884.aspx
var adTypeBinary = 1; // バイナリ
var adTypeText   = 2; // テキスト

// 読み込み方法
// StreamReadEnum
// http://msdn.microsoft.com/ja-jp/library/cc389881.aspx
var adReadAll  = -1; // 全行
var adReadLine = -2; // 一行ごと

// 書き込み方法
// StreamWriteEnum
// http://msdn.microsoft.com/ja-jp/library/cc389886.aspx
var adWriteChar = 0; // 改行なし
var adWriteLine = 1; // 改行あり

// ファイルの保存方法
// SaveOptionsEnum 
// http://msdn.microsoft.com/ja-jp/library/cc389870.aspx
var adSaveCreateNotExist  = 1; // ない場合は新規作成
var adSaveCreateOverWrite = 2; // ある場合は上書き

/*============================================================================*/



var args = WScript.Arguments;
if (args.length == 0) {
 WScript.Echo("引数を指定するか、ファイルをドラッグ&ドロップしてください。");
 WScript.Quit();
}

for (var i = 0; i < args.length; i++) {
 var path = args(i);
 if (path.match(inputExt) != null) {
  writeLyrics(path);
 }
}

function writeLyrics(path) {
 var streamReader = new ActiveXObject("ADODB.Stream");
 var streamWriter = new ActiveXObject("ADODB.Stream");
 try {
  // read
  streamReader.Open();
  streamReader.Type = adTypeText;
  streamReader.Position = 0;
  streamReader.Charset = inputEncoding;
  streamReader.LoadFromFile( path );
  var text = streamReader.ReadText( adReadAll );
  if (text.charCodeAt(0) == 0xFEFF || text.charCodeAt(0) == 0xFFFE) {
   text = text.substr(1); // delete BOM
  }
  
  // replace
  text = text.replace(/(^\s+)|(\s+$)/g, "");
  text = text.replace(/\<(\d\d:\d\d)\.(\d\d)\>/g, "[$1:$2]");
  text = text.replace(/\[(\d\d:\d\d)\.(\d\d)\]/g, "[$1:$2]");
  
  text = text.replace(/\[ti: *(.*?)\]/g, "@Title=$1");
  text = text.replace(/\[ar: *(.*?)\]/g, "@Artist=$1");
  text = text.replace(/\[al: *(.*?)\]/g, "@Album=$1");
  text = text.replace(/\[au: *(.*?)\]/g, "@Lyricist=$1");
  text = text.replace(/\[length: *(.*?)\]/g, "@Length=$1");
  text = text.replace(/\[by: *(.*?)\]/g, "@TaggingBy=$1");
  
  var offset = text.match(/\[offset: *(.*?)\]/);
  if (offset && offset.length >= 2) {
   text = text.replace(/\[offset: *(.*?)\]/, "@Offset=" + (offset[1] * -1));
  }
  
  text = text.replace(/\[[^\d].*?\](\r?\n)?/, "");
  text = text + "\r\n@TimeRatio=1\r\n";
  
  // write
  streamWriter.Type = adTypeText;
  streamWriter.Charset = outputEncoding;
  streamWriter.Open();
  streamWriter.WriteText(text, adWriteChar);
  
  /*
  // delete BOM
  var bomSize = 0;
  if (outputEncoding == "UTF-8") {
   bomSize = 3;
  } else if (outputEncoding.indexOf("UTF-16") == 0) {
   bomSize = 2;
  }
  streamWriter.Position = 0;
  streamWriter.Type = adTypeBinary;
  streamWriter.Position = bomSize;
  var buffer =  streamWriter.Read();
  streamWriter.Position = 0;
  streamWriter.Write(buffer);
  streamWriter.SetEOS();
  */
  
  streamWriter.SaveToFile( path.replace(inputExt, outputExt), adSaveCreateOverWrite );
 } finally {
  if (streamReader != null) streamReader.Close();
  if (streamWriter != null) streamWriter.Close();
 }
}

備考

タイムタグ仕様書(一般者向け)で定義されたフォーマットを「KRAフォーマット」と書いてはいますが、「KRAフォーマット」と言う名称はどこにも定義されてないので、便宜上の名前です。
「タイムタグ」や「タイムタグ歌詞」という名称は、LRC等を含めた歌詞定義フォーマット全般で使われる名称なので、フォーマット名称としてはあまり適切ではないと思います。そんなわけで、主に利用される拡張子「KRA」に基づき「KRAフォーマット」と呼称しています。
LRCは「LRCフォーマット」として定義されているため、迷うことはないのですが…。

2014-04-01

Medoly ver.1.3.0

Medoly

ちょっと変更が多いのでバージョンを飛ばします。


検索画面の検索結果にサムネイル表示

検索画面の検索結果にサムネイルを表示するようにしました。これは、自分の非同期処理の練習も兼ねてます。
画像の表示は非同期処理を行っていますので、表示されるまでにワンテンポ遅れます。非同期処理慣れしてないので、もしかしたらもっと上手く表示できるかもしれませんが、とりあえず今はこんなところで。あと、たまに画像が表示されない場合がありますが、対策がよく分かってないので、とりあえずこのままにします。そのうち何とかします。画像が表示されなくても大した問題はないので…。
ちなみに、同期処理で画像を表示させるとスクロールが非常に遅くなるので不可です。 



検索結果のソート順設定追加

検索結果のソート順序をしていできるようにしました。適用対象はタイトル表示のみです。ストレージの表示や各ディレクトリ(アーティストやアルバム等)の表示には適用されません。
「設定」画面から変更できます。変更後は全ての検索結果に適用されます。
デフォルトのソートは、従来通りタイトル昇順となっています。

ソートが大文字・小文字で区別されていた問題修正

上記のソート順指定処理で気付きましたが、再生キューのソート時に大文字・小文字で区別されていました。本来は大文字・小文字の区別をしないようにしたかったので、ソート仕様を変更します。
 

特定環境でジャンルが表示されないバグ修正

所持しているAndroid 3.2端末(HTC Flyer)で確認したところ、ジャンルの表示が正しく行われていなかったため、修正しました。
これがAndroid 3.2の仕様上の問題なのか、端末固有の問題なのか判断がつかないのですが…。

再生キューの長押しメニュー項目修正

前回削ったと思ってたら削られていなかった「再生」項目を削除しました。
…うっかり何かの拍子に元に戻してしまっていたようです。

文言一部修正

設定画面等の文言を一部修正しました。特に動作には関係ありません。

2014-03-28

Medoly ver.1.2.6

Medoly

ちょっと思いついた事を実装。


再生キュー項目の長押しメニュー内容変更

再生キューの曲を長押しした際のメニューに「先頭に移動」と「末尾に移動」を追加。個人的に少しほしかったので。
代わりに「再生」を除外。再生は項目をタップすれば良いだけの話なので必要性は低いかな…と。

検索画面のジャンル表示高速化

検索のジャンル画面の表示を少し高速化しました。
Androidのメディアデータベースは、一つの曲が複数のジャンルを保持できる構成になっています。
各ジャンルに含まれている曲数を表示させるため、今までは各ジャンルそれぞれについて曲数をカウントするという事をやっていたため、やや処理が遅くなっていました。当然、ジャンル数が多いと時間がかかります。
最近ちょっと調べてみたところ、検索URIに「content://media/external/audio/genres/all/members」を用いることで、ジャンルIDと楽曲IDの紐付けを行うテーブルにアクセスできる事が分かりました(ちなみに「audio_genres_map_noid」というビューを参照している。実体は「audio_genres_map」テーブルの模様)。
これをジャンルIDでグループ化して、各グループのカウントを取得することで、ジャンルに含まれる曲数を素早く取得できるようになりました。
ちなみに、プレイリストも同様の処理を行うつもりでしたが、同様ののURIが見当たりません…。

検索画面のストレージ表示高速化

検索のストレージ画面の表示を高速化しました。
これも曲数カウント絡みで、従来はテーブルからカレントディレクトリに含まれるメディアを全て取得し、Javaのコードで項目をカウントするという力業で曲数を取得していました。当然、曲数が増えると処理に時間がかかるようになります。ディレクトリの階層が浅くなり、サブディレクトリに含まれるメディアが増えていくと、それに合わせて表示に時間がかかるようになります。
変更後は、SQLiteのGROUP BYとCOUNT関数を活用するようにしました。ただし、SQLiteは文字列長に合わせた柔軟なグループ化ができない(私が知らない)ので、以下のような感じのSQL処理を行うようにしました。

START = カレントディレクトリパスの文字列長
LENGTH = カレントディレクトリに含まれる最大ファイル・フォルダ文字列長

SELECT [ファイルパス], SUBSTR([ファイルパス], START, LENGTH) AS subpath, COUNT(subpath) as count
FROM [メディアテーブル]
WHERE [ファイルパス] LIKE カレントディレクトリ%
GROUP BY subpath

上記の内容は大雑把なイメージですが、とりあえずSQLで可能な範囲でざっくり纏めてしまうというのが基本的な方針です。もちろん、同一のサブフォルダが複数に分割される場合もありますが、細かいカウントの合計値はJavaの処理で行っています。
なお、この処理の特性上、カレントディレクトリに一つでも長い名前のファイル・フォルダが含まれていると処理の効率が悪くなります。
ただ、ほとんどの場合は表示が高速化されますし、上位ディレクトリに行く程表示が遅くなる従来の問題は無くなります。

検索画面のジャンル・プレイリストのカウントが正しく表示されない問題修正 

これは、カウントする処理が誤っていたバグです。

検索画面で一つでもチェックが入っていた場合に、タップ挙動をチェックの変更とする

検索画面で、リスト上に一つでもチェックが入っていた場合、項目タップの挙動をチェックのON/OFF切換とするようにしました。ディレクトリのタップ動作によりチェックが全てクリアされてしまうため、誤操作により何度もチェックをやり直す羽目になる問題があったためです。

2014-03-25

Medoly ver.1.2.5

Medoly

エラー発生時の対応ダイアログ追加

エラーが発生してアプリが終了した際、次の起動時に以下の選択ができるようにしました。
もしアプリが起動できなくなった場合は、再生キューのクリア、または設定のクリアを実行してください。
  • 再生キューをクリア
    再生キューの内容をクリアします。
  • 全ての設定をクリア
    再生キューの内容及び設定を全てクリアし、初期状態に戻します。
  • 何もせずに起動
    そのまま起動します。通常はこれを選択してください。

ウィジェットが更新されない不具合修正

現在の再生曲をクリアした際に、ウィジェットに曲情報が残る不具合があったので修正。
これは、ImageViewの画像をクリアする際に、クリアのやり方が悪くて例外が発生していたためでした。これはAndroid 4.4なら問題なかったのですが、旧バージョンでは発生していました。

ストレージ検索の複数チェック時に検索結果がおかしくなる問題を修正

フォルダ検索で、複数チェックして別の条件を再検索した際に、検索結果が正しくならない場合があったので修正しました。
これは、SQLの検索条件が括弧で囲われていないためでした。

例を挙げると、今までは以下のようなWHERE句になっていました。
WHERE _data=? OR _data = ? AND artist=?
これを修正して、以下のようにWHERE句が組まれるようにしました。
WHERE (_data = ? OR _data = ?) AND artist=?

再生キューのサムネイルが正しく表示されるよう修正

再生キューのサムネイルが正しく表示されない場合があったため修正。
アルバムアートが存在しない場合、再生キューの1個前の画像を拾ってしまう場合があり、これをデフォルトの画像が表示されるように修正しました。

2014-03-18

Medoly ver.1.2.3

Medoly - Google Play の Android アプリ

ちょっと試してみたらすぐ出来たので1.2.3へアップデート。
 

再生キューの長押しメニュー追加

再生キューを長押しした際にメニューを表示するようにしました。既に他で代替できるような機能ばかりですが、一応一般的なアプリと同様の操作方法も出来た方が良いかな、と。
とりあえずアップデート内容はそれだけ。

 

2014-03-16

Medoly ver.1.2.2

Medoly - Google Play の Android アプリ

すぐ実装できそうだったのでサクッと機能追加。

関連付け処理追加

音楽ファイルが、他のアプリから「共有」や関連付け実行できるようになりました。
音楽ファイルを「共有」もしくは関連付けで実行すると、再生キューに登録されます。オプションから自動的に再生させるようにする事もできます。
試しに作ってみたけど、意外と面倒くさかった…。

2014-03-15

Medoly ver.1.2.1

Medoly - Google Play の Android アプリ

いきなり修正。

ソートアイコン変更

ソートアイコンが昇順が下矢印、降順が上矢印だったので修正。何となく、A→Zの感覚で昇順を下矢印にしたけれど、よく考えたら昇順は普通↑や△で表記されることが多い。
とりあえず、Excelのアイコン風にAとZを添えてアイコンを統一。

変更前

変更後


ソート時に画面が更新されない不具合修正

ソート時に画面の更新がされず、ソーとされていないように見える不具合があったので修正。


タイトル検索時に同名タイトルがまとめて表示される不具合修正

検索時に同一タイトル名があった場合、まとめて表示されてしまう不具合があったので修正。

Medoly ver.1.2.0

Medoly - Google Play の Android アプリ

Medoly Ver.1.2.0をリリースしました。
個人的な感覚では1.5.0ぐらいまでバージョンをすっ飛ばしたいところですが、表面上は大して変わり映えしないので1.2.0で。



内部処理の大幅な見直し

今回のバージョンで再生キューの内部的な処理を大幅に見直しました。というか、ほぼ別物です。別物ですが、表面上は特に変わってないと思います。そういう風に作ったので。
今までは再生キューの内容をファイルパスで保持し、アプリ起動時にファイルパスから読み込んでいました。アプリ起動時に進捗ダイアログが表示されるのはそのためでした。ところが、当然読み込みに時間はかかります。
これが問題になるのは、リモートコントローラやウィジェットを利用する場合です。再生ボタンを押すと、その時にアプリが起動して読込処理が走るため、再生キューに多くの曲が登録されているとボタンを押しても反応しない等の問題があったり、よく分からないエラーが発生することがしばしばでした。
そんなわけで、再生キューの内容をSQLiteのテーブルで定義し、そこからCursorLoaderを利用して逐次読み込みすることにしました。アプリの根幹部分をゴッソリ入替えたので、この改造でほぼ1ヶ月ぐらい費やしてます。
そのため、起動時の再生キュー読込処理は無くなりました。その代わり、 端末の性能が低いと再生キューのスクロールが多少もたつく事があるかもしれません。それを気にするほど大量に再生キューを登録する利用をしているかどうかは分かりませんが。

また、編集モードのキュー入替え時に再生キューリストが一瞬だけちらつくのはコレに伴う影響です。従来メモリに保持してたものをテーブルに保持させた結果、書き換え&画面更新で処理が遅れてしまうためです。…もしかしたら何かの解決策があるかもしれませんが、現状はそこまで調べ切れてないため、仕様とさせてください。
あと、再生キュー登録時の進捗バーが無くなったのもとりあえず仕様です。処理が変わったことで以前のような進捗バーを出すことが出来なくなってしまったため、表示をやめました。そもそも、100曲登録する程度なら大した時間はかからないので。1000曲ぐらい登録するとやや時間はかかりますが。頑張れば表示出来ないことは無いですが、それによって登録処理が遅くなっても本末転倒なので…。

ウィジェット追加

表面的に一番大きな変更点は、ついにウィジェットが追加されたこと。とりあえずレイアウト別に2種類あります。サイズが可変(最小サイズは2x1)なので、好みに応じて適当に拡大・縮小してご利用ください。Androidのバージョンによっては変なレイアウトになる可能性もありますが…。



 

ソートの降順対応

 内部処理の大幅な見直しに伴う副作用というか、元々これも視野に入れてたのですが、従来ソートが昇順だけだったのに加え、降順にも対応しました。これは、SQLite テーブルを使用した事によって、その辺の処理が簡単に実装できるようになったので追加しました。


ホームパスの省略表記

ホームパスを設定していた場合、ディレクトリパスの表示を半角チルダ「~」で表示するようにしました。省略しないと文字が長くなりすぎるためです。ホームパス以下でない場合や、上位のディレクトリは省略せずそのまま表示します。

その他

ラベル・メッセージの微調整、表記変更、アルバムアート解像度の修正、歌詞の初期表示で先頭にスクロールしない問題の修正等、細かいのはいくつかあります。
内部処理の大幅な見直しで処理が大きく見直されたので、修正が無意味になったりしたものがあったりして、把握できてない物が多いです。

2014-02-16

Medoly ver.1.1.5

Medoly - Google Play の Android アプリ

Medoly Ver.1.1.5をリリースしました。
オマケ機能を追加したので、バージョンを0.0.5飛ばしました。(オマケなので…)

LOOPSTART / LOOPLENGTHによる疑似ループ処理追加(Android 4.1以降)

音楽ファイルにLOOPSTART / LOOPLENGTHというタグが含まれていた場合、ループを行う処理を追加しました。特殊な音楽ファイルなので、普通の人がお目にかかる機会はあまり無いと思います。分かる人にだけ分かっていただければ…。
使い方としては、設定から「タグによるループ制御」をONにして、曲の再生順を単体再生に切り替えると有効になります。ループ可能な曲の場合、メイン画面の「プロパティ」タブにループという項目が表示されます。

なお、この機能は擬似的なもので、ループ時に正しく曲が繋がりません。というのも、単純にシークさせてループさせているためです。シークの指定はミリ秒単位です。正しく曲を繋げるためには、波形データをきちんと繋げて、再生ストリームに流し込んでやる必要があると思いますが、現在のところそれを実現させる上手い方法が思いつきません。音楽ファイルを展開して処理するライブラリをファイル種別毎に用意して、色々やると出来るような気がしますが、面倒臭いし、現状のアプリと互換性がとれるかどうかも分からないので保留。
というわけでその辺は置いておいて、これはオマケ機能として割り切って、ループが多少不格好でも構わないという人(主に私)向けです。その代わり、ループの位置を調整する設定を儲けてありますので、これで多少の差異は吸収できるような気がします、多分。あと、LOOPSTART/LOOPLENGTHタグ以外に、コメントに「LOOPSTART=***」「LOOPLENGTH=***」という内容が含まれていた場合、勝手に読み取ってループ可能なファイルと判定します。なので、Vorbisファイル以外に、MP3やAAC等でも使えます、一応。

なお、ループの開始にはAndroid4.1以降でMediaPlayerクラス追加された、字幕トラック関連のメソッド(addTimedTextSourceや、selectTrack等) を利用しています。字幕の出現イベントで、ループを実行している感じです。この処理、一旦SRT形式の字幕テキスト(ループ情報を書いたテキスト)をファイルとして書き出す必要があります。そんなわけで、一時的(一瞬だけ)テンポラリファイルが外部に書き出されますので、ご了承ください。
そのうちその辺のコード解説とかも書きたいけど、とりあえず後で。

設定画面にループ関連の設定を追加

上記に関連した、ループ関連の設定を設定画面に追加しています。ループのON/OFF切換と、ループ開始位置、ループ終了位置の微調整ができます。

再生キューのメニューを「選択中の曲へ移動」から「自動スクロール」のチェックボックスに変更

元々、設定画面に「キューの自動スクロール」というものがありましたが、これをタブのメニューに移動させました。これは、再生曲に合わせてスクロールさせる/させないを切り替えるものです。再生キューを自分でスクロールさせている時に曲が切り替わると、強制的に曲まで勝手に移動するのが鬱陶しいので、ON/OFFできるように設けてあります。設定画面にあっても使いにくいので、メニューまで移動させました。

メイン画面のタイトルをタップした際の挙動を、再生キューの選択中の曲に移動するように変更

上記変更に合わせ、元々あった「選択中の曲へ移動」という機能は、メイン画面のタイトルをタップさせた際に実行するようにしました。元々、 タイトルをタップさせた際は表示タブへの切り替えでしたが、それはタブをタップすれば済む話なので、少々持て余していました。そんなわけで、機能の置換えです。


画像読み込み処理の変更

サイズの大きな画像を読み込むとアプリが落ちる問題があったので、その対処です。

ソート時に再生位置が追従しない問題を修正

再生キューの先頭の曲を再生している際にソートをかけると、再生中の曲の表示が追従しない問題があったため、修正しました。「<」と「<=」の間違いです…。

プレイリストで重複データが含まれていた場合に、内容とカウントが一致しない問題を修正

プレイリストに重複した曲が存在した場合、検索画面の曲数カウントに齟齬が生じる問題があったので、修正しました。ContentResolver の projectionに「distinct」を追加しただけです。

今回の変更は以上のような感じになります。
いい加減、ウィジェット機能付けないとなぁ…。

2014-02-14

Medoly ver.1.1.0 & 1.1.1

Medoly - Google Play の Android アプリ

Medoly Ver.1.1.0及び1.1.1をリリースしました。
バージョンを1.1.10にしようとも思いましたが、機能が追加されたのでバージョンを 0.1あげました。
主な変更点は以下の通り。

終了タイマーの追加

終了タイマーを追加しました。画面右上のメニュー→「終了」を選択するとダイアログが表示されます。


このダイアログで終了時のタイミングを決定します。
任意の項目をチェックし、値を入力した後に「タイマー設定」でタイマーを実行します。再度ダイアログを開き「タイマー解除」を選択すると、設定したタイマーを解除します。再度タイマーを設定すると、以前のタイマーは上書きされます。
また、「すぐに終了」でアプリをそのまま終了します。
終了タイミングは以下の物があります。
  • 再生キュー完了時
    再生キューの再生が最後まで終わった時点で終了します。最後の判定は再生キューの再生済状態と、再生順の設定によります。タイミング最終曲で「先頭に戻る」と設定していた場合も、先頭に戻らず終了します。「1曲のみ」の場合は終了まで到達しませんので、必然的に無効となります。
  • 残り曲数
    設定した残り曲数を再生毎にカウントダウンし、0になった時点で終了します。カウントダウンは曲が再生し終わった段階で行われますので、曲をスキップしたり、再生キューで選択した場合はカウントダウンされません。先に再生キュー完了時の条件に達した場合も終了となります。「1曲のみ」でループする場合もカウントダウンします。
  • 残り時間・終了時刻
    終了時間になった時点で終了します。設定する方法が違うだけで、何れも同じものです。途中で再生が停止しても、タイマーが切れたり終了したりはしません。
大体機能としては以上のような感じです。
ちなみに、1.1.1は1.1.0のバグフィックスです。非常に初歩的なミスにより、時間のタイマーを解除しても終了時間に達した時点でアプリが終了するという問題があったためで…。

2014-02-05

Medoly ver.1.0.9

Medoly - Google Play の Android アプリ

Medoly Ver.1.0.9をリリースしました。今回は割と色々。

修正内容

メディア読込み時に進捗ダイアログを表示

メディアの読み込み時に進捗ダイアログを表示するようにしました。具体的には、検索画面からメディアの登録時、起動時の再読込時、プレイリスト読込み時の3点です。起動時以外は進捗バーが出ます。なお、ダイアログ表示中にキャンセルを押すとキャンセル可能です。
今までは、全てのメディアを読み込むまで一切の操作ができなかったため、曲数が多いとアプリが固まってしまう事象への対策です。 ちなみに、再生キューに一気に数百曲とか放り込むような無茶な使い方をするような自分のための機能です。
画面の表示更新とメディアの読込でマルチスレッドで動かす必要があり、これに対応させるために読み込み処理部分を大幅に組み直しました。正直、1.0.8から大分組み直してます。感覚的には1.2か1.3ぐらいでも良いような気がしますが、表面的に目だって異なるのはこのダイアログぐらいなので、1.0.9です。個人的に、バージョンの付け方はユーザーが感じる機能向上に合わせて付けるものと思ってますので…。
恐らく、これに伴って再生キューに2重に登録される 不具合が解消されたと思います。多分そんな気がします。…されてるといいなぁ。
また、この変更によって何かしらの不具合が発生する可能性がありますが、その場合は、ご報告いただけると幸いです。
なお、何となく想像はつくかと思いますが、現在は再生キューの中身を丸ごとアプリで保持してます(サムネイル除く)。本当はSQLiteデータベースを用いて、逐次読み込みで対応させるところだと思いますが、最初に作った時にそこまで考えていなかったので…。この方法なら恐らく、今の起動時の読み込み処理は無くなり、キュー追加も早くなるかもしれません。ただ一方で、再生キューのスクロールが引っかかるような動きになるかもしれません…が、そこまでキューにたくさん曲を入れる人はそんなにいないような気がするので、その辺の影響はあまり考えなくても良いような。
とりあえず、その辺りはまた組み直すつもりですが、今はこのままで。

プレイリストやジャンルを含む検索のエラー対応

検索条件にプレイリストやジャンルが含まれていた場合、検索に失敗してアプリが落ちる場合があった問題を修正。これは、プレイリストやジャンルのキーの列名が「_id」となっており、さらに曲のキーも「_id」となっていたため、これらを組み合わせると列が重複してしまいSQLがエラーを吐くというものです。
これに対し、曲の検索条件に「title_key」キーを使用したり、名前にテーブル名を含めたりして、問題が起きないように修正しました。

ちなみにAndroidのデータベースは、一つの曲が複数のプレイリスト/ジャンルに所属でき、アーティストやアルバム等のプロパティとは処理が少し異なります。それらを上手く処理するために、一部でSQLを直接書いています(本来、プログラムの安全性の観点からあまり好ましくはないのでしょうが…)。その結果、内部ではなかなかに面倒なSQLが組み上がってます。
なお、下記サイトによると、テーブル名が一部異なる端末も世の中には存在するようで、そういった端末では確実にエラーが起きます。もしそのような端末があった場合は御一報ください。(下記の情報はAndroid 2.x端末の話なので、最初から本アプリは動作しません。)
Android携帯に登録されているプレイリストを取得する時の罠 - 理ろぐ


リピート再生時の次へ/前へボタンの挙動変更

リピート再生(単曲再生)している時に、次へ/前へボタンを押した際に、再度同じ曲を再生する動きだったのですが、よく考えたらその動きおかしい…というわけで、次へ/前へを押したら再生キューの次/前の曲を再生するよう変更。ちなみに、再生キューの動きは全部自前で制御してるので、わざわざそういう動きを作ってたということになります。
…あんまりきちんと考えてなかった。

プロパティタブメニューの対象に無い項目を非表示

プロパティのタブを押した際に出る検索メニュー。今までは全部の項目を表示していたのですが、検索対象のメタ情報が存在しない場合は、その項目を表示しないようにしました。そういえば、なんで今までそうしてなかったんだろう。

その他

一部メッセージ変更など。
色々弄ったので細かいの忘れた。

現在把握してる問題

  • Android 4.4 (KitKat)で、スクロールのつまみが表示されない。これはAndroid側の問題臭い。
  • ごく希に、何もしてない時にプロセスが暴走してる事がある模様。再現性がなくて確認できない。多分、上記の読込処理に関連した問題だと思ってる。バックグラウンドのサービスが起動した時に読込処理が走って、何かやってるのかなぁ、と推測。とりあえず、上記に書いた読み込み処理の変更した後で考えることにする。

2014-01-22

Medoly ver.1.0.8

Medoly - Google Play の Android アプリ

すぐに直したい部分だったので、またバージョンアップ…。

修正内容

再生停止状態で電話かかってきた時に、再生が再開されてしまう不具合修正

以前からたまに発生していた不具合。アプリを終了した状態でも、電話着信時に再生が開始されてしまうことがあるという恐怖の問題がありました。必ずしも発生するわけではなく(というか、確率は低い)、発生条件が分からず頭を悩ませていたのですが、ようやく大体の原因を特定。

問題のポイントとしては、電話がかかってきた時に再生を停止させる処理。停止してる状態でこの処理が走ると、エラーが発生することがある。このエラーが起きる条件もよく分かっていない(そもそもAndroidの状態がどうなっているのかきちんと分かってない)のですが、データを全て削除したり、プロセスを終了させた後の起動後等に発生しやすい模様。単純なスリープや終了状態ではなく、他の何かがトリガになってるような気がするのだけれど、これがよく分かってない。
で、このエラーが発生すると、MediaPlayerのonErrorイベントが走る。onErrorイベントが走ると、「次の曲を開く」(エラーの曲を読み飛ばす)という処理が走る。その結果、曲が再生されてしまうという感じの流れ。Ver.1.0.2で再生処理を組み替えたので、その際に発生するようになってしまった模様。
対策としては2つ。まず、イベント発生時に電話の状態をチェック(TelephonyManager#getCallState()) し、アイドル状態でなければ再生させないという処理を追加すること。もう一つは、そもそもこのエラーが起きないようにするように、処理を見直すこと。色々見直してみると、エラーのチェックが甘かった…。
これは、単純に自分の技量不足の問題です…。

2014-01-19

開発用端末の調達

最近、開発用に1個端末を入手。

SC-02B - Wikipedia

docomo のGalaxy S。3年ぐらい前に発売された、 Android 2.2の頃のスマートフォンが流行り始めた頃の端末。中古で5000円ぐらい。ジャンクならもっと安いかも。

自分はdocomoの回線契約は無いし、そもそもこれは純粋にアップデートするとAndroid 2.3までしか入らない。ところがこの端末、ストレージ容量も多く、今でも色々弄られ続けてる端末なので、これにカスタマイズされたAndroidをインストールする事ができる。自分が入れたのは、CyanogenMod という有名なカスタムOS。現状最新の CyanogenMod 11をインストールし、Android 4.4の環境で動作させている。

これを開発用端末にしてる理由は、
第一に安上がりに最新(or 過去バージョン)のAndroid環境が手に入ること。
第二に、古めの端末で自アプリがサポートしてるAndroidのバージョンが動く中では下限に近いスペックと考えられるため、アプリのベンチマークに適任なこと。
第三にディスプレイが狭いため、画面のレイアウトを考える上で参考になること。
…といった辺りが挙げられる。まぁ正直、一番目の理由が8割ぐらいですが。 元々、ストア配信のバージョンと、開発バージョンでアプリが競合してしまって、開発バージョンを入れるための開発用端末が別途欲しくなったのが発端だったので、動けば何でも良く、安上がりであることが何よりも重要だった、という経緯もあります。


なお、CyanogenMod自体は問題なく動作し、3年前の端末とは思えないほど動作も快適です。重い処理を走らせるとちょっと厳しいかもしれませんが、実用的には問題ないレベル。
ちなみに、Cyanogen Mod自体はサポートも無いし、動作しなくても自分で何とかせざるを得なく、大変面倒なので、メインの端末で使うのはちょっとお勧めしません。

Galaxy SでAndroid 4.4

Medoly ver.1.0.7

Medoly - Google Play の Android アプリ

ちょっと微調整…。
あんまり頻繁にアップデートすると鬱陶しそうなので、今後はちょっと抑えめに。

修正内容

再生中に着信があった場合、着信終了後の再開をオプション化

以前から、停止状態でも電話着信時に再生が開始されてしまうことがあるという問題がありました。着信時に前回再生してた曲が流れ始めるので、うっかり受話ボタンを押そうものなら通話中にバックグラウンドで曲が流れるという、人によっては大変に恐ろしい不具合なのですが、事象の再現性がなく、今ひとつ要因に確信が持てないでいました。これは機種依存の可能性もあり、私の使用しているISW 13HTでしか発生しないかもしれません。
ただ、再生開始のメソッドを呼ぶ場所は限られているので、恐らくここだろう、というかここしかないだろう、という目星はついていました。「受話前に音楽が再生されていた場合は、通話終了後に再生を再開する」という処理を入れていたのですが、この辺のイベントやフラグの処理が想定外の順序で走っているのかもしれません。
少しコードを修正した上で、電話後に再生を再開する/しないの判断はユーザー側に委ねた方が良いという考えもあり、とりあえず受話後に再生を再開する処理をオプション扱いにしました。設定画面で切り替えられます。
これでしばらく様子見ということで。

歌詞の前後に空白を入れるように変更

歌詞を表示させた際、歌詞が画面端に詰まってしまう場合に1行間を開けるようにしました。単に個人的な見やすさの問題です。

2014-01-18

Medoly ver.1.0.6 リリース

修正内容

歌詞のオフセット保存機能追加

これは要望が上がったので追加。
再生中に歌詞の表示タイミング(オフセット)を調整することができますが、これを曲毎に毎回リセットせず、保存できるようにしました。ただ、これはズレがある歌詞に対して微調整をするための機能だったので、オプション扱いとします。設定画面でリセットする/しないは切り替えられます。

再生キューのサムネイル作成処理を修正

サムネイルの作成処理を少し見直しました。エラーのサムネイルを読み飛ばすようにしたので、エラーとなるサムネイルが沢山存在する場合に、再生キューのスクロールが高速化されます。…されるはずです。

再生キューのアニメーションスクロールの廃止

Ver. 1.0.2で修正したアニメーションスクロールですが、再生キューに多量の登録があると、アニメーションスクロールがまともに追従しなくなる事がわかったので、中途半端に動くぐらいなら邪魔なのでやめます。スクロール量に応じて切り替える事も考えましたが、基準が分からない上に端末毎に異なる可能性もあるので…。
smoothScrollToPositionFromTopメソッドは、大量のスクロールには全く向かないものでした。もっと細かくクロールさせる用途向けの処理ですね。

メディアスキャン処理の変更

Android 4.4でメディアスキャンがエラーとなっていたので修正。Intent.ACTION_MEDIA_MOUNTEDをsendBroadcastさせていたのですが、これはあまりよろしくないようで…。処理を修正して、MediaScannerConnection#scanFileを実行するようにしました。
 

通知アイコンの表示タイミング変更

これは私の環境だけかもしれませんが、停止時の通知アイコンを表示させていると、アプリを終了させtも無関係なタイミングで通知アイコンが再表示されてしまう問題があったため、通知アイコンを表示させるタイミングを少し調整しました。

アニメーションONの状態で初回タブ切換時に、タブが一緒にアニメーションしてしまう問題修正

アプリ起動時に、初回タブ画面が再生キューの状態からタブを切り替えると、タブ画面ではなくタブウィジェットがタブ画面に引っ張られるようにアニメーションしてしまうという問題がありました。原因がさっぱり分からなかったのですが、tabhostのタブ変更イベント( TabHost.OnTabChangeListener )の設定タイミングを、タブ登録の前に持ってきたところ発生しなくなりました。
操作開始前にTabHost.OnTabChangeListener を1回以上発生させておく必要があるのかなぁ、という予測。

その他

メッセージ等修正。


その他現在認識している問題

 

以下の内容は、現在のところ自分で認識している問題です。原因調査中です。

  • 登録時に二重登録されてしまう場合がある。
  • 使ってないのにバッテリーを異常に消費することがある。
  • 停止状態でも受話時に再生が再開されてしまう。
  • Android 4.4でスクロールつまみが表示されない。Andoroid 4.4のバグっぽい。対応するか否かは考え中。

2014-01-13

Medoly ver.1.0.5 リリース

昨日公開したバージョンに速攻でエラーレポートが上がってたので、その修正…。
レポート上げてくれた人、どなたかは存じませんがありがとうございます。

サムネイル生成時にエラーが発生する問題を修正

再生キューのサムネイル生成時に、エラーが発生する場合があったので修正しました。
こちらで再現することができないのですが、AndroidのDBにあるメディア情報と実際のファイル状態に齟齬があると発生するような感じです。とりあえずエラーの発生箇所は分かるので、問題が起きないようにプログラムを変更しました。

Medoly 1.0.2 リリース

Medolyのバージョン1.0.2をリリースしました。今回は以下のような変更があります。

メディアが再生できない問題を修正

音楽ファイルのタグ(メタ情報)の読込み失敗時に、再生その物がに失敗する問題がありました。失敗時は、メタデータの読込みをせずに再生を開始します。こんな所のエラー処理が杜撰だったのは、自分でも流石にどうかと思います。

再生に失敗したメディアが再生キューに存在する際の問題を修正

再生できないメディアが再生キューに登録されていた場合、再生キューの順序制御や停止に問題が発生するので、内部の処理を色々見直しました。 これに伴い、再生キューに関わる色々な問題が修正されています。
ただ、自分が再生キューの状態遷移をきちんと把握しきれてないため、まだちょっと怪しいかもしれません…。

再生失敗したメディアを再生キューから除外するオプションを追加

再生キューの問題に絡み、再生失敗時に再生に失敗したメディアを自動的に再生キューから削除します。
標準では削除しませんが、次に再生順が来た時は自動的に読み飛ばします(再生キューを直接タップすれば再度再生します)。なお、これは再生キューから外れると解除されます。また、アプリを起動した際に再生キューを再読込する場合にも解除されます。(要は、再生キューに保存されたフラグがクリアされた場合。)

再生済み状態が保存されない問題


再生キューの再生済み状態が保存されない場合があったので修正しました。これは、アプリを2回再起動すると発生します。
要因は、起動中の意図しないタイミングで再生済みがクリアされた状態で設定が保存されていたためです。対策としては、起動時に再生済み状態を保存するように修正しました。また、これに伴い全体的に設定の保存タイミングを調整しました。

再生位置を保存


再生位置を1秒毎に保存し、次回起動時に再生位置のリジュームを行うようにしました。今までも、画面を閉じた際や停止時に再生位置を保存していたのですが、これだとバックグラウンド再生時に全く保存されるタイミングが無いという…。適当に実装してたものですが、きちんとレジュームできるように作り直し。

再生キューのスクロールが中途半端に終わる問題修正


再生キューのスクロール量が多いと、最後まで完全にスクロールされない場合がありました。これは、AndroidでsmoothScrollToPositionFromTopを利用すると発生する場合があるそうです。なお、設定でアニメーションを無効にすると、setSelectionFromTopを使用するので、この影響はありません。

Issue 36062 - android - AbsListView.smoothScrollToPositionFromTop does not scroll correctly when position is the next visible item - Android Open Source Project - Issue Tracker - Google Project Hosting
listview - smoothScrollToPosition after notifyDataSetChanged not working in android - Stack Overflow
listview - Android smoothScrollTo not invoking onScrollStateChanged - Stack Overflow

原因はAndroid側の問題のようで、対応策としてはイベントで再スクロールさせています。ただ、これでも、スクロールに失敗したり、スクロールが連続して発生するために妙な動きをする場合があるので、少し様子を見てみます。これがダメなようならアニメーションを完全にカットしてsetSelectionFromTopのみに絞った方が良いかもししれません。個人的に、スクロールアニメーションは、上/下のスクロールが視覚的に認識しやすいので、あまり無くしたくはないのですが…。
これは、将来的なAndroidのバージョンで治る可能性もあります。

自動スクロール設定をONにしていると、項目削除時にスクロールしてしまう問題修正

再生キューを編集モードにした上で、キュー項目を削除すると、選択中メディアに自動的にスクロールする問題を修正しました。
再生キューの内容に変化があった場合に、全てスクロールする処理になっていたため、項目削除時にもスクロールが発生していました。これに対し、スクロールする場所を個別に指定して、発生タイミングを限定しました。

アクションバーのタイトルやアーティストが null になる問題修正

…単にnullチェックを怠っていただけです。タイトルがnullの場合はファイル名を表示、アーティストがnullの場合は何も表示しないように変更しました。

メール送信機能を無効化

元々、問題発生時にエラーをメールで送信してもらう機能をつけていたのですが、Androidアプリには標準でエラーレポート機能が備わっているため、不要なので削除しました。
…単純に標準のエラーレポート機能を知らなかっただけです。初めてアプリ作ったので、この辺の知識が足りてませんでした。すいません…。
ただ、開発用のデバックビルドでは動作するようにしています。開発用なので、普通の人の目に触れることはありません。
なお、実際にエラー発生時はエラーレポートを送信していただけると非常に助かります。

実装してたエラーレポート機能

検索条件の見直し

検索時に、タイトル項目から再検索すると検索結果が正しく反映されない問題を修正しました。

再生停止時に、通知アイコンを表示しないオプションを追加

個人的には、再生停止時に通知バーから再呼び出しが出来るので便利なのですが、鬱陶しいと感じる場合もあると思うので、切り替えられるようにオプションに追加しました。

その他、諸々の修正をしてますが、細かいので省略。というか、忘れました。

今回はそんな感じで。
設定項目を多少いじってるので、設定画面の設定項目も増え画面も間延びして不格好なのですが、余力があればもう少し整理します…。


追記

設定画面が開けない問題があることが分かったので、修正しました…。申し訳ありません。
現在、Ver. 1.0.3となります。

さらに追記

ファイル読込みがエラーとなる問題と、再生順ボタンを押すと落ちる問題を修正しました。
ハッキリ言うと、デバッグ用コードの消し忘れです…。 すいません。