Archive for the ‘gmusicbrowser’ Category

(身から出た錆?に追われて疲れた話: 僕しか使っていないソフトの話で普遍性がないが、なるべく一般的に書く。)

数日前、PCを再起動した後に、Linux用音楽プレーヤーgmusicbrowser(GMB, 「僕版」)の再生履歴を自作の音楽再生履歴・感想記録システムMlhiのwebで見たら、アルバム画像がおかしいのに気付いた。別のアルバムの画像が表示されていた。実はこれは今年の3月頃にも起こっていて、GMBの曲の番号(ID)が詰められた(昔のBASICのリナンバーみたいなもの)ため、MlhiのDBに登録されたIDとずれて、別の曲のアルバム画像がGMBから取得・表示されることが分かったので、その時はIDを詰める処理をスキップするようにして解決したと思っていた。

僕としては、ユニーク(重複しない)かつ不変であるべきIDを詰める(変える)なんて意図が理解できない※。詰める理由はライブラリ内の曲が削除されたからだと想像しているのだが、IDは整数で、今は少なくとも32ビット(64ビットだって行けるだろう)だから、少なくとも2000万、あるいは4000万曲には割り当てられるから、使い放題だ。だから、曲を削除してもIDを詰める必要は全くない。にも関わらず、なぜそんな危険で無駄なことをするのだろうか。まあ、作者の思想とか気の迷いなのだろう。

※こういうのが、いわゆる「想定外」問題だ。「普通はないよね」って思うことは、実はいくらでもあるのだ。普通なんて幻想だ。そして、全部「想定外」と言えば何でも許される訳ではなく、どこまで想定したかがポイントだ。偉い人たちは、そこが分かってない(、というかうまく逃げているのだろう)。

それから、そういう事態になってもなるべく破綻しないように作るのも、設計・開発者のセンスとか能力だ。

それに、曲の削除にしたって、個人で何千万曲も入れる訳じゃないから多少無効な曲があったって、(今もそうしているように)フラグだけ設定してエントリは削除せず、残しておいてもいい(それが気になるなら、DBの構造をちゃんとすべきなのだ)。

これに似た問題として、マイナンバーの番号不足を想像する。(以前書いたが、)僕の計算では遅かれ早かれ足りなくなると思うのだが、いつかリナンバーするのか、あるいは、また作り直すのか。

のだが、やっぱり再発した。こういう根深い問題は大抵再発して、なかなか直らないと相場が決まっているので、「やっぱり」と思うとともに結構がっかりした。

IDを詰めないようにしたつもりなのに、なぜかまだ詰めるようだ。もちろん、作者は行方不明で聞くことはできず、直してももらえないので、自分で解決するしかない。そして、3月に(いや、それ以前から)思ったが、GMBは超大嫌いなPerlで書かれているうえに、内部構造が妙に凝っていて理解不能なので、根本的に直すことができない。

仕方ないので、MlhiがGMBの曲を指定するのにGMBのIDでなく、別の新しいIDを使うことにした。結構大改造なので、なるべく避けたかった。まあ、そもそも、GMBのIDの仕様を確認もせずに勝手に(ユニークかつ)不変と思い込んで使った僕が悪いので、全く身から出た錆ではあるが、なかなか大変だった。

大改造だけど、なるべく変更を少なくしたかった。そこで、MlhiのDBの構造は変えず、GMBのIDを入れていたプレーヤー固有のトラックIDを格納するフィールド(カラム)に格納する内容を変えることにした。GMBのIDの代わりに、曲の音響的な特徴から生成したID(今までもMlhiのトラックID(メインのキー・ID)として使っていたもので、AcoustIDのfpcalcコマンドで計算している)を格納するようにした。GMBのIDと新しいIDはプリフィクスで識別できるようにして、同じフィールドに混在可能にした。なお、この新しいIDは、当然ながらGMBのDBにも登録し(フィールドを追加した)、GMBのIDがいくら変わっても(その新しいIDで)同じ曲が検索できるようにした。

GMBに曲の固有ID(utid)を追加した("Track ID"の列)。まだ再生していない曲("Today's girl")では空だが、再生すると自動で設定される(「ザ・ベスト」)。"ID"は従来のGMBのID(固有でない)。

また、そのようなDBの中身の変更をバッチ処理(SQL)で行うのは失敗のリスクがあったので※、使いながら自動的に変更するようにした。つまり、GMBでの曲の再生時(Mlhiへの履歴の登録時)に、既存の曲のプレーヤー固有のトラックIDのフィールドに昔のID(GMBのID)が入っていたら、新しいID(曲の特徴から生成したID)に入れ替えるのである。

※実際、MlhiのDBは確認のためにしかツールで開いていなかったのに、いつの間にかDBのエントリが一個空になっていた・・・ 間違って何か押したか、修正中に(あるいはもっと前に?)バグが出たのかは不明だ。 ← バックアップを調べたら、変更版を試している時に誤動作して消えたようだ。

なお、曲の特徴からIDを生成するのは、今まではMlhiに履歴を登録するプログラム(登録プログラム)で行っていたが、そのIDをGMBにも格納するようになったので、GMBで生成して登録プログラムに渡すようにした。

余談だが、PerlからID生成プログラムを実行する時、曲のファイル名に日本語が入っていると文字化けして、どうしても直らなかった。ごく当たり前のことすら普通にできないのでブチ切れそうになったが、仕方ないので、Base64でエンコードしてプログラムに渡すようにした。

もう、とにかくPerlなんて1文字だって見たくない!! 何もかもオカシくぐちゃぐちゃで※、見るだけでイライラが溜まる。Perlだったら断然Pythonの方がいい(でも、僕にはPHPが一番だ)w 良くあんなものでまともなプログラムが書けるものだ。そこには感心する。

※"my"や"our"なんてセンスが悪いし、関数(サブルーチン)の引数は(,)で囲い区切ってってもそうでなくてもいいなんて見難いし、$とか@とか%とか()とか[]とか{}とか=~とか\とか、とにかく記号が多過ぎて(しかも、それらの複合もある)、「いい加減にしろ!」だ。

GMBの場合の処理の流れの概要は、以下のようになる(Spotifyでは曲の固有IDがISRCになっている以外は概ね同様)。

曲の再生開始

GMB

  1. 開始から指定時間(例: 3秒)後に、以下を実行する(すぐにスキップした場合は登録しないようにしたかったため)。
  2. その曲に固有ID(utid)が設定されていなければ、音響的特徴から生成して、GMBの曲DBのutidフィールドに格納する。+
  3. Mlhiに再生開始を通知する(utidを指定)。

Mlhi(履歴登録プログラム)

  1. 指定された曲が以前再生したことのあるか(指定されたutidがトラックIDのエントリがDBに格納されているか)調べ、なければ(初めて再生する曲)そのエントリを作成する。その時、GMBから曲の情報(例: タイトル、アーティスト、アルバム)を取得する(utidで曲を指定する)。
  2. 以前再生したことのある曲の場合、DB内のプレーヤー固有のトラックIDがutidでなかったら、指定されたutidに置換する。+
  3. 再生開始日時をDBに記録する。また、再生回数を+1する。

曲の再生終了

GMB

  • Mlhiに再生終了を通知(utidを指定)する。併せて完奏率も送る。

Mlhi(履歴登録プログラム)

  • 再生終了日時と完奏率をDBに記録(追加)する。完奏率は、それまでの完奏率と合わせる処理をする(履歴表示時に平均の完奏率を求めるため)。

曲の再生履歴の表示

Mlhi(web)

  1. DBから再生履歴を抽出し、GMBの曲の場合には、アルバム画像のファイル名や曲の長さなど(履歴には関係なく自明なため)DBには格納していないが、履歴表示にはあった方がいい追加情報をGMBから取得する(utidで曲を指定する)。
  2. その他の情報(例: コメント、評価)をDBから抽出し、履歴と共に成形して表示する(→ )。

※今回追加した処理は項目の最後に"+"を付けて示した。

 

変更を少なくしようとはしたけれど(実際、処理の流れの本筋は何も変わっていない)、やっぱり結構多くなり、昔作ったのですっかり忘れていたこともあって大変で、4日くらい掛かった(ている)。その間は、例によって、気付くと一日が終わっていたり、食事中にもバグが見付かったりして、全く気が休まらなかった。更に、昔の誤り(しかも、関係ないところにも!)も結構あって、「どんなものでもテキトーに作ってはいかん」と思い知らされた。。。

とは言え、何でもきっちり作っていたら、終わらずに使えなくなってしまう(機会を逃す)ので、そこの見極めが難しい。そこら辺もセンスや能力なのだろう。

そして、動作確認して基本的にはOK(何度も戻って修正した)だったのだが、そもそもの、GMBのIDのリナンバーが起こらず、本当に効果があるのかが確かめられない。曲を削除しても強制終了しても、なぜか起こらない。どうにも挙動不審なプログラムで、そういうのを使い続けるのもどうかとは思うのだが、他にいいものがないので仕方ない(いつかは自分で作り直したいが、無理だろうな)。

問題の現象が再現しないのでは仕方ないので、定期的にGMBのIDのリナンバーが起こったかどうかを検出するように定期実行機能(crontab)に設定しておいて、起こったら改めてアルバム画像を確かめることにした。

 

なお、僕しか使っていないものなので、他の方では直ったどうかも判然としないからキャプチャを載せても意味がないが、以下のように直った(単なる自己満足で載せるw)。

問題修正後のMlhiの再生履歴のアルバム画像

図中の"FCM5:"で始まるのが、曲の特徴から生成したIDである(それをGMBの新しいIDにも使っているので、"GMB:FCM5:-"と略記している)。また、"AIM5:"はアルバムのIDである。

 

PS. 当然ではあるが、曲の特徴から生成するIDは、同じ曲(ファイル)ならいつ何回生成しても同じ値になり(昔生成した値と今回生成した値が同じだった)、まだ重複していないので、ちょっと感心した。

PS2. 誰も気にしてないはと思うが、僕が好きなのは80年代のコイズミまたはkyon2であって、現代のダサい小泉さんではない。この投稿は妙なタイミングになったが、そこのところははっきりさせたいw (5/11 19:28)

 

(5/11 9:10 画像を追加、わずかに加筆・変更; 18:52 処理の流れの概要を追加、その他に加筆)

  •   1
  •   0

ようやく正式な名前を決めた、Music listening history and impressions "Mlhi" DB (音楽再生履歴と感想の記録・検索システム※)に、先日やる気が出てしまったgmusicbrowser(GMB)対応機能にを追加しようと作り出し、さっきようやく動き出したのだが、気付いたら一週間経っていたw

※日本語の名前は今考えたので、やけに長いうえにダサいw

まあ、見た目はほとんど変わらない(そういうふうに作ったので)。GMBで再生した曲は再生の都度DBに記録され、履歴webに出る。Spotifyと同様に完奏率が出るし、評価やコメントを付けることもできる(それ以外の細かいことはまだだが)。

GMB対応に伴うDBの構成変更の内容が確定していなかったのと、うっかりDBを壊すのが怖いので、今はテスト用のDBを使っているが、もう少ししたら本物に切り替えたい。

それから、Spotifyでもそうしたのだが、再生した曲のDBへの登録を外部プロセスでなく、GMB本体(Spotifyではミニプレーヤー)に組み込んだ。これもまだできたてで怖いのだが、外部プロセスの場合、再生開始してからDBに登録されるまで30秒近く掛かっていて、いつも「遅いなあ、まだかよ」と思っていたのが、瞬時に(数秒以内に)登録されるようになったのが大変気持ちいい。これなんだよ! (と、ひとりごつw)

以下に、少し実装の話を書く。

  • 以下のような流れでGMBの再生履歴をDBに登録する。
    1. GMBで曲の再生を開始する。
    2. GMBはDB登録プログラムにトラックIDを指定して、「再生開始の登録」として起動する。
    3. DB登録プログラムは、GMBからDbusで曲情報を取得して本システムのトラックIDを生成し、再生開始日時と再生回数をDBに登録(それぞれ、追記、加算)する。
    4. 最初に再生する曲の場合、主な曲情報(タイトル、アーティスト名など)と完奏率、評価、コメントなどもDBに登録(転記)する。
    5. その曲の再生が終了する。
    6. GMBはDB登録プログラムにトラックIDと完奏率を指定して「再生終了の登録」として起動する。
    7. DB登録プログラムは完奏率をDBに登録(加算)する。
  • GMBの曲のトラックIDは、MD5で音響指紋のハッシュを求め、それをBase64(base64url)で短くしたものにした。
    • 今は、仮にGMBの曲がSpotifyと同じものでも関連付かないが、将来は(ISRCへの変換・対応テーブルのようなものを作って、)同じ演奏を関連付けられるようにしたい。
  • 本システムでは複数種類のトラックID(例: ISRC, Spotify, GMB, 音響指紋)を混在して使うが、それらをプリフィクスで区別(URLのスキームのような感じ)するようにしている。
  • GMBでのジャケット画像は当然ながらローカルのファイルだが、近頃は(リモートの)webページと(ローカルの)file:スキームは混在して使えないようなので、画像ファイルをsym-linkで仮想的にwebサーバの配下に置き、リモートのファイルとして指定している。馬鹿らしいが、サーバの設定では対処できなかったので、そうした。GMBの演奏がSpotifyと関連付けられるようになれば、Spotifyの画像を参照することもできるが、それも馬鹿らしい。
    • なお、ジャケット画像が曲のファイルに埋め込まれている場合もあるが、それを表示するには、抽出して一時ファイルを作ることになって管理が煩雑になるので、現在は使っていない。
  • GMBは、内部に曲の再生開始と終了時に実行される関数があり、しかも、終了時に完奏率を計算してくれるので、事前に「面倒だ」と思っていたのが馬鹿らしいほど、手間が掛からずにさらっと組み込めた。
    • また、今までの再生履歴を記録してくれていたのもありがたい。ただ、今までにファイルを移動したりして履歴が消えてしまったものがあるのが残念だ。今後は本システムを使うことで、そういう問題を防げる。
  • DBには2つフィールド(カラム)を追加した。音楽プレーヤspecificなトラックIDとアルバムIDである(後者は念のために追加したが、まだ使っていない)。将来的には、現在あるSpotfify専用のトラックIDとアルバムIDを廃止して、こちらだけにしたい。

思わぬ利点は、今までは、GMBはSpotifyと違って評価が付けられない(実際にはできる)とか再生履歴が残らない(上記のとおり、実際には残る)とかコメントが書けない(実際には書ける)などの理由で(要は、見やすくないとか手軽に更新できないのが面倒だったようだ)、余りGMBで聴かなくなっていたのだが、これができてから、(テストの意味もあるが、)俄然使う気が起こったことだ。将来的には、SpotifyとGMBをシームレスに(シャフル)再生できそうな(したい)気がして来て、夢にはまったく限りがないw

BTTFで言えば、やっと落ち着いたと思っていたら、またドクが来て、「未来に道は要らない!」とか言われて引き戻されるようなものだw

あと、やけに長いコメントが入っている曲が見付かったりする(CD-DBに入っていたのが取り込み時にそのまま入ったのだろうが、今まで全然知らなかった。 ← 実際にはコメントに購入日を記載した時に気付いたはずだが、すっかり忘れていた)のは、おもしろいのはもちろん、今までは不便で活用できなかった情報が生かせそうな点もwebでの履歴表示のいいところだ。

これでもまだ83%といった感じだが、大分落ち着いてきた。でも、プログラム中に山ほど"TODO"があるので、なかなか先は長い。そういうのは普段は何もないのだが、突然牙を剥くので(早速、ここに載せる画像を追加する時に出たw)まったく油断できない。

 

(6/17 19:46 若干加筆・修正)

  •   0
  •   2

鋭意寄り道しながら開発中の音楽再生履歴記録・検索システム(仮)の横道の、gmusicbrowser(GMB)で再生した曲も履歴DBに入れようと四苦八苦準備している中で出たこぼれ話。

そもそもは、GMBで各演奏(録音, 曲)を区別する値「トラックID」は、音響指紋をハッシュ(ダイジェスト)処理で短くしたものを使う方針にした時、有名なハッシュ処理方式のMD5の結果が32文字と長いのが気に入らなくて、何とかしたいと思ったのが発端である。そんな、とても細かい話が書いてあるので、読まれる方はほとんど居ない気はするが、(いつものように)僕の整理のために書く。

まあ、そもそも、トラックIDは曲名などではないので、手で打ち込んだり目で見て何かする(読む)ことはないし、長いとはいっても音響指紋自体ではないので1つが数KBになる訳でもないから、それによってDBのサイズが肥大化することもないので、完全に好みの問題だ(有名な林檎のオジさんがやってそうな話かもw)。

なぜ"CRC48"がないのか?

はじめに目を付けたのはCRCである。ただ、僕が使っているPHPには32ビット(8文字)のCRC32しかなく、どうも心許ない気がした。具体的には、ID(曲数)が増えた時に重複(「衝突」)しそうな気がした。とは言え、僕はこの分野に詳しくないので、本当に32ビットで不十分なのかの確証はない(理論上は、32ビットでは約40億(232-1)通りの表現ができるが、重複(冗長性)を持たせているだろうから、現実的にはもっと少なそうだ。それでも、数万曲だったら完全に余裕だろう。ただ、ハッシュの表現範囲がどうやって決まるのかが分からないので、特定の使い方で範囲が狭くなるのなら安心できないと思った)。まあ、これも全く気分的な問題だ。

探すと、64ビットのCRC64(16文字)はあった。が、それは少し長く、僕としては12文字くらいのが欲しかった。それは"CRC48"というべきものなのだが、いくら探してもほとんど出て来ない。調べると、ハッシュ値を短くする方法が分かったので、それでCRC64を48ビットに短くできた。最初は単純にマスク(≒除算または剰余)して短くすればいいのかとは思ったのだが、詳しくないので、安直なことをしてハッシュの強度が下がる(→ 衝突しやすくなる)のが嫌なので、丹念に調べた。結局は普通にマスクしても問題なさそうだった。念入りにするには、長いハッシュを分割してXORする方法があった。

それにしても、CRC48がないのが不思議だ。Stack overflow(コンピュータ技術者のQ&Aサイト)でも、「結構要る」という意見もあった(UUIDに使うらしい)。全く不便だ。

音響指紋計算プログラムfpcalcの隠れオプション-hashの不思議

CRC48を探したり試行錯誤しているうちに、音響指紋の計算に使っているfpcalcに、探していたものそのものかも知れないオプションがあることが分かった。それが-hashで、検索してもほとんど出て来ないので、知っている人は少なそうだ。

これを指定すると、音響指紋の値に加えて、音響指紋のハッシュ値も出てくる。この値があまりにも妙なので、うなってしまった。この値を使うと、同じ演奏(録音)が分かる可能性がある。ただ、残念ながら、常に分かるとは限らない。

同じ演奏(録音)のハッシュ値は同じか近いことがあるが、全く違うこともある。全く同じファイルなら同じだが、リマスターや単にベスト盤に入っているだけで異なってしまうことがあった。逆に、リマスターでもオリジナル版でもモノラル版でもレコード版でも同じ値になったりもした。同じ演奏で値が違う場合も、最後の桁が1だけ違う場合もあったし、特定の桁が思わせぶりに違う場合もあった。以下にその例を示す。

  • "Bohemian Rhapsody": b04cb826 (リマスターでないベスト盤), 904cb826 (リマスターとそうでないオリジナルアルバム)
  • "Crazy Little Thing Called Love": d434a0ae (リマスターでないオリジナルアルバムと新しいベスト盤), d400a0ae (リマスターでないベスト盤), d42ca02e (リマスターのオリジナルアルバム)

謎の挙動だ。それぞれの桁に意味があるような気がして、それを突き詰めるのもおもしろそうだったが、いつも同じ動きをしないと不便なので(僕としては、同じ演奏ならすべて同じ値になるか、同じファイルでない限り、全部別になって欲しい)、使うのは見送った。そもそも、同じ演奏(録音)かどうかは音響指紋の間の「距離」で確率として表すとのことなので、ハッシュは簡易的に似た演奏を見分けるにはいいだろうが(-hashオプションを紹介しているページには、重複したファイルを減らす使い方が書いてあった)、IDとして使うのは良くなさそうだ。

SHA-3(というかWikipedia)に騙されたw

CRC48に落ち着き掛けたのだが、厳密に言えば、CRCはハッシュではないし、自分で勝手に短くするのが気に入らず、任意の長さで出せる標準的なハッシュ方式を探した。すると、SHA-3はそれができるとのことだったので、プログラムをダウンロードして48ビットで試してみたら、エラーになった。調べたら、最短ビット数(224ビット)が決まっていた。あと、任意でなく数とおりしか指定できなかった。任意の長さにできるというのは、確かWikipediaで見たのだが、鵜呑みにするのは良くないと実感した。今回はちゃんと確かめたから良かった。もしかしたら、プログラムを変更すればできたのかも知れないが、中身がチンプンカンプンだった。

MD5を短くする。

SHA-3は諦めたものの、「短いハッシュ」で調べているうちに、Stack overflowで目から鱗のアイデアを見付けた。Base64エンコーディングで文字数を減らすのだ。ハッシュ値は普通は、"10f280198c517c830fa302c4e1bcca7b"(MD5の例)のように16進数で書く。これは元々は数値なので、Base64で表せば短くできるのだ。簡単に言うと、16進数は0-9とa-fしか使わないが、Base64はもっと多くの文字を使うから1桁で示せる値が多いので、短く書ける(桁数が減る)。

MD5は128ビットと数が大きいので手こずったが、できた。元は32文字だったのが"EPKAGYxRfIMPowLE4bzKew"のように22文字で表わすことができ、10文字も短くできた。希望は12文字くらいだったが、(今はobsoleteになっているけど)実績あるハッシュ処理を使うことで、衝突の心配が皆無になるだろうから許せると思い、今に至る。これで、いつでもGMBで再生した曲をDBに登録できる(屏風の中から虎を出してくれたらそのプログラムを書きさえすればねw)。

なお、この方法でCRC64も12文字にできる(基本的には、数値なら何でも可)ので、途中まではそれを使おうとしていたが、CRC48を止めたのと同じ理由でMD5の方が良さそうだったので乗り換えた。

(6/11 6:09追記) ちょっと思い付いて調べたら、SpotifyのトラックIDも僕と同じ22文字だった(アルバムIDも同じ)。これもBase64みたいな表記なので(例: 7oYN5pKRoFiVLoDFK6R3O1)、元はMD5のような長さの値なのだろう。偶然の一致だけどおもしろい。ハッシュ値だとしたら、何の情報を元にしているのか興味あるところだ。そして、(これは想像だが、)ID数が数億になるとCRC64では不足するのかも知れない。もちろん、使い切るまで衝突しない訳ではないので、ある程度の余裕が要ると思う。その点で、MD5にしたのは正解だったのではないか。

それに引き換え、(前にも書いたが)マイナンバーは脆弱だ。個人の番号は10進数で12桁なので全く余裕がない。当てずっぽうな値でも有効になるだろうし、(おそらくありえないがw、)長く使われ続けると不足するので、将来は死んだ人の番号を使い回す羽目になると思う。素人でも分かるくらいのしょうもなさだ。そのうち、そういう問題の解消を名目にしてまた新しい仕組みが(利権の確保とともに)生まれるのだろう。役人連中は楽だね。

似たようなことはISRCにも言える。12桁と短いうえに、区分されていて使える数が少ないから、いつかは破綻するし、年を2桁で表記するからとても古い録音は扱えないし(例えば、"19"は1919年なのか2019年なのか? まあ、仕組みができてから録音されたものを登録するスタンスなのだろうが・・・)、2100年以降は使えない。始まったのは2000年以降と推測するが、だとしたら日本の役所並みにお粗末だ。

ちなみに、確認と評価のため、音響指紋のMD5でGMBに入っている全曲(約1.5万曲)のトラックIDを生成してみたところ、90組くらい同じIDが出たが、衝突ではなく、元が同じ演奏(例: 重複したファイル、タグを変えたものとオリジナル、(ごくまれに)収録アルバムが異なる同じ演奏)のためだったので、本来の目的が達成できていることが分かったし、問題なくIDに使えることも分かったので安心した。あと、大量の曲ファイルに重複がほとんどなかったのには、我ながら感心した。なお、音響指紋の計算(fpcalc)が意外に遅く、大量の曲の処理には結構時間が掛かった。

心なしか、ものすご〜く本題から外れている気がするし、ちょっと買い物に行くのにハマーとかモビルスーツに乗るようなくらいやり過ぎな気がしないでもないが、趣味だし、いろいろな知識が得られたので良しとしようw

(6/11 5:41, 6:09 わずかに加筆・修正、追記; 6/11 10:32 わずかに補足)

  •   0
  •   1

開発中の音楽再生履歴記録・検索システム(仮)、そこそこ使えているうえに、煮詰まっているというか、完成度が高くなってきたために(それでもまだ8割くらいか)、いくら作り・直してもキリがなくて(しかも、劇的にすごくはならない)疲れたので、ちょっと脇道に逸れて、先日書いたgmusicbrowser(以下GMB)との連携・統合の一部の、GMBの再生履歴を取って記録することを試してみた。

まあ、GMBで再生している曲情報を取ること自体は全く簡単で、Dbusインタフェースを使えば目をつぶってもできたくらいだが(実際には少し手こずった)w、その先が大変だ。

先日も書いたように、再生している曲・演奏・録音(以下「演奏」)が「何か」を特定(同定)することが難しい。なぜ同定が要るかというと、GMBでもSpotifyでも同じ演奏を再生したら、同じ演奏の履歴として記録したいのだ。片方で再生したらもう片方とは別扱いになるのでは全く無意味だ。

そして、上記のGMBから取れる曲情報は、曲名、アーティスト名、アルバム名などで、それだけあれば充分だと思われるだろうが、実際には不十分だ。例えば、以下のような問題がある。

  • 同じアーティストが同じ曲(だけど実体が異なる)を何度も演奏/録音/発売した場合(例: ライブ、再録、リミックス、シングル/アルバム版、リマスター)、それらの区別が付かない。: 例: John Lennon "Give peace a chance" (通常版と"Shaved fish"中の短縮版)
  • 同じ演奏だけど微妙に曲名などが異なる場合、同じ演奏と判定できない。: 例: "Somebody To Love"と"Somebody To Love - Remastered 2011"
    • 逆に、何度も演奏/録音/発売した場合に微妙に曲名を変えてくれると、1番目の問題が緩和できる。: 例: 「かもめが翔んだ日」と「かもめが翔んだ日(スタジアム・バージョン)」
    • 「ファジー判定」のような仕組みがあるといいのかも知れないが、今度は上のような微妙に曲名を変えてくれた別演奏が同じになってしまいそうだ。
    • CDをPCに取り込んだ時に使ったCDDBの曲情報と、Spotifyの情報が異なる場合もあるし、自分で記述を変更した場合もある。
    • 日本のアーティストでは、表記が日本語かローマ字かの違いも問題になる。

そこで、先日も書いたように、各演奏はISRCで区別し(ISRCのないものは別扱いにする)、ある演奏がどのISRCに対応するかを音響指紋で識別しようと考えた。音響指紋と演奏を照合するためのシステムは、AcoustIDとMusicBrainzを使うことにした(無料で使えるのはこれらしかないため)。

手順は以下である。

  1. GMBの演奏(ファイル)の音響指紋を計算する。: fpcalcコマンド
  2. その音響指紋をAcoustIDに送り、演奏に紐付けられたMusicBrainz ID(以下MBID)を得る。
  3. 得られたMBIDでMusicBrainzからISRCを得る。

全くシンプルで、すぐにでもできそうに見える。確かに、それほど時間が掛からずに作れた(でも疲れたw)。が、ちょっと試したらとんでもなかった。以下のような問題が噴出した。

  • ある音響指紋に対して、実際とは異なる演奏(候補)が出てくることが結構ある。: 確率的なものなので仕方ない。
  • AcoustIDに登録されていてもMBIDがない演奏が結構ある。: ボランティアベースだから仕方ない。。。
  • MBIDがあってもISRCがない演奏が結構ある。: ボランティアベースだから仕方ない。。。
  • 登録内容(例: 曲名)が誤っている演奏がある。: ボランティアベースだから仕方ない?? 気付いたら修正しろってこと?
  • MusicBrainzのアクセス制限(アクセス頻度)が厳しく(最大 平均1回/秒)、それを破らないように、間隔をおいて何度も検索すると時間が掛かる。: 無料だから仕方ない。。。
    • 素行の悪いアプリは公表されてアクセス制限されるようなので、なるべく規則を破らないようにしないといけない。
    • AcoustIDにも同様に制限があるが、まだ緩い(最大 平均3回/秒)。

まさに、「聞いてないよー!」の世界だw

それでいろいろな工夫をしたが、現時点では、完全な自動処理(演奏の同定)をするのは難しい感じだ。以下に、したことと起こった問題を書く。

  • (MusicBrainzに情報がない場合に、)曲情報(曲名、アーティスト名など)でSpotifyを検索する。演奏時間やリリース年などでフィルタリングを厳しくする。
    • 最初の問題(同名曲の区別ができない/微妙に違う名前の曲が同じと判定できない)が起こる。
    • ライブ版がアルバム版と認識されることがある(曲名などが同じで、たまたま演奏時間が近い場合)。
    • "Various Artists"が幅広くマッチしてしまう。 → 無視するようにした。
  • (MusicBrainzの誤情報に備えて、)MusicBrainzで得られたISRCでSpotifyを検索して正当性を確認する。
    • Spotifyにない演奏が結構ある。SpotifyにあってもISRCがない演奏もあるうえに、MusicBrainzでもSpotifyでも複数のISRCがある演奏もある。そもそも、曲情報の表記が異なることがあるので、判定すら難しい。

ちょっと見た感じでは、多くの(2/3以上?)普通の演奏では問題なく動いている(→ 出力の例)のだが、たまに間違うのはやっぱり気に入らない。

他の問題として、MusicBrainzではリマスターやレコードの演奏などが全部通常版(例: CD)と同じに区分されている(Spotifyは、基本的に最新の1種類しかない。リマスターがあればそれだけになるし、当然ながらレコード版はない)。これは、技術だけでなくポリシーの問題も絡むので、すぐにはどうすればいいかが決められない。リマスターやレコードを通常版と別扱いにしたいこともあるし、そうでない場合もあるのだ。

AcoustIDとMusicBrainzはイマイチなので使わなくてもいいのかと思ったが、そうではない。それ以外に有効な手段がないので、やっぱり音響指紋での識別をした方がいい。その点で、Spotifyが音響指紋を提供してくれると一番ありがたいが、そうも行かない。今のところは、(リアルタイムでの)完璧な同定は諦め、バッチ処理でGMBとISRCの対応表を作って、その結果を「見て」修正するのがいい感じだ。ただ、GMBには1.5万曲くらい入っているので何とも大変そうだし、時代遅れな気がする。

次善の策として、SpotifyにはなくてGMBでしか再生できない演奏についてだけ対応表を作って、その内容を確認することが考えられる。それなら2500曲以下と大分減るが、そういう演奏はISRCがないものも多いので、別の問題(ISRC以外の何で演奏を区別するか)がある・・・

できそうでできないのは、SpotifyのAPIの音楽分析アルゴリズム(演奏の「活発さ」や拍などが分析されて公開されている)でGMBの演奏を分析して音響指紋の代わりに照合に使うことだ。でも、SpotifyのAPIではローカルファイルの分析はできないと思うし、Spotifyのアルゴリズムは公開されていないので、自分で実装することもできない。

もう一つ、最後の手段としてあるのは、仕舞い込んだCDを引っ張りだして、その中に記録されているであろうISRCを取り込むことだ。これが一番確実ではあるが(こうなると分かっていたら、最初からやっていただろう)、ISRCが入ってないものもあるだろうし、レンタルのものはできないし、そもそも、面倒だからやりたくないw

てな訳で、またしても(普通の人には全くどうでもいい)泥沼にはまり込んだ今日この頃・・・

(6/9 7:15追記) 昨夜、寝る直前に思い付いたのだが、IDとして各演奏に固有の識別記号を割り当てたいので、曲ファイルのダイジェスト(ハッシュ)値(例: MD5)をIDに使ってみたい。ただし、普通にファイルのダイジェスト値を使うと、それはタグ(例: 曲名)を変更しただけでも変わってしまって不便(タグを変えるとファイルとIDが合わなくなってしまう)なので、音響指紋のダイジェスト値を使うことを考えた。

ファイル名などをテーブルに記録して、そこでの通し番号などでIDを割り当てることもできる(GMBの方式)が、ファイルを移動したり名前を変えたらテーブルを更新しなくてはならないから不便だ。それを解消するために、曲のファイル自体にIDを持たせたい。そのためには、ファイル内(例: タグ)にIDを記録するか、ファイル名にIDを付加するか、上記のようなファイルの特徴をIDにするのがいいだろう。

IDをファイル内に記録する場合、既存のファイルすべてに記録する必要があって面倒なうえに、内容が変わらないのにファイルが更新されるのは嫌だし、今後、ファイルを追加するたびに忘れずにIDを記録しなくてはならないので煩雑だ。

ファイル名にIDを付加する(デジカメ画像の管理で実施している)のは簡便だが、既存の多くのファイル名を変えるのは好ましくない。例えば、GMBへの曲の登録をし直す必要があって、現実的でない。また、ファイルを追加するたびにIDを追加しなくてはならない煩雑さもある。

ファイルの特徴をIDにするのであれば、ファイル自体には何もしなくていいうえに、ファイルの中身を変更しない限り、あらかじめ決めた手順(計算)によっていつでも同じIDが得られるから便利だ。

音響指紋は、その名のとおり、(ファイルに格納された)音の特徴(だけ)に基づいているので、タグは無関係なはずだ。実際に試してみたら、本当に、タグを変えても音響指紋とそのダイジェスト値は変わらなかった(当たり前のことだが、結構うれしかった)。なお、当然ながら、異なる演奏は別の(音響指紋・)ダイジェスト値になった。ただし、同じ演奏でも、通常(CD)版とリマスター版やレコード版の音響指紋は異なったので、結構敏感なようだ。

この方式なら、IDはファイル(の中身の音)に固有だし、生成にも再現性がある(同じ音のファイルに対してはいつでも同じ値が生成できる)。ただ、音質や雑音などの条件が異なると「同じ演奏」でも同じIDにはならないが、そこは諦める(そもそも、GMBの中では重複した演奏(ファイル)は極力排除しているから、大きな問題ではないと思う。あるとすれば、リマスター版やレコードをオリジナル(CD)と同じと扱うかどうかの類だ)。

逆に、ダイジェスト値に衝突(偶然の一致、重複)が生じて、別の演奏に同じIDが割り当たる可能性はある。ただ、広く使われている方式でも(ハッキングを除いて)そういう問題が起こったという話は聞かないので、確率は低そうだ(また、音響指紋のデータは短いので、指紋の小さな変化でもダイジェスト値に反映されるから衝突は起こりにくそうだと想像するが、確証はない)。万が一衝突した場合は、(どうにかしてそれが検出できたとすれば、)IDに子番号を追加すればいいのではないかと思っている(と書いたものの、IDをテーブルなどで一元管理しないので、IDを生成した時点では衝突は分からなそうだ・・・ が、下に書いた変換テーブルで対応できるかも知れない)。

また、ISRCへの変換は変換テーブルで行う。その時に、重複した同じ演奏のIDに一つのISRCを割り当てることで集約できそうだ。

再生履歴DBでは、当初は、同じ演奏であっても、GMBのもの(音響指紋からIDを生成)とSpotifyのもの(ISRCからIDを生成)とは別のものとして記録するが、上記ISRCへの変換テーブルができたら、履歴などの情報を自動的にマージ(統合)したい(実際には、記録されたデータ自体は変更せず、DBの機能を使って統合されているように見せたい)。

と、またしても夢は膨らむのであったw (そして、例によって落とし穴が待ち構えている?)

(21:06 わずかに加筆・修正; 21:19 題の誤りを修正; 6/9 7:15 追記)

  •   1
  •   0

[10/1 8:18 この問題は解決した。原因と対処を最後に示す。]

面倒なことは、いつも突然やって来る。昨日、ふと、SpotifyとGMB(gmusicbrowser)を切り替えると音量が違うのが気になっていたので、それを可能な限り合わせたくなって、テスト信号を作って再生したら、思わぬことに気付いてしまった。

MP3のスイープ信号を再生したら、GMBからの音がおかしかったのだ。特定の周波数(例: 5.5kHz)が小さくなった。以下に問題の波形(問題の起こった5.5kHzの正弦波の再生波形の比較)を示す。上は問題がある場合、下は問題のない場合である(左チャネルだけを抜粋した)。なぜか変調が掛かっている感じで、音量が小刻みに振動している。そのために音が濁って聞こえる。

興味から、この変調波の周波数を調べたいのだが、普通にスペクトルを出しても出ず、なぜかLPFでも抽出できない(LFP後のスペクトルが出ない)。目視では24Hz程度と分かるが、なぜLPFで取れないのだろうか。振幅がとても小さいのか(そうなら、こんなに振れない気がする)。まだまだ知識が足らないようだ。。。

↑ 分かった! スペクトルを拡大したら、5.5kHz付近に2つの山があった。それらが干渉していたのだ。2つの差は約25Hzで、上記の目視と一致した。でも、なぜこういうことが起こるのだろうか? 新たな謎だ。

参考のため、実際の音(再生音を収録したもの)をこちらに置く。最初が問題のある場合、次が問題のない場合である。音量が大きいが、小さくしても現象は変わらなかった。多くの環境で再生できるようにMP3にしたので、環境によってはどちらも同じように(問題があるように聞こえる)可能性がある。

同じ音でもFLACのファイルを再生する場合は問題が起こらず、出力系をJACKからPulseAudioに換えても問題は起こった。それから、同じMP3のファイルを他のプレーヤー(例: XPlayer)やGstreamer(以下、GST)の再生プログラム(gst-play-1.0)で再生しても同じ現象が起こるが、問題の起こらないプレーヤー(例: Spotify, AlsaPlayer)もあるので、GMBが再生に使っているGSTのMP3デコーダ(デフォルトのもの)に問題がある可能性が高いことが分かった。

更に試したら、madというデコーダを使うと問題が起こらなくなることが分かった。以下に、gst-launch-1.0での例を示す。下記太字の"mad"を"decodebin"に変えると、問題が起こる。

gst-launch-1.0 filesrc location=test.mp3 ! mad ! audioconvert ! autoaudiosink

結局、GSTのデフォルトのデコーダ(decodebin?)に問題があるようなのだが、仮にGSTのバージョンが古いせいだとしても、OSのパッケージのものを使っているため、それより新しいものにバージョンを上げるのはかなりの手間(自分でコンパイルする)なので、上の例のようにGMBで使うMP3デコーダを変更するようにした。

MP3デコーダはmad以外にmpg123audiodecやavdec_mp3でも問題が起こらなかった。ただ、gst-launch-1.0で使う場合にはmadしか音が出なかった(他はprerollのエラーが起こった)。これは、僕がGSTの使い方を良く理解していないせいだ思う。

それで、3つの中では(名前から)一番品質が良さそうな(イメージの)avdec_mp3を使うことにした。ただ、何か問題があった時やデフォルトのデコーダで試したい時に手軽にデコーダを変更できるように、GMBの拡張設定で変えられるようにした。

他に分からなかったのは、GSTのパイプライン(上記のコマンド例のようなもの)でMP3デコーダを指定する順番が最初でないと うまく動かなかった(再生開始しない)ことだが、これも僕がGSTを良く理解していないせいだと思う。

 

昨夜から延々と試行錯誤と対処をして、ようやく本題の音量を合わせる話に進むことができた。テスト信号とメーターで音量を比較してみたら、結局、音量正規化を行わない場合には、どちらも同じ音量で再生されることが分かった(まあ、デジタルなのだから、大きく違ったらおかしいが・・・)。僕のシステムではDACの前に部屋の特性補正用のイコライザを入れているが、両方とも音量を100%にしてもオーバーフローは起こらないようだった。ただ、念のため(論理的でない「気休め」)、イコライザの前で1dB下げるようにした。

音量正規化を行う場合には、SpotifyとGMBの音量は異なっていた(これが、気になっていた音量差である)。それぞれの仕組みや目標音量が異なるためだろう。手持ちとSpotify両方にある数曲で調べたら、曲にもよるが、1〜3dB程度Spotifyが大きかった。それでミキサー(jack_mixer)を使って2dB程度下げたら、差が1dB以下となった。これで聴感的にも合えばいいが・・・ → 残念ながら、曲によってSpotifyの正規化後の音量が違うようで、あまり合わない感じだ。

まったく、手製のシステムは不意に問題が起こって気が抜けないものだ。まあ、オーディオ道 趣味なんてそんなものだろうw ただ、JACKだといろいろな部品をマウスで配線して手軽に試行錯誤や確認ができるのは便利だし、いろいろな知らなかったことが分かるのはおもしろい。

それから、MP3デコーダの問題はダウンロード購入したMP3音源の音質劣化を起こすことに気付いて確認したら、確かに若干音がおかしくなっていた。具体的には、ルプーのモーツァルト ピアノ協奏曲 第21番(1975)で弦(バイオリン)の音量が大きい箇所で違和感がある。実際、今年の頭に音が悪いことに気付いて書いている(これはこっちの問題だし、苦情を言ったら確実に泥沼化して疲弊しただろうから、黙ってて良かったw)。今聴くと、MP3デコーダを変更すると、違和感がほとんどなくなって、すっきりした気分のいい音(曇り空がちょっとした青空になった感じ)になる。確かに、録音が古いことによる音の悪さもあるのだが、MP3デコーダの問題が大きかったようだ。

もちろん、他のMP3音源でも同じ問題があるはずだが、特定の音(例: 5.5kHz)が続く場合にしか顕著にならないようで、たまたま僕の手持ちの曲ではそのパターンが少なかったらしく、気付かなかったようだ。実際、ルガンスキーのラフマニノフ ピアノ協奏曲 第2,3番(2005, 2003)ではまったく差が分からず、以前どおりのいい音にしか聞こえない。

それにしても、ルプーについては本当に音が悪いことが分かっていたなんて、僕の耳と環境は随分良くなったものだ。つい最近まで何がいい音か分からなかったことを思うと、全く感慨深い・・・

 

(10/1 8:18追記) MP3の音がおかしい問題の原因が分かり、対処できた。

しつこく試したり調べていたら、ヒントになる投稿が見つかった。どうも、GSTにインストールしていたFluendo MP3 Decoder(flump3dec、パッケージ: gstreamer1.0-fluendo-mp3)が古い(その投稿が2007年なので、それより前!)うえにバグがあったようだ。そして、今(実際には2007年以前から)はそれがなくてもMP3を再生できるとのこと。僕が自分でgstreamer1.0-fluendo-mp3をインストールしたのかは今となっては分からないが、余計だったようだ(← 別のPCには入っていないので、自分で入れたようだ)。

それで、gstreamer1.0-fluendo-mp3をアンインストールして問題のファイル(5.5kHzおよびスイープ)を再生してみたら、正しい音が出た(再度インストールしたら、やっぱり駄目になった)。ルプーのモーツァルト ピアノ協奏曲 第21番も大丈夫そうだ。それで、GMBのMP3デコーダの指定も止めて、デフォルトに戻した。

いつものように最後はあっけなかったが、原因が分かって良かった。それにしても、余計なパッケージは削除して欲しいものだ(→ Ubuntuの人)・・・

  •   0
  •   0

5/22(US時刻)に、USなどでGoogle Play Music (GPM)がYouTube Musicに統合された。日本もそのうちそうなるらしい。4月下旬のニュースでは「Google Play Musicが年内に終了」などどいうセンセーショナルな見出しでびっくりしたのだが、中身を読むと、実際には上記のようなことで、全然「終了」ではない(それどころか、実質的には名前が変わった程度)のだが、記者が無知なのかアクセス数を稼ぎたいのか知らないが、大げさな見出しになっていた。(→ 先週、より正確なニュースがあった)

僕は、GPMが終了することはまずないと信じていたので(というのは、これくらいのサービスは、Googleのパワーやリソースをもってすれば、全然問題ない(Spotifyは結構辛そうな気がする)。逆に、投資に比べて利益(お金以外も)が多そうだ。)、それについては全然心配しなかったのだが(仮に終わりになっても、Spotifyなどの代わりがあるから、お気に入りの曲・アルバム一覧などを保存して、移るだけだ)、ちょっと気にあることがあった。それは、GPMをYouTubeに統合するために、システムの内部構造が多少変わる可能性があり、その変化の影響で、今僕がgmusicbrowser (GMB)でGPMを聴ために使っている、gmusicapiなどのプログラムがうまく動かなくなったら、GMBでGPMが聴けなくなって不便を強いられるのではないかという心配だった。その後、疲れのせいかちょっと調子が悪くて余裕がなくて、たまたま、移行した頃から家ではGPMを聴いておらず、移行の件もすっかり忘れていた。

そして今朝。たまたまGPMを掛けたら、なぜか再生できなかった。アプリを再起動したり何度試しても駄目で、無事死亡した。ログを見たら、HTTPエラー403(Forbidden)で曲取得用URLの取得に失敗していた。最初は、自分のPCのLinuxの更新でPython(gmusicapiを動かすプログラム)やgmusicapiを駄目にしたかとか、GPMの認証がおかしくなったのかと思ってその辺りを調整してみたが、駄目だった。それからGPMが移行したことを思い出して、それに関係しているのかも知れないと思った。そして、苦労して作ったシステムが、気付いたら音もなくバベルの塔のように瓦解してしまっていて、また一から作り直しになるのかと、結構重苦しい気分になった。

HTTPエラーが出ていることから、GPMの通信手順がちょっと変わったのかとか、曲のURLを取得するためのURLが変更になったのかと思った。それで、gmusicapiを最新版にしたり、AndroidのGPMアプリの中に新しいURLが書かれていないか調べたが、前者は効果がなく、後者は見当たらなかった(アプリでは使っていないのか、別のページからリダイレクトしているのか)。あとは、参照するDNSサーバを変えてみたり、locale(言語)を変えてみたりしたが、効果はなかった(思い付いて試してみたのだが、まあ、効果がなくて当たり前だ)。

そこで、試しにパスワードを異常なものにしてみたら、別のエラー(HTTP 401だったと思う)が出たので、認証自体は問題なく通っていることが分かった。また、gmusicapiを使ってGPMの曲の検索をしてみたら、正常にできた。要は、本当に、曲のURLを取得する要求(だけ)がハネられている(Forbidden)なのだ。

それで、いつから駄目になったのか(いつまでちゃんと使えていたのか)を調べたら、5/23の6時頃までは正常に再生できていた(それが最後だった)ことが分かった。切り替えがUS時刻の5/22 0時だったとしたら、それは日本時間の5/22 13時前後だから、切り替え後しばらくは使えていたようだ(ここは不思議だ。切り替えはUSの午後から夜だったのかも知れない)。

八方塞がりになったので、検索してみた(Googleだと「自サービスに関する望ましくない情報」としてブロックされている可能性があったので、Bingも使った)のだが、ほとんど出て来ないので、まだ誰も困っていないようだ。それから、gmusicapiが公開されているサーバ、Githubの問題掲示板(Issues)を見てみたら、何となく、それらしいのがあった。スレッド名は以下である:

get_stream_url gives 403 with 'DEVICE_NOT_AUTHORIZED' for Mobileclient.FROM_MAC_ADDRESS #590

去年からの問題ではあるのだが、近頃になって3人が投稿している。そして、(現時点で最後の)fizzybunkという人の投稿を読んで確信した。近頃(移行に関連して?)、曲のURL取得要求を出す時に指定するデバイスIDには(正しい)AndroidデバイスのIDを指定する必要があり、それ以外はエラー403になるようになったようだ。以下に主要な部分を載せる:

I even have tried using a valid android device_id that is registered with the account, seems the only way I am able to get Mobileclient.get_stream_url() to work is being logged into the account on the mobile app with the device of the device_id at the same time. Otherwise it is throwing a 403 error.

今までは、(デフォルトの、)PCのMACアドレスから生成した仮のデバイスIDが使われていたのだが、それでは駄目になったのだ。それで、今使っているスマフォ(AQUOS)のデバイスIDを指定すれば動きそうだとは思ったのだが、とんでもないヘマをしたらAQUOSでも聴けなくなってしまう可能性もあったので、まずは、iPhoneのIDで試したが、駄目だった。

それどころか、iPhoneのGPMのアプリを動かしたあとでGPMのデバイス一覧を調べたら、今日からiPhoneは「PC」扱いになっていた。以前はスマフォ("iOS")だったのに、それとは別に、(ちゃんとしたGPMのアプリを動かしたというのに)PC扱いのデバイスが増えてしまった。GPMに登録できるデバイス数は10個までで年間に4個しか解除できないので、これは結構ひどい。iPhoneの人から文句が出そうだが、まだ誰も騒いでいないようだ。

他に試したことも全然駄目だったので、意を決してAQUOSのデバイスIDで試したら、嘘のようにちゃんと動いた。fizzybunkさんの書き込みは正しかったのだ。

(ほぼ一日を潰して、)とりあえずは復旧した。が、AQUOSのデバイスIDを使うことで、AQUOS側に影響が出ないとも限らないし(ただし、分身の術を使わない限りw、PCのGMBとスマフォで同時に聴くことはないので、競合の問題はない)、今後、更に別の変更がなされて問題が起こりそうで心配だ。前者については、もし問題が起こるようなら、昔使っていたNexus 4にGPMのアプリを入れて、そのIDを使おうと思う。※ 後者はGoogleの腹一つなので、全くどうしようもない。ただ、世の中には多くのGPMアプリが出回っているから大きな変更はできないはずで、今回のようなちょっとしたことであることに期待する。

※AQUOSのデバイスIDを使って曲のURLを取得していると、AQUOSがすぐに(数時間以内)Googleからログアウトしてしまって不便なので、Nexus 4のIDを使うことにした。この問題はGithubの別のスレッドにもあったので予期してはいたが、いろいろな落とし穴が多そうだ・・・ (5/28 6:15)

ただ、本当に駄目になってしまったら、新しい曲の取り方を探すか、別のサービス(例: Spotify)に移ることを考えることにする。だが、仮にSpotifyに移ったって、通信手順などは非公開なので、曲にアクセスするためのプログラムはやっぱり第三者がリバースエンジニアリングで作ったものだから、いつ動かなくなっても不思議はない。だから、サービスを移るのはそれほど本質的でなさそうだ(ただ、SpotifyにはLinuxのアプリがあり、GPMのブラウザよりは使いやすそうなので、GMBで全然聴けなくなってしまった時に移る先としては、意味がある)。

まあ、結局のところ、Google様が心変わりしないように祈りながら音楽を聴く程度しか、できることはなさそうだw

 

PS. それにしても、今回の変更で、日本、いや、世界中で、ものすごい数の人が「あれー!? 再生できない?」って言いそうなものなのに全然そうでないってことは、LinuxでGPMを苦労工夫して聴いている人は、本当に少ないってことのようだ・・・

  •   0
  •   1

面倒なのでずっと放置していたのだが、以前から気になっていたgmusicbrowserの(GMB)問題を一個修正した。きっかけは、今朝か昨夜だったか、シャフル再生中に、ある曲を聴きたくなったのだが、その問題のために躊躇したことだ。

その問題というのは、アルバムなり全曲のシャフルなりのプレイリストを再生中に一時的に別の曲を再生する(「キュー」機能)と、それが終わった後に本来の次の曲に戻らず、一時的に再生した曲がプレイリスト中にあればその次に飛び、なければプレイリストの先頭に戻ってしまうというものだ。

調べたら、元々そういう機能が作りこまれていないようなので(作者の精魂が尽きた?)、機能を追加した。相変わらずPerl言語はチンプンカンプンだし(Perlを作った人は、頭が腐っているか狂っている。どっちにしても、かなりおかしい。どうしてあそこまで分かりにくくしたのか、是非聞いてみたい)、GMBの造りも完全には理解できていないので苦労したが、何とかできた。なかなか疲れたし、昼食もとれなかった。「どうなってんだろう」と、軽い気持ちでエディタを開いたのが運の尽きだったw

それはともかく、終わってから、こういうのはデロリアン(タイムマシン)を改良しながら飛び続けたり、アイスティーに入れる氷を作るために巨大な装置を作ったドクに似ているかもなあと、ちょっと思った。

ちなみに、早速、聴きたかった「俺ら東京さ行ぐだ」を聴いて満足した。一体、どうしてそこまで好きなのか?w

 

PS. 今、「ついでにあの問題も・・・」と思い付いてしまった。午後も駄目っぽいなw

結局手を付けてしまって、案の定、手こずったが、ようやく何とかなった。またGMBの迷路のような造りが少し分かった。が、すぐに忘れそうだw (23:43)

ちなみに、その問題は、アルバムアーティストが設定されていないアルバムのジャケット画像がおかしくなる(別の同名のアルバムのものが表示される)というものだった。なぜか、GPMの曲を再生する時だけ起こるのだが、例えば、"Live in Japan"や"Greatest hits"という名前のアルバムで驚かされた。GMBのバグ以外に、ジャケット画像情報の格納の仕方が結構残念なことに気付いたのだが、さすがに抜本的な改良は難しい。 (5/6 0:12)

PS2. プログラミング言語の選択には、その人の個性とか趣味が現れるようで、職場の僕の居るチームでは、見事に各自が別々の言語を使っている。僕はPHP(awkやsedも)、隣の人はPerlといった具合だ(Cは、全員、必要な時に使う)。それで特に問題が起こらず、うまく行っているのも、おもしろいw おそらく、偉い人が、無理に「統一して効率を上げよう」と思うほど馬鹿でないのが効いているのだろうw (5/6 0:35)

  •   0
  •   0

この連休にはオーディオ(スピーカー・部屋)の音響特性の測定と調整をしようと思っていた。が、今日は下の家族が居るようで、子どもが走り回ったりして雑音が出るので延期した。それで、ちょっと前に思い付いた機能を作ってみた。

僕は、gmusicbrowser(GMB)の出力は標準のPulseAudioでなくJACKに出すようにしている。それに深い意味や大きなメリットがある訳ではないのだが、イコライザや出力はJACKなので、PulseAudioを介さずに直接JACKに入れることで、わずかに音の劣化が減ることを期待しているのだ。

が、それにはちょっとした問題がある。GMBのJACK出力は、曲を再生中以外ではなくなってしまうので、そのままでは再生するたびに接続しなければならず、全く実用的でない。それで、jack-plumbingという自動接続プログラムで接続するようにした。

それで問題なく使えていたのだが、先日、一つだけ問題が見つかった。音源がモノラル(正確にはチャネル数が1)の場合、左のスピーカーからしか音が出ないのだ(JACKでなく、PulseAudioの時は出ていた)。

どういうことかというと、GMBやJACKにはモノラルをステレオに変換(正確には、単一チャネルの音を両チャネルに出す)する機能がないため、モノラルの場合にはGMBからは最初の出力ポート(左チャネル)しか生成されず、jack-plumbingにもチャネル数が1の時は重複接続するような機能はないため、結局、左チャネルの入力ポートにしか接続されないので、左からしか音が出ないのだ。

モノラル(1チャネル)の音源なんてほとんどないから、我慢・無視すればいいだけなのだが、好奇心とか興味でなんとかしたくなった。それで、それにはGMBの改造が要るので、どうせやるなら、ついでにJACKへの自動接続機能も付けて、jack-plumbingを廃止したくなった。やり方は大体分かっていたので、今日は実装と修正をした。

それから、別件だが、少し前に、特定のアプリの音を特定の出力(例: ヘッドフォン)だけに出したくなって、それも実現していた。

以下に、それぞれの実現方法や実装の概要やちょっと苦労した点を書く。

(4/29 13:25追記) さっき、昨日思い付いたこと(下記の「Gstreamerの機能でも接続できるのかも知れない。」)を試したら、意外に苦労はしたものの、概ねうまく行った。おかげで、処理を大幅に簡素化できた。Gstreamerには分からないことが多過ぎて、まだ気に入らないこと(モノラルの処理)があるのだが、とりあえずは良しとする。以下に、その内容を"[4/29]"として追記する。

(4/30 9:42追記) 昨夜(4/29)、上に書いた「気に入らないこと(モノラルの処理)」もできた。Gstreamerの動作が調べたことと違うので、やっぱり苦労した。他に、特定アプリの音に関する接続漏れが見つかったので、修正した。以下に、それらの内容を"[4/30]"として追記する。

GMBからJACKへの自動接続機能

  • GMBで再生時、Gstreamer(再生用ライブラリ)の再生準備をした後(playbinというものの初期化後)、指定したJACKの接続先(入力ポート)に接続する。
    • JACKへの接続にはjack_connectコマンドを使った。もしかしたら、Gstreamerの機能でも接続できるのかも知れない。
    • [4/29] GstreamerのJACKモジュール、jackaudiosinkの自動接続機能でJACKに接続できるようになった。今までできなかったのは、仕様(使い方)を理解していなかったのと、JACKサーバに知らない機能があったためだった。
      • jackaudiosinkのプロパティconnectを"auto"にし、port-patternに接続先を設定(正規表現。例: "jack_mixer:In .+")すれば自動的に接続される。
      • JACKサーバのバージョンによっては、self connect(アプリが自分で接続する機能)が無効化されているために接続できない場合がある。その場合は、jack_controlコマンドでself connectモードを" "(self connect有効)に設定し、サーバを再起動する。
        • 例:  jack_control eps self-connect-mode " "
      • self connectを有効にした時、PulseAudioサーバのバージョンによっては、pacmdでmodule-jack-sinkをロードした時に、自動的にデフォルト出力に接続される場合がある。その場合は、ロード時に自動接続を無効化すればいい。
        • 例: pacmd load-module module-jack-sink connect=false
      • ただし、私はJACKサーバをjackdbusで起動しており、その場合にはmodule-jack-sinkが自動でロードされるようで、上記の指定は無効なので、JACKサーバの起動後に自分で切断している。設定ファイルに書けばいいのだろうが、JACKの開始スクリプトに書いた方が確実と考えた。
  • 曲をスキップさせたり再生ゲインのモードを変更した時などにはplaybinが初期化されるので、再度接続する。
    • [4/29] jackaudiosinkの機能で接続する場合には不要になった。
  • 理由は分からないのだが、playbinの初期化直後は接続に失敗するので、その場合は一定時間後にリトライするようにした。
    • たまたま、GMBには500msの周期処理(UpdateTime)があることに気付いたので、その中にリトライを追加した。
    • [4/29] jackaudiosinkの機能で接続する場合には、自分で接続しないので失敗することもなく、リトライは不要になった。
  • 接続済みの場合に再度接続しようとすると、無駄な処理の繰り返しになって負荷が増えるので、接続しようとする前に接続済みかどうかを調べるようにした。
    • JACKの接続確認にはjack_lspコマンドを使う自作のコマンドを使った。
    • [4/29] jackaudiosinkの機能で接続する場合には、自分で接続しないので不要になった。
  • 接続先ポートはGUIで設定できるようにすべきだが、頻繁に変える訳ではないので、今はプログラムの中に記述している。

GMBでのモノ→ステレオ変換

  • 上記のJACKへの自動接続時に、再生しようとしている曲がモノラル(チャネル数=1)だった場合、最初のチャネル(左)をJACKの右チャネル入力にも重複接続する。
  • [4/29] Gstreamerの信号処理機能を使えば、1チャネルを2チャネルにすることも可能そうだが、方法が分からなかったので、ミキサーにモノラル入力を追加し、モノラルの場合には、出力先をそこにすることにした。
    • 試しにモノラル入力に音を入れてみたら左右両方に出力されることが分かったので、こうした。
  • [4/30] Gstreamerの信号処理機能を使い、1チャネルを2チャネルにするようにした。
    • GMBのイコライザの処理を参考にして、Gstreamerの「パイプライン」というものを追加した。
    • 調べたところでは、"audioconvert ! audio/x-raw,channels=2"(チャネルを2つに増やす)というパイプラインでできるはずなのだが、どうしてもエラーになってしまう。また、"audioconvert mix-matrix='<<(float)1.0, (float)0.0>, <(float)1.0, (float)0.0>>'"(左チャネルの音を右チャネルにも出す)も駄目だった。試行錯誤の結果、"audiochannelmix left-to-right=1 right-to-right=0  ! audioconvert"(左チャネルの音を右チャネルにも出す)は成功したので、これを使うことにした。
  • 曲がステレオ(チャネル数>1)に戻った場合は、右チャネルへの重複接続※を切断し、通常の接続を行う。
    • JACK接続の切断にはjack_disconnectコマンドを使った。
    • [4/29] ※「右チャネルへの重複接続」は「モノラル入力への接続」と読み替える。
    • [4/29] jackaudiosinkの機能で接続する場合には明示的な切断手順がないため、一旦、playbinの状態を初期化してから設定すことにし直した。以下に手順を示す。
      1. playbinをset_state("null")で初期状態にする。
      2. 同様に、Ready状態にする。
      3. 出力先を変更する(port-patternを設定)。
      4. 1と同様に、Paused状態、Playing状態にする。→ 再生が再開する。
    • [4/30] GMBからは常に2チャネルが出力されるようになったので、重複接続やモノラル入力への接続切り替えは不要になった。
  • この時、通常の接続を接続する前に重複接続を切断すると、一瞬音が切れるように聞こえるので、通常の、右から右への接続を行ってから、重複接続(左から右)を切断するようにした。
    • こうすると、一瞬音が混じるはずだが、音が切れるよりはマシなので、こうした。実際には混じって聞こえる感じはしないので、一種の錯覚なのだろう。
    • [4/29] jackaudiosinkの機能で接続する場合には音が途切れないので、不要になった。

特定のアプリの音を特定の出力だけに出す。

  • PulseAudioに、新しい出力先を作る。
    • 新しい出力先を作るには、pacmdコマンドでmodule-jack-sinkをロードした(pactlでもできるはず)。
    • 例: pacmd load-module module-jack-sink channels=2 client_name=PA_alt_jack_sink  sink_name=PA_alt_jack_sink
  • 対象のアプリの音をその新しい出力に出すようにする。
    • アプリの音の出力先は、音量調節アプリ(pavucontrol)で設定すれば、それが記憶されて、以降は自動で出力されるようになるが、プログラムで明示的に接続した方が、後日、設定したことを忘れて困ることがないと思うので、次のようにした。
      • pactl subscribeコマンドでPulseAudioのイベントを監視し、対象のクライアントが接続されたら、pactl move-sink-inputコマンドで、対象のクライアントの出力を新しい出力に接続する。
      • subscribeではクライアントIDしか出力されないので、pactl list sink-inputsコマンドでクライアント名を取得して対象のクライアントかどうかを判定し、同時に得られるクライアントの出力(sink-input)IDをmove-sink-inputに指定する。
    • なお、pactlコマンドもクライアントとして認識されるので、上記処理でpactlを実行するたびに新規クライアントのイベントが生成されて無限ループになってしまうので、pactlコマンドの実行後は新規クライアントのイベントを1個読み捨てるようにした。そのため、タイミングによっては本当の新規クライアントイベントが破棄される可能性があるが、起こる確率が低いので、対処はしていない。

以下にJACKの接続例とミキサーの図を示す。

左図で、左中央の"gmusicbrowser"がGMBの出力で、上記の自動接続機能によって中央のミキサーの入力(jack_mixerのIN L,R)に自動接続される。モノラル時には、上記の変換機能によって、中央図のように、GMBの1つの出力が重複してL,Rチャネルに自動接続されて、両方のスピーカーから音が出るようになる。

[4/29] 改良後のJACKの接続例とミキサーの図を示す。

[4/29] モノラル時には、上段右図のように、GMBの出力がミキサーのモノラル入力(jack_mixerのMono_In)に入り、ミキサーによってL,Rチャネルに出力されるので、両方のスピーカーから音が出るようになる。

[4/30] モノラル時も左右チャネルから音が出るようになったので、ミキサーのモノラル入力への接続は行わなくなった。そのため、モノラル時の接続状態は上の「通常(ステレオ)再生時」と同じである。

特定アプリの音は、PulseAudioから左下のPA_alt_jack_sinkに出力され、ミキサーの入力(jack_mixerのAlt_In L,R)に接続されている(この接続は勝手に切れないので、静的な設定にしている)。ミキサーでは、この音(の中央列: Alt_In)をミュートしている(図のMボタンがミュート)ので、通常の出力(MAIN L,R)からは出ないのでスピーカーからは出ず、特定の出力(Realtek_aout)だけに出力される。ミュートを解除すれば、通常の出力にも流れて、イコライザ(jack_rack)を経由してJACKの標準の出力(system)に出て、スピーカーからも出すこともできる。

[4/30] 特定アプリ以外の音(通常の音)をヘッドフォンに出すことを忘れていたので、接続を追加した(jack_mixer: In Out L/R → Realtek_aout: playback_1/2)。

まあ、オーディオシステムは、あくまでも音楽を聴くのが目的であって、こういう作業はまったく本質ではない、自己満足とか慰みの領域ではあるのだが、自分で構築したシステム(暇さえあれば、自分のアイデアで改良できる)で音楽を聴けるのは、なかなか楽しい。そして、こんなことはWindowsやMacではまず無理(可能ではあるだろうが、手軽にはできない)だろうから、Linux+JACKにして良かったと思う。とはいえ、実は、こここに書いたことだけなら、WindowsやMacではやる必要のない作業なのかも知れないw

が、それにしても、こういう作業をすると、今まで知らなかった・やったことのないことを見つけたり気付いたりすることがあって、技術者としてはおもしろいし、いつか仕事でも使えるかも知れないから、一石三鳥だw まじめな話、Windowsだと、基本機能になければ(その基本機能もコロコロ変わる)、他人が作ったアプリを探して使うしかない(ものすごく時間を掛けて、自分で作れば別)から、おもしろくないしつぶしが効かない。あ、Mac?、あんなものは林檎社がUNIXを腐らせしまっているので、全く論外ですw

 

PS. 今の録音エンジニアなどの方には当たり前なのだろうが、今回のように、「接続を忘れてたから追加する」とかいうのがマウス1個wでできるのは、JACKの大きなメリットのように思う。昔(僕が中高大の頃)は、本物のケーブルで接続しなければならなかったから、端子やケーブルや機器が足りなければ接続できなかったし、端子の形状が違っても駄目だったし、信号レベルやインピーダンスも考慮すべきだったし、並列・直列接続し過ぎると音質が劣化するし、ミキサーを介さずに複数の出力を同じ入力に繋げるのは電気・音質的に良くないことだった。

今はそんなことを全く意識する必要がなく、自由に接続できるので、すごく便利だ。機器にしたって、「ミキサー(あるいはミキサーのチャネル)がない・足りないからから増やそう」と思っても、お金を工面して買いに行くw必要などなく、即座に無料で増やせる。もちろん、PulseAudioやALSAでもそういう変更は可能だが、GUIではできず、設定ファイルを書き換える必要があるから、「ちょっとやってみよう」という気にはならない。

そういうことがあるから、いろいろな手間や欠点があったにも関わらず、JACKに乗り換えようと思ったのかも知れない。 (4/29 10:34)

 

(4/29 5:02 加筆・修正; 13:58 改良した内容を追記; 4/30 10:16 4/29夜の改良・修正点を追記、その他、若干修正; 4/30 10:37 題を変更; 4/30 17:59 若干修正)

  •   0
  •   0

プログラムの「詰め」というのは、とにかく手間と時間が掛かる。大体、中心となる部分を作るのの10倍以上の時間が掛かるのではないだろうか。プログラムを使い物にするにはどうしても必要な作業のだが、退屈だし疲れるので、余り好きではない。それでも、なかなかうまく行かなかったことを打開するような、ちょっとしたアイデアがひらめくことがあって、それが本当にうまく行った時は楽しい。だから、いじりだすとなかなか止められない。仕事と違って、納期も予算もないのが困るw

一方、世の中には、すごいアイデアを思い付いたら、素早く目を引くデモ的な物(ポップ音楽だったら、サビとかデモ版)を作って、有名になったりお金をせしめたりしたら手放して(どこかに売って)、トンズラする(あるいは、別の物を作り出す)、スマートな人が結構居るようだが、無責任で嫌いだ。確かに、いいアイデアを出せるのはすごいと思うが、「最初にちょろっと書くのは誰だって楽しいんだよ!」とか「もう少し面倒見ろよ」と言いたくなる。

という訳で、日々続けているGPMDP (Google Play Music Desktop Player)の改良(もはや改造と言った方がいいだろう)は大分いい感じになってきた。まだ詰め切れていない点や不具合や抜本的な改良案もあるのだが、おおよそ7.5割くらいの出来になって、普通に(これには「変わったことはしてはいけない」という意味もあるw)音楽を聴けるレベルになった。具体的には、GMBだけで聴いている時と使い勝手での違和感がほとんどなくなった。例えば、GMBのリモコンで操作できるので本当に便利だ。実際に、昨夜からは、(実装・修正するだけじゃなくて)GPMの曲を聴くのに使っている。

問題だったユースケース(GPMDPをどういうふうにGMBと統合・機能分担して使うか)も、作業しているうちに大分詰められた。基本的には、GPMDPは曲の検索に使い、GPMDPの再生ボタンを押したら、実際の再生や再生の操作はGMBで行おうと思っている。まだ完全にシームレスになったとは言えないが、なかなかうまく統合できたと思う。

今週はさまざまな改良や修正をしたのだが、主にGPMDPとGMB (gmusicbrowser)との連携動作(例: 曲の追加や変更の同期)に関するものなので、見た目では、先週と比べて目立った違いは余りない。大きな違いは、(実際に曲を再生している)GMBで次の曲に移ったことがGPMDPにも伝わって、曲情報やジャケット画像が更新されることだ。以下にデモ動画を置くので、興味のある方はご覧頂きたい。

画面左半分はGMBの、右半分はGPMDPのウインドウである。GMBで再生中の曲が次に変わると、少ししてから(その次の曲の取得と処理の時間: これは如何ともし難い)GMBにその次の曲が追加され(画面左下、5曲目)、GPMDPの曲情報やジャケット画像(画面右中央)が更新される。

下の動画は、力技でやっているうえに、まだバグがあって、(いつ何が起こるか分からず、)ドキドキする機能なのだが、GMB(画面左下)でリスト中の前の曲をダブルクリックして移動した場合でも、GPMDP(画面右中央)も戻るようになっている(戻るのが遅いのは愛嬌w)。もちろん、取得されている範囲で、先の曲に進むこともできる。

リストの最後(7曲目)に2曲目の曲が追加されるのはバグである。難しい。。。

冷静に考えれば、再生中の曲のGPMDPへの同期なんて、音楽を聴くには不要な機能で、本当に飾りなのだが、「おもしろそうで、不可能でないならやってみたい」という、技術者の無駄なこだわりがあって実装したw

(10/1 6:38 わずかに修正)

 

PS. プレーヤーにGMBを使ったのは正解だった。最初は単純な再生だけを考えていたので、alsaplayerなどの軽いものにしようかと思っていたのだが、機能的に不十分な点があったので、最初からGMBで試した。また、GMBはコマンドラインにPerlのプログラムを指定して実行できるという、夢(麻薬)のような機能があり、それでGMBの内部データにアクセスできるので、かなり役に立っている。もう開発は停まっているようだが、セキュリティホールになるので、公開版に入れ続けるのは難しいかも知れない。

PS2. かなりハードルが高いのだが、将来的には、GPMDPじゃなくて、ブラウザのGPMのページから直接GMBと連携できないかと思っている。GPMDPが必要なのではなく、GPMのページ内の要素にアクセスするために使っているだけなので、ブラウザのアドオンか何かでできるのではないだろうか。

同時に、可能な限り機能を外部に出して、JavaScriptの部分を減らして開発効率を向上させ、GPMのページ構成になるべく依存しないようにもしたい。

とは言え、本当にやりたいのは音楽を聴くことなので、苦労してそこまでやることもないかとも思っている。

(10/1 17:44追記) と書きつつも、(だるくてドライブを止めたため、)暇だったので、ちょっとだけ試してみた。一番最初に気になった、GPMのページ中の再生ボタンを押したらGMBに曲を送れるようにする時に必要な、ボタンのイベントハンドラを変更できるかを試した。

最初は、安直に、ローカルのHTMLでiframeでGPMのトップページを読み込んで、JavaScriptで書き換えればできるだろうと思ったのだが、駄目だった。それから、いくつかのライブラリやAJAXやjQueryまで試したが、駄目だった。

どうも、セキュリティ上の制限のようで、ローカルのファイルからGPMのサイトを呼び出すのが駄目なようだ(おそらく、元がローカルファイルじゃなくても、サイトをまたがるようなことが駄目なのだろう)。それを回避したかったのだが、できなかった。この制限は「同一生成元ポリシー」というものらしく、かなり昔からあるようだ。

それで、一旦、ローカルのHTMLを使うのは諦めて、(GPMDPを改造して使う前提で、)GPMのページ中の再生ボタンのイベントハンドラを変更できるかを試した。JavaScriptには疎いので、それすらも苦労したが、できた。getElementById()で再生ボタンの要素を探して、onclickメンバにハンドラを設定すれば良い。以下のようにすると、GPMの再生ボタンを押すと関数click_func()が呼ばれる。

var elem= document.getElementById("buttonContent");
elem.onclick= click_func;

function click_func() {...}

onclick(onClick)は属性ではないとのことで、setAttribute()で設定しても呼ばれないことに気付かず、苦労した。あと、ボタンの本来のハンドラが依然として有効(どうやっているのか不明)なのがちょっと気に入らないが、まあ、細かいことだ。

(10/2 7:08追記) その後、GPMの再生につながるボタンは山ほどあるので、それらを全部書き換えるのは現実的でないことに気付いた。それらの呼び先を修正するのも困難なので、やっぱり、低レベルなところはGPMDP(実際には、その下位のライブラリ)に依存するのがよさそうだ・・・

  •   0
  •   1

GPMDPの改良。音量の正規化の改良について考えていたら、確か、以下のようなことだったと思うが、GPMDPでなく、外部プレーヤーで再生した方がいいのではないかということになった。

  • GPMDPの構造上避けられない、曲の開始と同時に一時停止しても先頭が一瞬再生されることがある問題は、外部プレーヤーを使わないと解決できない。
  • 再生ゲインの計算のために曲のファイルをキャッシュしているのだから、そのファイルをVLCやgmusicbrowser (GMB)などの外部のプレーヤーで再生することができる。
  • 再生ゲイン対応のプレーヤーなら、音量(ボリューム)の設定やファイルにを再生ゲインを乗算しなくても、再生ゲインタグに設定するだけで、プレーヤーによって反映されるのも良さそう。
  • GPMDPは使い勝手がいいとは言えないし、リモコンを2個にしたり、2つのプレーヤー間の切り替えもしたくないので、操作性を改善する点でもGMBで再生したい。

いずれにしても、昨夜までに構想が練り上がった気がしたし、基本的なところは容易にできそうだったので、今朝、GPMの曲をGPMDPでなく外部プレーヤーで再生できるようにしようとする作業を初めてしまった。そして、確かに、再生できるようにすること自体は簡単だった。14時前には、GMBで再生できるようになった。

GPMの曲をGMBで再生

図の左上はGMBのミニプレーヤーで、右はGPMDPのウインドウである。GPMDPが再生しているように見えるGPMの曲は、実際にはGMBで再生されている。その証拠に、双方で同じ曲・アルバムになっているし、僕はこのアルバムは持っていないし、GPMDPの再生時間は0だが、GPMは0ではない。

が、やっぱり難航し、この時間になっても終わっていない。残っている難しいことは、以下である。

  • 次に再生する曲を途切れないようにGMBに送り続ける(今は、最初の1曲の再生が終わったら停まってしまう)。また、ギャップレス再生のためには、再生中の曲が終わる前に送る必要がある。
  • GMBとGPMDPの連携 (例: GMBの再生が終わったことを検出して、次の曲を再生開始する)
  • GMBとGPMDPの操作の統合 (例:GPMDPで一時停止したら、GMBを一時停止させる)。GMBに統一すればいいのだが、曲の検索など、どうしてもGPMDPでしかできないことがある。まだ、ユースケースの検討が充分でないので、方針が固まっていないせいもある。

でも、今日のところは、目論見どおりGMBで再生できたので、良しとする。

(気が向いたら、詳細を追記します。)

(9/24 21:35追記) どうにかこうにか、次に再生する曲をGMBに送り続けられるようになった。GMBも自分で作ったGPMDPの追加プログラムも、想定外の動きをしたりして、なかなか大変だった(おもしろかったけどw)。

GPMの次の曲を自動でGMBに追加

図の左はGMBのウインドウで、右はGPMDPのウインドウである。GPMDPのプレイリストの、現在再生中の次の曲がGMBに自動的に追加され、連続再生できるようになった。

余談: 上のジャケットの工藤静香、なんかお化けみたいだ。。。

動き出したばかりで、まだまだ荒削りで、さまざまな不具合はあるが、とりあえずは音楽が聴ける状態になったので、一段落だ。それに、(GMBの)リモコンで一時停止などの操作ができるのは、すごく便利だ。

不具合の例:

再生していると、再生中の次の曲以降も勝手にGMBに追加される。まあ、悪いことではないが、想定外の動作なので複雑な心境だ。そして、それに伴ってGPMDPの下部のプレーヤー部分は、まだ再生していない曲になってしまう。しかも、それは上側のキューの再生中(と思っている)曲とも違うのが解せない。

  • 再生中の曲: "Here comes the Sun"
  • GPMDPが再生中(と思っている)曲: "Let it be"(上部)、「二人静」(下部)

細かいことが多いので、先は長そうだw

PS. 今日からか、URLに"?hl=en"を付けても無視するようになったようで、日本語で表示されるようになってしまった。やっぱり、ネットサービスはvolatileだなあ。 ← URLの書き方を間違えていたようだったので、撤回する。 (9/25 20:26)

(9/25 22:56 わずかに修正、余談を追加)

  •   0
  •   1

先週末に、gmusicbrowser (GMB)に、デスクトップウィジェットのアルバムアートを押すと、メインウインドウをアイコンにしたり戻したりする機能を付けた。GMBは、メインウインドウを閉じた時に、タスクトレイに最小化(アイコンにする)できるのだが、タスクトレイアイコンが小さくて押しにくいので、もっと楽に操作したかったのだ。

実装は意外に難航した。普通に最小化したのでなく、タスクトレイに最小化したのを復帰させると、その前より幅が広くなるという問題が解決できなかった。それで、普通に最小化した時にしか復帰できないようにした。

そして、今朝気付いた。実際には、GMBをタスクトレイに最小化しない設定にして(その時は間違って閉じないように注意する必要がある)、普通のアイコン(小さくない)で操作すれば充分だし、その方が操作が一貫している。あるいは、メインウインドウを閉じた時に、タスクトレイでなく普通に最小化するように変更するほうが、ずっと簡単だったような気がする。それに、今気付いたが、この機能では、アルバムアートがない場合は操作できないという欠点がある。

本当の問題は、「タスクトレイアイコンが小さくて押しにくい」というだけのことだったのだから、何か押しやすいものがあれば良かった訳で、それは(既にある)普通のアイコンそのものなので、苦労して新しい機能を作らなくても良かったのだ。

まあ、折角作ったのだから戻さないでおこう。そして、後で、幅が広くなる問題を解決したい。これは、GMBの起動時にも起こることがあるので、何かがおかしいようだ。

そして、物でもソフトでもシステムでも世の中の仕組みでも、安易にいろいろな機能や施策を追加しがちだが、その前に、本当の問題をもっと良く考えた方が、複雑にならず、コストも掛からず、新たな問題も生まれないのだろうと思う。とは言え、安易に見えるけど、実際には、多くの「頭のいい」人たちがいろいろ考えた末におかしくなってしまうことも多々あるので、なかなか難しい。

PS. 世の中の仕組みなどは、実際には、「いろいろ考える」方向が意図的に異なっていて、本当の問題は分かっているけど、根本から解決する気は毛頭なく、「頭のいい」人たちの都合のいいように「解決しようとしている振り」をするように見せて、みんなを欺いていることが多い気がする。

PS2. 本題のGMBだが、昨夜、メインウインドウの閉じるボタンを押した時に、タスクトレイでなくアイコンに最小化することができるように変更した。また、閉じた時の動作設定に、従来の動作(閉じた時にタスクトレイに最小化または終了)に加えて、アイコンに最小化する動作も追加して選択できるようにした。これで、間違って閉じるを押して終了することもないし、ウィジェットでもアイコンでも押せば復帰できるから、操作も統一できた。(1/25 4:47)

設定ウインドウの追加した部分

  •   0
  •   1

GMBでアルバムアートの検索をするプラグインPicture finderは、USのYahoo!でしか動かない。Googleなどを選択しても、結果が何も出ない。

APIが変わったのだろうと思ったが、実際に調べたら、確かにGoogleの検索結果のフォーマットは変わっていた。それで、とりあえず、Googleで検索できるようにした。

新しいフォーマット(構造化されたデータのようだ)に合わせて、fetch_cover.pmを修正した。まだまだPerlは良く分からない(し、分かりたくもない)が、既存のソースから類推してやってみたら、出来た。参考までに、主要な変更点を載せようと思ったのだが、良く考えると、日本のユーザーはとても少ないだろうし、分かる方は既にやっているだろうから、止める(もし欲しい方がいらっしゃいましたら、お知らせ下さい。無保証にて差し上げます)。

日本のYahoo!にも対応したかったが、面倒だし、中で使っているのはGoogleなので、止めた。他のサイト(例: last.fmやAmazon)にも対応したかったが、やっぱり面倒なので、とりあえず、Googleだけで満足することにする。

ただ、そのGoogleにしたって、彼らの心変わりによって、いつか変更が要るようになるのだろうな。でも、出力がHTMLである限りは大丈夫だ。でも、それもそのうち変わる気がする。。。

  •   0
  •   1

 

この週末は、デジカメからの画像取り込みプログラムの改良(同一カメラで1万枚以上への対応)や掃除や散歩やドライブなど、有意義なことをしようと思っていた。

のであるが、実際には、したかったことは何もできなった。

昨日は、疲れのせいかだるかったので、家にこもってEvernoteからDropbox Paperへの移行をした程度だ。移行は、本文はコピー・ペーストでできるのだが、埋め込んだ画像はできないので、それを手作業で復元する(元のファイルがPCに残っているのはそのまま、オリジナルがあるものはリサイズして、Paperに埋め込む。ないものは、EvernoteからダウンロードしてリサイズしてPaperに埋め込む)必要があって、結構時間が掛かった。日記1個に30分くらい掛かった。全部で7本を移行して、日記の残っていた分は終わった。が、あとドライブ関係が44本もあるので、移行プログラムを作りたくなって来た。

今朝は、「今日こそは」と思っていたのだが、思わぬ落とし穴にはまった。発端はgmusicbrowser(GMB)の、次のリストだ。

Something wrong!

何の変哲もない、普通の(と言うには重複が多いがw)ビートルズのアルバムの一覧の一部に見えるが、常々感じていた不満(何が悪いかすぐに気付いた方は、ちょっとした愛好家ですね)を思い出して、余計なことを考え付いてしまった。

同じ年に出たアルバムをちゃんと発売順にできないものだろうか?

説明すると、上の図の上半分の黄色いのは"Magical mystery tour" (1967年11月発売)、下半分のごちゃごちゃしたのは"Sgt. Pepper's lonely hearts club band" (1967年6月発売)である。それぞれ同じ年に出たのではあるが、気に入らないことに、リストには発売の逆順に出ているのだ。タイトルがアルファベットの順だとM→Sなので、上のように並んでしまっているのだ。

まあ、「そんな細かいこと、自分で分かってればいいじゃん」とは思うし、今まで我慢して来たのだが、他の例では、デビューアルバムの前にセカンドアルバムが出ることもあった覚えがある。さすがにそれはどうかと思う。要は、元になったMP3の仕様(ID3)が安直で、現実に対応できていないのだ。それを見なかったことにして使い続けるのは、技術者としてはやっぱり嫌なのだ。

技術的な問題以外にも、仮に上のリストの順で「全曲再生」したら、曲順がおかしくて、それこそ話にならない。

ソートに使われる発売日の情報は音楽ファイルに格納されているのだが、基本的には年だけしか格納できない。せめて月まで入れられればいいのだが、仕様上は無理だ。

それで、いくつかの案を考えた(それだけで数時間掛かった気がする)。

  1. 余り使っていないフィールド(例: Mood(TMOO), Grouping(TIT1), Rating, Discname(TSST)など)を流用する。
  2. GMBにカスタムフィールド(例:リリース日(例: "19670601"), 同じ年の時の順番(例: 1, 2))を追加する。
  3. GMBにAlbum sort order(TSOA)フィールドを実装する。
  4. GMBに他のフィールド(例: Original release time(TDOR))を実装する。
  5. 新しい独自フィールドを実装する(TXXXを使う)。
  6. GMBに2個の追加フィールド(1: リリース日、2: リリース日があればリリース日、なければ年)と、後者のフィールド(2番)を使うソート条件を追加する。

検討したところ、最後の6が残り(正確には、1-5の検討の結果、6のように2段階にしないとうまく行かないことが分かった)、「ちょっと試しに」実装に着手してしまったのが運の尽きである。半分は意外にすんなりできて、下のように、「リリース日または年」のフィールドが表示できたのだが、例のリストのソート順に反映することができなくて、(昼寝もしたけど)気付いたら外は暗くなっていて、とてもがっかりした。

「リリース日または年」が出るようになった (年はアルバム名の右の<<の右にも表示されている。上のエントリのアルバム名とリリース日は、テスト用の架空のもの)

随分時間を掛けてうまく行かない理由を調べたのだが、どこを直せばいいのか、まだ分からない。ソート時に、追加したフィールドをどうしても参照できない。この辺りのプログラムは、他と比べて今ひとつ直感的でなくて分かりにくく、GMBの暗部を見た気がした。まあ、何にでも触れて欲しくないことはあるだろうから、仕方ないね(僕のプログラムにだって、結構ある)。

試行錯誤の結果、何がどうなっているのは分からないが、どうにか、リリース日(なければ年)でアルバムをソートできるようになり、問題の"Magical-"が"Sgt. Pepper's-"の下に出るようになった。

リリース日でアルバムをソートできるようになった。

ところが、一難去ってまた一難。今度は、次のような、ちょっと困った状況に陥ってしまった。

"Baby it's you"はどこに置けば?

一番上の"Baby it's you"は、近年発売されたシングルなのだが、録音は1963年なので、入手した時に年に録音の年を入れた。これをどこに配置すべきなのか、結論が出ない。正しいリリース日にすると、初期の曲なのに最後の方に出るし、録音日にすればそれらしい場所に出るが、リリース日を入れるべき箇所に録音日を入れるのは掟破りな気がする。

規則に従ってリリース日を入れて、最後の方に出すのがまっとうなのだろうが、もっといい規則を決めたいところだ。「録音年」のフィールドを作ると、何かいいことがあるだろうか? (12/13 23:00)

"Baby it's you"の「リリース日」の解決策が見つかった。放送日をリリース日に入れることにした。ただ、シングルとして発売されたこの曲は、アルバム"Live at the BBC"に入っているのとはバージョンが違うので、調べても放送日が良く分からない。ただ、録音日は3日しかなく、そのうちアルバム版と同じ日に収録されたと推測できる。そして、もしかしたら放送されなかったのかも知れない。が、いくら考えても埒が明かないので、アルバム版の放送日とした。(12/15 5:09)

  •   0
  •   1

以前からやりたかったのだが、gmusicbrowser(GMB)のレイアウトと使い勝手を改良した。Perlで書かれたGMBのプログラムや、レイアウト設定ファイルを、見様見真似で試行錯誤して、なんとかした。主な変更点は以下のとおりである。

  • メインウインドウ (ベース= "Lists, Library & Context")
    • 現在の再生ゲインのモードを、"--"(off), "T"(トラック), "A"(アルバム)のように表示できるようにした。アイコンでも表示可能なのだが、適当な画像ファイルがなかったので、とりあえず文字にした。この部品は新たに作った。
    • 上記再生ゲインの表示を押すと、off → トラック→ アルバム → offのように順にモードが切り替わるようにした。
    • タイトルなどの文字やアルバムアートを大きくし、パーツの配置を改良した。
    • ボリュームを水平のスライダーにし、音量の値も表示するようにした。ボリュームは実際にはほとんど操作しないのだが、現在の音量が一目で分かるのはいい。
  • デスクトップウィジェット (ベース= "screenlet")
    • 再生位置のバーの右に、再生順序(シャフルなど)、フィルタ(自動プレイリスト)、キューの状態(次の曲の指定の有無、再生中の曲が終わったら停まるなど)、再生ゲインのモードを追加した。これは、メインウインドウに表示されているものと同じである。
    • 再生位置のバーの中に再生位置(時間)を表示するようにした。
    • タイトルなどの文字を大きくし、発表年も表示するようにした。
    • 文字領域の背景を不透明にした。

まだ収まりが悪いところや色がイマイチなところがあって、いろいろ直したいのだが、かなり僕の理想に近くなった。特に、デスクトップウィジェット(MusicBeeのコンパクト/ミニプレーヤーに相当)で再生ゲインのモードが表示・切り替えできるのは、MusicBeeではできなかった(要望しても対応してもらえなかった)ことで、長年の悲願だったから、結構嬉しい。

残念なのは、以下のことはすぐにはできないことだ。どれも、GMBの構造上、設定や小さい変更での対応は困難なようだ。

  • メインウインドウの曲一覧などのフォントが(設定では)変えられない。見難くはないから、とりあえずは問題ないのだが、中国(台湾?)系のフォントのようで、例えば、「福」の偏が「示」になったりしているのがちょっと気になる。
  • (特にデスクトップウィジェットで、)タイトルなどが長い場合に、複数行での表示ができない。

それでも、こうやってプログラムや設定を変えることでかなりのカスタマイズができるところは、完全にMusicBeeを超えている。特に、レイアウトの設定ファイルは独自の言語のようになっているのだが、もしそうでなかったら、こんなに自由なカスタマイズはできなかっただろう。その点は(最初は訳が分からなくて戸惑ったものの)感心している。それで、偶然にせよ、なかなかすごい「原石」を見つけたものだと自画自賛している。

あと、PerlでGUI(GTK)のプログラムが作れるのは、PHPにはできないことなので、すごく羨ましい。それにしても、どうしてPHPではできないのだろうか?

試行錯誤して、なんとか、デスクトップウィジェットでタイトルなどが長い場合に改行できるようにした。GMBがテキスト表示に使っている、GTKのLabelウィジェットのワードラップの機能を使うようにした。これには、設定だけでなくGMB本体の改造が必要だった。

アルバム画像下の曲情報は、以下のようにデスクトップウィジェットのレイアウトファイル(desktop.layout)に記述して表示している。

HBtrk_info= 8Text(font= 'Noto Sans CJK JP Regular 10',
color=black, opacity=1,

width-chars=32, wrap=1, wrap-mode=word,
markup="$title
<span size='xx-small'>\n\n</span>
$artist<span size='xx-small'>\n\n</span>
$album ($year)")

下線部で最大幅とワードラップを指定しているが、これをサポートする機能をGMBに追加した。GTKを良く理解せずに作っているので、最大幅はピクセル数で指定したいができておらず(そのため、文字数によって、右側に微妙に空白ができたり、ウインドウの幅が広がったりする)、文字の大きさや空行(無理に作っている)の高さなど、いろいろ改良・調整したい点はあるが、とりあえず、やりたいことができたので、うれしい。(11/9 23:53)

空行の高さは、タイトルなどの各項目を別のLabelにして、パディングを指定して縦に並べることで解決した。ただ、最後の要素(アルバム)にはパディングを指定しなくても隙間が空くのが謎だ。

また、右側の微妙な空白は、width-charsを指定しなければできないことが分かった。ただ、その場合、max-widthやmaxwidthを指定してもウインドウの幅が広がることがあるのが謎だ。(11/10 6:22)

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88_2016-11-10_06-03-57

行間を改良

HBtitle= 8Text0(font= 'Noto Sans CJK JP Regular 12', color=black, opacity=1, \
width-chars=32, wrap=1, wrap-mode=word, \
markup="$title")
:
VBmain = Cover(yalign=0, forceratio=1,hover_delay=1, \
hover_layout_pos=.5w x w,hover_layout=play_controls) \
8HBtb_ind HBtitle 4HBartist HBalbum

思わぬ欠点が見つかった。ウインドウのサイズが広がった後に、行数が少なくなると、下の方が空いて間抜けになってしまう。ちょっと調べたところでは、gtk_widget_set_redraw_on_allocate()で直りそうな感じだが、さてどうだろう? (11/10 19:46)

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88_2016-11-10_19-43-56

間抜けな状態

更に試行錯誤したのだが、なかなか広がったウインドウは小さくならない。そこで、次善の策として、アルバムアートと曲情報の部分で余白を吸収するようにしてみたら、今までよりはマシになった。また、どういう訳か、アルバムアートが段々大きくなることがあったので、サイズを厳密に指定した。

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88_2016-11-10_22-02-22

余白が目立たないようにした。

HBcover= Cover(minsize=250, maxsize=250, forceratio=1, \
hover_delay=1,hover_layout_pos=.5w x w,\
hover_layout=play_controls)
:
VBmain = _HBcover 8HBtb_ind _VBtrk_info

こういう試行錯誤って、GMBやGTKやPerlをもっとちゃんと勉強すれば効率良くできるのだろうが、趣味なのでそこまでの気力は出ない(もし、PHPでGTKが使えるのなら、俄然やる気が出るのだがw)。逆に、いろいろつまずいたり、回り道・寄り道しながら自分の希望を叶えていくのがおもしろいと思う。そして、仕事じゃないから最短経路をとる必要はなく、こういう遊びがかえって仕事のヒントになることもあると思う。(11/10 22:17)

(Labelウィジェットのワードラップ機能が今ひとつのせいなのか、)改行時に文字列右側にできる無駄な空白が嫌なので、ちょっと頑張ってTextViewウィジェットを使えるようにして、解決した。

ただし、どういう訳かTextViewの背景色を設定することが難しいようなので、とりあえず全体を白にした。それでも、どういう訳か微妙に色が違っていた(TextViewがわずかに黒くなっていた)ので、それに全体を合わせた。が、それでもまだ合わないようだ。あと、上下に広がったウインドウが狭まらないのは相変わらずである。そこら辺は今後の課題としても、かなり良くなったのは確かだ。

あと、TextViewにしたことで、曲名などをマウスで選択してコピーすることができるようになった。これもMusicBeeで叶えてもらえなかった(「ホットキーでできるからいいでしょ」と言われた)ことなので、感慨深いものがある。

ただし、曲名とアーティストとアルバム名をそれぞれ別の要素にしているので、コピーも別々にしかできない。とある理由(TextViewの中では、フォントサイズを容易に変更できない)で別々にしたのだが、できれば何とかしたい。でもまあ、選択・コピーできないよりはずっといいだろう。

ちなみに、デスクトップウィジェットのレイアウトファイル(desktop.layout)でのTextViewの指定は、以下のようになる(タイトル表示の部分)。

HBtitle= 5TextView0(font= 'Noto Sans CJK JP Regular 12', \
color=black, bgcolor=lightgray, minwidth=200, maxwidth=250, \
wrap-mode=word, markup='%t')

(11/11 21:18)

ちょっと苦労したが、曲名とアーティストとアルバム名を同じTextViewに入れられた。insert_with_tags()を使い、TextViewの中でフォントサイズを指定するようにした。これをTextViewのサブクラス"SongInfoView"と名付け、以下のような指定で曲情報が出るようにした(フォント指定は未実装)。

HBsong_info2= 5SongInfoView(font= 'Noto Sans CJK JP Regular', \
color=black, bgcolor=lightgray, minwidth=200, maxwidth=250, \
wrap-mode=word)

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88_2016-11-11_23-15-12

全部の曲情報を一度で選択できるようにした。

まったく"The long and winding road"だが、いろいろアイデアが出て楽しい。(11/11 23:22)

選択はできるが、Ctrl-Cではコピーできないというオチがあった。右クリックでメニューを出して"Copy"を選択すれば可能なので、キーを登録すればいいのだろう。やっぱり長い道のりだw (11/11 23:51)

ショートカット(あるはアクセラレーション)は有効にならず、広がったウインドウは、どうしても縮まない。前者は、どうもOSレベルの設定が関係しているようだ。もちろん個人の設定ファイルもあるようだし、「デスクトップ環境」なのだから、その方が筋がとおっているのだが、アプリだけで手軽に対処するのが難しいし、ログインし直さないと試せないので面倒だ。そして、後者は深い謎だ。八方手を尽くしたが、どうにもならなかった。GTKをちゃんと勉強しないと駄目かも知れない。

そして、昨夜からの収穫は、(外見上は)本当に微妙な修正だ。タイトルなどを一度にコピーできるようにした時に、行間が数ピクセル広がってしまったのを直した。なんか、大昔、LaTeX(って知ってる方はいますか?)で資料を書いて、微妙なところを何度も直して(印刷して紙を無駄にして)、最後にできた紙(の内容でなく外見)を見て悦に入っていた頃の雰囲気を感じているのだが、なんとも複雑な気分だ。まあ、僕は賛同していないが、神は細部に宿るのかも知れないし、趣味だからいいよねw

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88_2016-11-12_17-39-53

行間を調整

そして、何度もくどいのだが、(謎はあるものの)こうやって自分でちょっとプログラムを書いて自分好みに自由に改造できるのは、ありがたいし楽しい。ただ音楽を聴けるだけじゃなくて、その道具に手が入れられるようになったのは、常に歯痒かったMusicBee/Windows時代に比べるとすごい進歩だし、結構感激している(酔ってるせいもあるが)。これは、GMBの基本思想が良く、オープンソースにしてくれて、今の実装が悪くない(Perl自体が読みにくく、コメントは少ないけど、まあ酷くはない)ことに加えて、スクリプト言語(Perl)/Linuxベースであることもある(やっぱり、腐り切った窓でC#なんて使ってられない)。そして、こういう機会を与えてくれた、gmusicbrowserの作者のQuentin Sculoには本当に感謝している。いつか、改良・変更部分を整理して提出したいと思っている。(11/12 18:28)

ここからはいつものディスになるのだが、僕がMusicBeeを止め(られ)た原因は、作者の(勝手な)方針について行けなかったということだ。作者本人が個人で作っているのだから、何をしたって自由なんだろうけど、ただ、他人の意見を余り聞かず、ソース公開せずに他人の力をほとんど借りようとせず、Windowsにべったり固執し、全く必要がなくメリットが感じられないことを「改良」して、バージョンアップしたと言って悦に入っているのは、MicrosoftやAppleのように筋が悪いし、もし彼がソフト技術者だったら、「終わっている」と思う(一言で言えば、ガキだ)。途中まではいい方向に進んでいたし、機能的には他を寄せ付けないのに、とても残念である。(11/12 18:42)

試行錯誤の結果、デスクトップウィジェットのサイズ(特に高さ)が行数に応じて縮まるようになった。ごく当たり前に、ウィジェットのresize()メソッドに小さ目のサイズを指定すれば縮んだ。以前にも試したのだが、ウィジェットのnewの中から呼んだのが失敗して、それでresizeは使えないと思い込んでいたのだ。どうも、newが終わらないとうまく動かないメソッドがあるようだ。もちろん、newの中でresizeなんてするのは良くないのだが、GTKやGMBでウィジェットのメソッドの呼び出し順序が良く分かっていないので、newの時に描画する文字列を設定することになって、結果的にresizeもしている。(11/14 22:31)

その後、ワードラップでの改行の高さが若干高く(広く)て下の要素との区切りと見分けが付きにくい場合があったので、微妙に改良した。更に、プレイリストやアルバム中の何曲目を再生しているかを、"3/10"のように表示できるようにした。(11/18 21:52)

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88_2016-11-18_21-48-21

微妙な改良

 

※まずいないとは思いますが、もし、日本語が読めてGMBを使っていて、「ここの投稿を参考に改造したいけど、詳細が書いてなくて今ひとつ分からない」という方がいらっしゃっいましたら、可能な範囲でお教えしますので、お知らせ下さい。でも、プログラミングのスキルがあることが前提です(申し訳ないですが、「コピペでなんとかしたい」という方は駄目です)。そして、そういう方は、自分でできてしまうような気がします。(11/10 19:31)

(11/11 10:14 わずかに修正, 11/12 追記)

  •   0
  •   2

先週、GMB(gmusicbrowser)の音楽ファイルの同期プログラム(sync-music)の高速化のアイデアがひらめいたものの、疲労や多忙ややる気がでなかったりで、なかなか実装できなかったのだが、昨日と今日で何とか作り込めた。途中では期待したほどの結果が出ず、がっかりしかけたのだが、その原因が分かって、最終的には期待以上の結果になった。

高速化の内容と効果

  1. 音楽ファイルのタグ情報を、GMBから同期プログラムに渡す。→ ファイルのタグ情報をsoxiやexiftoolなどの外部プログラムを使わずに取得できるので、その分高速になる。
    1. GMBでの同期開始時に、同期対象のファイルの同期に使うタグ(アーティスト、アルバム名、トラックの再生ゲイン、埋め込みアルバムアートの有無)の値をCSVに入れて、同期プログラムに渡す。
    2. 同期プログラムは(音楽ファイルを開かずに、)CSVの内容から、同期先ディレクトリ、音量調整量、アルバムアートの埋め込みの必要性を判断する。
  2. アルバムアートが埋め込まれているファイルには、アルバムアートを埋め込まない。→ アルバムアートを縮小して埋め込まなくて済むので、その分高速になる。

結果(ポップス全10677曲、非実行(エンコード・ファイルコピーを行わない)での同期= 全く更新がない場合を想定した所要時間):

  • オリジナル(V1): 93秒 (8.7ms/ファイル)
  • 高速化後(V2): 8秒 (0.75ms/ファイル)

先週の「今の数倍〜10倍高速になりそう!」の目論見を超えて、

約12倍速となった!

(ただし、非実行で速度を比較したので、上記2番の成果は反映されていない)

更新がない場合に8秒なら許せるし、MusicBeeと同じくらいの速度になったと思う。高速に動くプログレスバーは気分がいいものだ。

高速化ってのは、「血のにじむような」というと言い過ぎだが、まあ、堅く絞った雑巾から更に水滴を絞りだすようなところがある。実際、「これ以上は無理」と思えても、更に良くなることがあって、そこがおもしろいのだが、結構精魂を使い果たす傾向はあるw

V2のその他の機能の実装と動作確認と修正が終わり、さっそく車で使っているUSBメモリに同期しているのだが、そのメモリ(Lexar 128GB)が激遅で、エンコードしたファイルを書き込むのにものすごく時間が掛かって、並列処理も高速化も台無しというオチになった(爆) もしかしたら、メモリが壊れ掛けているのかも知れない。(11/5 16:24)

USBメモリへの同期は(11/6の)明け方に終わっていた。約11時間(3.8秒/ファイル)掛かった。(エンコード時に)PCのSSDに対して書き込む場合には約1秒/ファイルだったので、このメモリは約4倍遅かった。そして、実行ログを見たら思わぬ不具合が起きていた。同期したファイルの一部を削除していたのだ。バグかと思って調べても、なかなか原因が分からなかったのだが、結局はWindowsのファイルシステム(VFAT)の異様な仕様のせいであることが分かった。一つは、ファイル名の最後の"."を無視するのと、もう一つは、ファイル名の型(大文字/小文字)を無視するというものだ。以下に例を示す。

  • "The Journey Continues..."というディレクトリを作ると、実際には"The Journey Continues"ができる(最後の"..."がなくなる)。
  • "WINK MEMORIES"というディレクトリが既にある場合に"Wink Memories"というディレクトリを作ってもエラーにならないが、実際には既にある"WINK MEMORIES"に統合されてしまう。

前者は意味不明だし、後者はいまだにそんな古臭い仕様を残しているのに呆れて頭に来る(笑えるさすがなことに、高慢な意識高い林檎社のデスクトップOSも、Unixがベースなのにわざわざ型を無視していたっけ)。一般人は大文字と小文字の区別ができないとでも思っているのか? 今使っているのはLinuxだが、互換性維持のために、そういう下らない仕様も実現しているのだろう。

問題に対処して確認していたら、ドライブに出ようと思っていた時間(7:30頃)を過ぎてしまった。そして、疲れたので、今まで寝ていた。動作確認を兼ねて、午後には出掛けたいな。(11/6 11:45)

結局、GMBの改良(のための試行錯誤)をしているうちに億劫になって、ドライブには行かず仕舞いになったが、駐車場でエンジンを掛けて再生確認して、問題なかった。音もアルバムアートも出た。何となく音質が悪い気がしたが、ヘッドフォンで聴いた時には問題なかったので、疲れとか気のせいだろう。これでひとまず、音楽のWindows(MusicBee)からLinux(GMB)への移行は完了だ。あとは、おいおいGMBに細かい改良をして行こう。

でも、その前に、写真(画像)のLinuxへの移行(ACDSee → digiKam)を完了させたい。(11/6 17:33)

(11/5 18:21 加筆, 18:43 誤りを修正, 11/6 11:45 加筆, 12:58, 15:17 加筆修正, 17:33 車での確認結果他を追記)

 

PS. 途中で余り高速化できなかったのは、並列化処理の検討不足だった。並列化の時は、複数のファイルを同時にエンコードするのだが、同時に実行している数が最大値に達した場合に、更に処理を開始するには、どれかが終わるまで待つ必要がある。待つ時、一定の時間間隔でどれかが終了したかどうかを確認するのだが、その間隔が500msと長かったために、高速化が妨げられていた。

まあ、実際にはエンコード(数秒掛かる)するので、500msでも問題ないのだが、ファイル数が多くて更新がほとんどない場合にはかなり効いてくる。

PS2. こういうアイデアを入れて、最初に動かした時に結構うまく行くと、うれしくなる。「こいつ、動くぞ」と脳内に浮かぶかも知れないw

  •   0
  •   1

MusicBeeからgmusicbrowser(GMB)への移行は、かなり終わりに近く、もうMusicBeeは関係なくなって、GMBに閉じた話になっている。ただ、いつものことだが、ゴールは見えると遠ざかるもので、動作確認でいろいろな問題が出て来て、なかなか終わらない。予想外にも今日はほとんど全部使ってしまったのだが、あと少しでようやく本物のUSBメモリで試せそうな感じだ。以下に、つまづいたことを書く。

全体的な動作確認

ファイル名のクォート("や`や/などの特殊な文字を無効にする処理)がまだ不完全だった。これの対応はどうも場当たり的で良くないので、根本的に別の方法にした方がいい気がする。同期プログラムからOSのコマンドを呼ぶ(system()など)時に、ファイル名をシェルに解釈させないようにすれば、クォートしなくてもうまく行くように思うので、あとで何とかしたい。(が、そう思っていて実際にやったことは少ない・・・) → シェルに特殊文字を解釈させないようにする方法はないようなので、簡単には行かなそうだ。system()などを使わなければいいのだろうか? (23:28)

詳細な動作確認-エンコードしたファイルの確認

音量の正規化が全く駄目だった。再生ゲインはピーク値で決まっている訳ではないので、ファイルからピーク値のタグを抽出して、それで音量を調整しても駄目で、トラックの再生ゲインのタグの値で調整すべきだった。ffmpegにはタグに書かれたトラックの再生ゲインで音量を調整する機能がないので、soxiで(アーティスト名などのタグと同時に)トラックの再生ゲインのタグを抽出して、その値からffmpegに指定する音量の値を計算することにした(再生ゲインのタグがない場合は諦めるが、実際には、すべての手持ちファイルには再生ゲインのタグが入っている/入れるので、問題はない)。なお、exiftoolは処理が遅いので、基本的には使わないことにした(soxiで文字化けする場合は諦める)。

今、何となく、エンコードと音量の正規化にはffmpegでなくsoxを使った方がいい気がして来たので、あとで考えよう。soxが文字化けするのはMP3だけだろうから、その時だけ特別な処理をすれば良さそうだ。→ 残念ながら、soxはアルバムアートの埋め込みができないので、使えない。(23:25)

ポータブルHDDにポップス全曲(約1万曲)を同期して確認

処理が遅過ぎた。最初の同期では、1万曲をエンコードするのに約4時間掛かった。全部エンコードしたのだから仕方ないのだが、エンコードしない場合(更新ファイルの確認のみ)にも遅かった。1万曲で約1000秒(17分)掛かった。いろいろ調べたところ、上に書いたように、exiftoolが遅かった。それをsoxiに換えたところ、約10倍高速になった。ただし、soxiはMP3の再生ゲインタグを抽出できないので、その場合はexiftoolも使うことにした。

また、下に書いた、子プロセスの終了判定がうまく行かないために入れた処理も遅かったのだが、原因が分かったので省けた。現在は、(エンコードしない場合)1万曲で約100秒(1.7分)で済むようになった。これでもまだ遅い気がするので、あとで何とかしたい。

その他に、ファイル名のクォートの問題も再発したし、タグからアーティスト名を抽出する処理にバグがあったりもした。

それから、GUIのキャンセルの検出処理がうまく行かなかった。子プロセスの終了を判定する関数(pcntl_waitpid())が期待どおりに動かず、かなり手こずったのだが、結局は、それに渡すプロセスIDが異なっていたことが分かった。

なお、最初の同期(エンコード処理)時のシステム負荷なども調べたので、以下に書く。

  • CPU使用率: 約80% (同時処理数=5)
  • CPU温度: 約60℃
  • メモリ使用率: 約20%
  • GMBの音切れ: なし
  • 同期対象ファイル一覧のサイズ: 約1.5MB
  • 結果のログのサイズ: 3.7MB
  • ディスク使用量: 約55GB/約1万曲

使用率80%で動かし続けると、さすがにCPU温度が上がって、ファンが少しうるさくなる。でも、BIOSにファン回転数の調整機能があるようなので、安心だ。ファイル一覧のサイズは思ったより小さくて、同期プログラムなどは問題なく動作した。ただ、ログをエディタ(kate)にペーストしたらハングしてしまったのには、結構がっかりした。

その他の問題

  • キャンセル(処理の中止要求)の検出のためにzenityのプログレスの終了を待つつもりが、全部の子プロセスを待っていて、並列処理しているエンコードプロセスの終了でキャンセル処理してしまった。
  • [GMB] 再生中に再生ゲインがoffになることがあるようだ。メニューの表示と実際が合っていない。原因不明なので、追って調査する。
  • [GMB] 再生するだけでファイルが更新されて、同期対象になってしまう。プラグインalbuminfoが自動的にジャンルを更新しているので、一旦、albuminfoとartistinfoを使うのを止めた。あとで更に調査して修正したい。
  • [zenity] LANG=Cで起動して、日本語のディレクトリを選択すると、出力が"?????"になる。→ zenityは日本語(LANG=ja_JP.utf8)で起動することにした。
  • [zenity] プログレスのウインドウのサイズが大きくなる。原因不明。文字列(ファイル名)が長過ぎるからか。

今後

少し使ってみて、不要にファイルが更新されないことを確認してから、実際に車で使っているUSBメモリに全曲を同期して、カーナビで再生できることを確認したら、終了となる予定。ただ、上述のように、改良したい点もあるので、ゴールはもう少し先になりそうだ。

その後、いろいろ良さそうなアイデアが浮かんだので、これが完成する前に第2版に着手しそうだ。最もいい考えは以下だ。

この同期プログラムをGMBから呼ぶ時に使っているexportというプラグインには、曲のファイル名や情報をCSVやM3U形式で書き出す機能があるのだが、(必要ならそれを改造して)同期の時に使うタグ情報(アーティスト名、アルバム名、トラックの再生ゲイン値)を書き出せば、音楽ファイルのタグを読む必要がなくなるから、今の数倍〜10倍高速になりそう!

それから、もし上が駄目でも、次の案もいい。

今は音楽ファイルのタグは外部プログラム(exiftoolやsoxi)で抽出しているが、実はPHPのライブラリでも取得可能だ。php-getid3というライブラリはいろいろなフォーマットに対応しているから使えそうだ。これを使えば、外部プログラムを呼ばなくて済むので、数倍高速になる可能性が高い。

更に、もし外部プログラムを使わないで済めば、特殊文字のクォートが不要になるので、プログラムが「マトモ」になる。ただ、ffmpegは必ず使うので、特殊文字の展開をしないsystem()関数を作る必要はある。

そして、そろそろプログラムの改良や修正が怖くなってきた(間違ったら戻れない)ので、GitやSVNのようなバージョン管理システムを入れようかと思っている。→ ローカルだけで使うのであれば、Gitがとても楽なことが分かったので、それにした。GUIはSmartGitを試している。(10/31 22:55, 11/1 6:39)

  •   0
  •   0

昨日辺りから、MusicBeeからgmusicbrowser(GMB)への移行に関して残っていた、USBメモリなどのデバイスに曲を同期(転送)する機能を作る作業が捗って、基本機能がほとんどできた。

機能概要は以下のとおり。(MusicBeeのデバイス同期機能にならった)

  • 転送対象: GMBの管理している曲。特にフィルタ(=自動プレイリスト)の結果の曲。
  • 転送先: USBメモリなど (Linuxのディレクトリなら何でも可)
  • 転送時の処理: MP3でないファイルはMP3に変換して転送する。
  • その他: 同期先にあって同期元にないファイルは削除する。

どのようにGMBに組み込む(連携させる)か、いろいろ考えたのだが、GMBのexportプラグインを改造することにした。これには多くのメリットがあった。

  • プラグインなので、(外部コマンドを自分で起動するなどせずに)GMBからシームレスに起動できるようになった。
  • GMBのプレイリストをファイルに保存する機能があるので、それを使って、同期対象のファイル一覧を作れる。(当初は自分でプレイリストを保存する手順が必要かと思っていたのが、不要になった。)
  • 外部コマンドを呼ぶ機能もあるので、それにならって、実際に同期を行う外部プログラムを起動できる。
  • 設定機能もあるので、同期機能の設定を作る時の参考になる。

それからいろいろ検討した結果、以下のような作業・処理手順を実現することにした。

  1. デバイス(USBメモリ)を挿す。
  2. GMBで、同期対象のプレイリスト(フィルタ)や曲を右クリックして、"Sync to directory"を選択する。
  3. (以下の処理が終わるまで、GMBは待ち状態になる。)
  4. 同期プログラムは、指定された曲に対して、以下の処理を実行する。
    1. その曲が同期先(デバイス)にある場合には、何もしない。(同名だが内容が違う場合に備えて、ファイル名以外にタイムスタンプでも判定する: ホストの日時 > メモリの日時 → 転送する)。
    2. その曲が同期先にない場合、曲のフォーマットがMP3でなければMP3にし、トラックの最大値を統一する(例: -0.9dBになるように、データを増幅する)。同時に、再生ゲインのタグを除去して、同期先にコピーする。
  5. 同期プログラムは、同期先にあって同期元にないファイルを削除する。

ある程度実装した後、以下の問題が見つかったので、対処した。

  • GMBは同期プログラム(外部プログラム)の終了を待たない。→ 同期結果が分からない。→ 同期するプログラムで、途中経過や結果のダイアログを出すことにした。
  • 同期元のトップディレクトリは一つでないので、それを元に同期先のトップディレクトリを設定してはいけない。→ 同期するプログラムで、同期先のトップを指定するようにし、トップ下のディレクトリは曲のタグ(アーティスト、アルバム)から生成するようにした。
  • どうやって同期先のトップディレクトリの指定をするか。→ GUIプログラムzenityでファイル選択ダイアログを出すことにした。
  • 元のファイルにトラックの最大値(REPLAYGAIN_TRACK_PEAKなど)がある場合は、改めて最大値を求めずにそれを使えばいい。
  • SOXでは日本語のMP3のタグ(アーティストなど)が文字化けする。→ ffmpegを使うことにした。
  • カーナビでアルバムアートを表示させたいので、MP3にアルバムアートを埋め込む必要がある(埋め込まないと、カーナビには表示されないため)。→ オリジナルのディレクトリからアルバムアート(cover.jpgなど)を探して、縮小してMP3に埋め込むことにした。
  • 処理(特にMP3へのエンコード処理)が遅い。→ とりあえず、ffmpegを2スレッドで並列化した(-threads 2)。本当はファイルごとに別プロセスで処理するといいのだろうが、結構面倒だ。→ その後、MP3には-threadsは効果がないことが分かったので、止めた。(10/28 5:53)

以下、実装時の細かい話を書く。

  • 同期先のファイル名は、同期元のファイル名のsuffix(拡張子)を"mp3"に置換して生成している。が、今思えば、タグから作っても良かったかも知れないし、通し番号でも良かったかも知れない。
  • ファイル名によっては、特殊な文字("や()など)が入っているため、外部コマンドに渡す際にファイル名をクォートする処理が必要だった。
  • 同様に、USBメモリはVFATなので、同期先のファイル名中の、VFATで使えない文字は"_"に変換するようにした。
  • 再生ゲインタグの抽出にはexiftoolを使った(-s2)。ffmpegでも可能だが、exiftoolの方が速かったためである。
  • FLACとMP3では再生ゲインタグの名前や記法が異なるので(下の例を参照)、両方に対応した。
    • FLAC: REPLAYGAIN_TRACK_PEAK: 0.921652
    • MP3: User Defined Text               : (replaygain_track_peak) 0.921652
  • 同期元に再生ゲインタグがない場合に最大音量を求めるのには、ffmpegを使った(-af volumedetect)。この場合は、エンコードの前に追加処理として行うため、処理が遅くなる。
  • ffmpegでの音量の正規化は、loudnormフィルタがちゃんとしているようだが、車で聴くのにそこまでする必要はないため、MusicBee同様、volumeフィルタで最大値を設定することにした(例: volume=-0.5dB)。← volumeに指定するのは絶対値でなく増減なので、最大音量近くを意図して-0.5dBを指定するのは誤っている。(10/29 18:29)
  • 再生ゲインの計算は単純な最大値ではないようなので、元のファイルに再生ゲインタグがない場合は、警告を出して、正規化を諦めるようにした。ffmpegでloudnormフィルタを使えば求められるが、それにしてもGMBで再生ゲインを求めたファイルと音量差ができると考えたからだ。そもそも、元のファイルはすべて再生ゲインタグを入れている前提なので、問題は生じないはずである。(10/29 18:29)
  • 不要なタグの削除は、エンコード時にffmpegで行った。(例: -metadata REPLAYGAIN_ALBUM_GAIN=)
  • 少しでも小さいファイルサイズで音質を良くするため、MP3は可変ビットレート(例: -q:a 3)でエンコードした。ちょっと聴いた限りでは、音質に問題はなかった。品質に3を指定した場合、平均ビットレートは約180kbps程度になり、ファイルサイズはFLACの1/5程度になった。
  • アルバムアートの埋め込みも、エンコード時にffmpegで行った。
  • アルバムアートの埋め込み前に、convertコマンドを使って256x256画素にしている。元のサイズがそれより小さい場合は処理しないようにしたいが、今はいつも処理している。→ その後、縮小のみをする方法が見つかったので、元のサイズが最小サイズより小さい場合は、拡大しないようにした。(例: -resize "256x256>") (10/29 18:35)
  • 上述のように、ffmpegでさまざまな処理を一気にしているため、ffmpegを起動するコマンドラインがかなり長くなった。
  • 同期先にあって同期元にないファイルの削除は以下のようにしている。
    1. 同期の前に、同期先のファイル一覧を取得する。
    2. 同期中に、同期またはスキップしたファイル一覧を保存しておく。
    3. 同期終了後に2つの一覧を比較し、同期したファイル一覧にないファイルを削除する。
  • 削除機能の基本動作は問題なかったが、ファイル数が多い場合に使用メモリ量や負荷が高くなる可能性があるので、要注意だ。

実装後、この同期プログラムで作成したMP3ファイルがカーナビで再生でき、アルバムアートも表示されることを確認した。

以下、操作時の画面キャプチャを示す。

最後に、同期機能に関して以下のような残件はあるが、現在のところ、GMBに大きな問題はなく、この先にもあるとは思えないので、MusicBeeからGMBへの移行は完了したと考えてよいだろう。

  1. 同期プログラムのオプションを実装する。
  2. exportプラグインの設定画面で同期のオプションを設定できるようにする。
  3. 細かい調整・高速化
  4. 全体的な動作確認
  5. 詳細な動作確認
  6. 実際に使っているUSBメモリにフル同期(全曲を同期)してみる。
    1. 所要時間を測る。
    2. 使用メモリ量を測る。
    3. カーナビで再生を確認する。

上の1, 2を実装し、同期中のファイル名を表示し、同期結果に実行ログも表示できるようにした。(10/27 21:46)

3の高速化について、ffmpegのマルチスレッド指定(-threads)は効果がなかったので、複数ファイルのエンコードを同時に実行できるようにしてみた。すると、曲にもよるが、6ファイル(プロセス)を同時に処理した場合には、約3-4倍高速になることが分かったので、6プロセスでの並行処理を採用することにした。以下に、いくつかの測定結果を示す。(CPU=Core i7-2600, 転送元=HDD, 転送先=SSD)

・ポップス 28曲 (Blondie, FLAC, 125MB)

  • 1プロセス: 112s → 4s/曲, 1.1MB/s
  • 2プロセス: 65s → 2.3s/曲, 1.9MB/s
  • 4プロセス: 39s → 1.4s/曲, 3.2MB/s: 1プロセスの約3倍速
  • 6プロセス: 29s → 1s/曲, 4.3MB/s: 1プロセスの3.9倍速

・クラシック 6曲 (ブロンフマン, FLAC, 313MB)

  • 1プロセス: 81s → 13.5s/曲, 3.9MB/s
  • 2プロセス: 49s → 8.2s曲, 6.3MB/s
  • 4プロセス: 34s → 5.7s/曲, 9.2MB/s: 1プロセスの2.4倍速
  • 6プロセス: 26s → 4.3s/曲, 12MB/s : 1プロセスの3倍速

なお、6プロセス同時の場合、CPU使用率は75%程度まで上昇した。

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88_2016-10-28_21-42-49-1

ここまでやって大分良くなったし、プログラムもなかなか本格的になったのだが、まだまだ9.75合目程度で、完成までにはもう少しある。そして、ここからは結構辛い。。。(10/28 22:25)

 

PS. これが完成して気が向いたら、他の改良・修正(主なものは以下のとおり)と一緒に作者に送ってみようかと思っている。(僕からすれば、)寄付よりはいいかなと思う。

  • 再生ゲインモード(off/album/track)の切り替えを(設定画面でなく)メニューで行えるようにした。(頻繁に使うため)
  • 「今の曲が終わったら停止のon/off」を行う内部コマンドを追加した。(リモコンで行いたいため)
  • 設定の"Remember playing position between sessions"をonにしている時、次回起動時に再生を開始しないようにした。(文面通りにした)
  • Queueの"Stop after this song"がonの時、再生終了後、次の曲に進む(ただし、停止のまま)ようにした。(次回の再生時に次の曲に進める手間を省くため。MusicBeeに合わせた)

そして、オープンソースプログラムは自分でいろいろ改良・修正できるので、やっぱりすごくいいと思う。そして、GMBはプログラマーにとって、最高の音楽プレーヤーだと思う。

(23:30 加筆・修正; 10/27 5:35 少し加筆;10/28 5:53 ffmpegの-threadsについて追記;10/28 22:25 並列処理について追記; 10/29 7:24 わずかに修正; 10/29 18:35 再生ゲインタグがない場合とアルバムアートのサイズが小さい場合について更新)

  •   0
  •   1

疲れたので、この週末はプログラミングは休むつもりで居たが、Linux移行作業は進めていた。しかも、気付いたら、散歩や掃除などの予定していたことを何もせずに、プログラミングしていて日が暮れていたという体たらくである。

が、そのおかげで、MusicBee(MB)からgmusicbrowser(GMB)への移行がかなり捗った。MB関連で残っていたのは、以下である。

  1. 音楽ファイルの管理の移行
  2. デバイスとの同期
  3. GMBのリモコン(I/Oデータ USB-IRUNIT2)対応

それらのうち、1と3ができた。

まず、音楽ファイルの管理の移行での問題点は以下だった。

  1. GMBはWAVとWMAをサポートしていないので、一覧に出ず、再生できない。
  2. MBのカスタムタグのGMBへの移行。

WAVとWMAの件は、基本的には単純で、それらをFLACに変換すれば良い。なお、それら以外にGMBのサポートしていないフォーマットは使っていなかった。

手順としては、MBの自動プレイリストでWAVとWMAのファイルを抽出(検索)して、それらに対してフォーマット変換を行った。ちょっと面倒だったのは、変換したファイルをサブディレクトリに入れたかったのだができなかったことと、WAVには再生ゲイン(その曲の音量の最大値のようなもの。いろいろな曲の音量を揃える時に使う)の値が入っていないので、変換後に計算する必要があったこと程度だ。

サブディレクトリについては、おそらく、MBの変数を使えばできたのだろうが、入力画面には出ず、どうせヘルプドキュメントもなく、調べるのが面倒だったので、同じディレクトリで我慢した。WAVなどが同じディレクトリにあっても、GMBはそれらを認識しないのだから、自分でディレクトリを見ない限り、問題はない。

その量は膨大で、約8600個(約220GB)もあり、全ファイル数の半分以上だった。変換には4スレッドで約5時間掛かった。FLACに変換後のサイズは約70GBだった。なかなか気持ちよく圧縮された。もちろん、HDDにはまだ余裕があるし、何かの間違いがあるかも知れないので、元のファイルは消していない。なお、WMA(ロスレス)からFLACへの変換がロスレスなのかちょっと心配はあるが、どっちにしても聞き分けられないので、問題はない。この処理によって、念願だった、GMBでビートルズの曲が聴けるようになった。

なお、再生ゲインの計算は約2000ファイルに対して実施し、約30分掛かった。

次に、カスタムタグの移行を行った。カスタムタグは、MBだけの特別なタグで、基本的にはファイルに書き込まれていないので、ファイルを移しただけではGMBは認識しない。また、ファイルに書き込んでいるカスタムタグでも、GMBは「やれば表示できる」程度で検索やソートには使えないので、何らかの対応をする必要がある。それにはいくつかの方法があるのだが、以下のようにした。

  • 値が1/0のタグ (例: 「この曲を無視する」)
    1. 自動プレイリストで、対象の(値が1の)ファイルを検索する。
    2. ファイル一覧を通常のプレイリストにエクスポートする。
    3. それをGMBでインポートして、その中の曲に対して、GMBのラベルを設定する。
  • 値が任意のタグ (例: 購入日)
    • いい解決策がなかったのだが、幸い、音楽ファイルではタグも対象のファイル数も少なかったので、手で、一般的なタグであるコメントに追記した。
    • ビデオファイルにはいくつかのカスタムタグがある(例: ソース)のだが、GMBはビデオが扱えないため、別アプリで対応する必要があるので保留にした。

なお、プレイリストは、MBの設定では"M3U(#EXT)"形式で、絶対パス・UNIX形式でエクスポートし、エディタでパスを修正した。M3U(#EXT)形式でないと、GMBではインポートできない。ファイルの先頭に"#EXTM3U"が必要なようだ。なお、UNIX形式にしても、パスの先頭にドライブ名が残るなど、そのままでは使えなかった。相対パスにしても、ドライブが複数あるためにパスが間抜けになっていて、便利ではなかった。この辺りは、もう少し何とかすべきだろうが、Windowsでしか動かないプログラムに求めるのは酷だろう。

それから、余計なファイルを削除したり無効化したり、タイトルなどが文字化けしたファイルに対応した後、ファイルの過不足のチェックをした。概ね問題はなかったのだが、移行前後のファイル数に若干違いがあった。全体としては、GMBの方が数十個多かった。どうしてかは良く分からないが、誤差の範囲だろうし、少ないよりはいいだろうw いや、実際には、デジタルなので誤差はないはずなのだが、まあ、聴いた時におかしければ対処するから問題ない。そのためにも、オリジナルのファイルは残しておくのだ。

あと、どういう訳かアルバムアートが出ない曲が結構あったので、手で登録したり、なぜか再生ゲインが入ってない曲があったので再度計算したり、同じアーティストでも微妙に異なる綴り(例: "the"と"The")のせいで別人に扱われているのを、手で修正したりした。

リモコン対応は比較的楽だったが、問題点は以下だった。

  • どうやってリモコンとGMBをつなぐか。
  • GMBにないコマンド(今の曲が終わったら停止のon/off)をどうするか。

リモコンとGMBの接続には、以下の方法がある。

  • キーボードのシミュレーション (MBでやっていた方式)
  • MPRIS2
  • DBus

キーボードのシミュレーションはいろいろと面倒なので、却下した。例えば、キーを送る前にGMBのウインドウを探したり、ウインドウマネージャのホットキーを無効にしたりする必要がある。

次に、MPRIS2とDBusはどちらでもいいのだが、GMBにないコマンドを追加する可能性があったので、メッセージの仕様に自由がある(仕様がない)DBusにした。DBusだと、GMBのRunCommandコマンドでほとんど何でもできる一方、MPRIS2は(下位ではDBusを使っているのだが)、仕様にあるコマンドが少なくてできることが余りなく、わざわざ規格を決めたメリットがあるのか、ちょっと疑問だ。

リモコンのキーを読んでDBusで送信するプログラムは、以前試しに作ったものがあったので、改良程度でできた。ただ、思わぬ問題が起こった。リモコンのキーを押すと、ウインドウマネージャが終了するらしく、ログアウトしてしまうのだ。

この原因は良く分からないのだが、おそらく、リモコンのキーがキーとしては滅茶苦茶(文字でなく、バイナリデータ)なので、ウインドウマネージャが誤動作して落ちるのではないかと思う。検索したら、運良く対処方法が見つかった。xinputというコマンドで、リモコンを除外すれば良い。除外するには、リモコンのデバイス名などを調べる必要があった(USB接続なので、デバイス名が変わる可能性があるため)ので、ちょっと手間が掛かった。udevadmやlsusbといったコマンドを使った。

(2016/10/29 7:06 追記) しばらく使っていると、xinputでの除外設定が解除されてしまうようなので(以前あった、xmodmapで変更したキー配置が戻るのと同様か?)、XOrg (X Window Systemのサーバー)の設定にリモコンを無視する設定を追加して、恒久的な対応をした。以下に設定例を示す。

/usr/share/X11/xorg.conf.d/10-evdev.confに追加:
 Section "InputClass"
   Identifier "bad device"
   MatchProduct "I-O DATA DEVICE,INC. USB-IRUNIT2"
   Option "ignore" "on"
 EndSection

なお、リモコンはudevというプログラムでLinuxに自動的に登録されるのだが、デフォルトでは管理者(root)以外はアクセスできない。そのため、リモコンを認識した後で、他人もアクセスできるようにする処理をudevに設定を追加した。

それからリモコン関係の機能追加をした。まず、面倒だと思っていた「今の曲が終わったら停止のon/off」コマンドをGMBに追加した。「今の曲が終わった後の動作の指定」のコマンドはあったので、それのコピーと修正(現在の状態を元に、新しい状態を設定する)でできた。更に、Windowsでやっていた、リモコンの電源ボタンで休止する機能も付けて、リモコンは完璧になった。

今気になっているのは、あるプレイリストを再生中に別の曲が入ってしまう問題である。例えば、ポップスのプレイリストを聴いているのに、途中でクラシックが入ることがあった。やっぱり原因はわからないのだが、GMBの設定で、「現在の楽曲は常にプレイリストにある」と「最新の楽曲」をoffにしてみたら、今のところ再発していない。それにしても、どちらも意味不明な訳ではあるが、自分でやる気はしないので、文句は言わない。ただ、設定がなくて英語にできないのが、ちょっと不便だ。Linuxのデスクトップアプリは、OSの言語設定に合わせるのが流儀なのかも知れない。

もう一つ気になっているのは、戻れる曲数に限度があることだ。1曲程度しか戻れない。シャフルしているせいだろうか。まあ、戻ることは滅多にないので、問題ではない。

という訳で、残ったのはデバイス(USBメモリ)との同期だけのはずだ。それについては、方法を考えた。基本的には、soxというオーディオファイル処理プログラムと、デバイスとホスト間のファイル同期処理(デバイスにないファイルだけを転送する)を併用すればできそうだ。

もちろん、ファイル同期プログラムは世の中にいくつもあるのだが、以下の特別な処理が必要なので、使えない。

  • デバイスに転送する時に、MP3でないものをMP3に変換する。
  • 再生ゲインを恒久化する(タグの数値でなく、データ自体を増幅する)。
  • 再生ゲインのタグを除去する。

ファイルの同期はデジカメでもやったので、それほど難しくないだろうから、あとは実装するだけだ。

そして今は、MusicBeeでなくgmusicbrowserで音楽を掛けている。VirtualBoxとその上のWindows 7を動かさずに済むようになったおかげで、メモリ使用率は12%程度に減り、平常時のCPU負荷も2%程度に減った。全くいいことづくめだ。更に、GMBに再生ゲインの切り替えメニューを追加できれば、差し当たっては言うことは何もない。最高の音楽プレーヤーのできあがりだ。

その後やる気が出て、GMBに再生ゲインの切り替えメニューを追加できた。先日試しに作ったコードがそのまま使えた。今は設定するだけで、現在の状態(off/アルバム/トラック)は表示できないが、結構便利になった。あと、ダウンロードしたソースだとなぜか英語表記になっていて、更に都合がいい。(23:44)

20161023-gmb-add-rg-1

その後、ちょっと苦労したが、なんとか再生ゲインの切り替えメニューに現在の状態を表示できた。まったくPerlは判じ物で、どうなっているのか理解できないが、便利なのは認める。(10/25 4:15)

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88_2016-10-25_04-13-07-1

  •   0
  •   0

(ここのところ、しばらく寝不足続きで疲れたので、この週末はLinux移行関連のプログラミングは休み(のつもり))

先日からWindowsの音楽プレーヤーMusicBeeのLinuxでの代替に、gmusicbrowser(GMB)を試している。絶対的な機能の豊富さではMusicBeeに敵わないのだが、使い込むにつれ、なかなか侮れない奴だという気がしてきた。

一番のいいところは、MusicBeeと違ってオープンソースだという点だ。プログラムは比較的コンパクトだから、気に入らないところはその箇所を探して自分で改良できる。Perlで書かれているのでビルドが不要だし、インストールすら不要だから、手軽に試せる。実は僕はPerlは苦手で(何といっても読みにくい!)、今までほとんど使っていなかったのだが、昨日、初心者向けの解説ページを参考に(実は、普通の変数と配列に区別があることすら知らなかった)リプレイゲインの切り替えメニューを付けてみたら、何とかできてしまった(もちろん、それをちゃんと使える物にするには、結構手間が掛かる)。同様に、ウインドウのレイアウトもテキストファイルで書かれているので、自由に変更できる。プラグインももちろんPerlで作れる。だから、WMAやWAV対応も自分でできそうな気がしてきた(面倒だけど)。

繰り返しになるが、オープンソースでコンパクトだから、仮に開発が終わってしまっても、ちょっとした不具合は自分で直せそうだし、機能追加もできそうだ。ドキュメントは余りないが、ソースを見れば何とかなる。とにかく、要望でもバグでもいちいち作者に「お願い」するのは面倒なのだ! しかも、大抵は却下されるし、MusicBeeの作者とは指向も違っていたから、最後は頼む気も起こらなかった。

それから、便利な機能が意外に多いのにも感心した。デスクトップウィジェット(プラグイン)なんて、邪魔なだけで使わないだろうと思っていたのだが、試してみたら意外にいい。MusicBeeのコンパクトプレーヤーやミニプレーヤーの代わりになりそうだ。(MPRISに対応しているから、命令を送信するすプログラムを作れば)リモコンにも容易に対応可能だし、(実際に便利かは不明だが)任意のCD取り込みプログラムを起動することもできる。

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88_2016-10-22_08-52-15-2-1

gmusicbrowserのメインウインドウ(左)とデスクトップウィジェット(右上)

 

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88_2016-10-22_08-44-07-1

gmusicbrowserのデスクトップウィジェット

要は、僕ら(Unix使い)にとっての「普通」から逸れずに作られているということなのだろう。だから素直に使えるし改造もできる。Windowsのようにイライラすることはまずない。もちろん、機能不足やバグや気に入らない点はあるのだが、自分で直せる可能性があるのは、欠点を隠して余りある。こんなに魅力的なソフトが普及していないなんて、ちょっともったいないと思った。まあ、そのメリットが活かせるのはUnixのプログラマーだけなのだろうが。

(10:49 加筆・修正、16:06 わずかに修正)

PS. 今気付いたが、GMBには、音量メーターだのスペアナだのビジュアリゼーションなどの、派手だけど役に立たない要素がない。せいぜいあるのは、上に書いたデスクトップウィジェットや、通知の表示程度で、それだってオプションだ。それから、もちろんのことだが、最大化して起動したり、ウインドウを閉じても終わらない(設定で可能)ということはない。それらは、僕にとって、GMBの好感度を高める一因になっているようだ。(13:35)

PS2. 気が向いたので、ちょっと話は逸れるが、Unixの文化なり思想についてちょっと書く。僕の経験や理解では、Unixでは「派手は糞」だ。できるだけ「静かに」すべきなのだ。まあ、昔はGUIなんてなかったから、そうするのは、(コマンドラインの)プログラムの出力程度だったが、とにかく、無駄なメッセージはだらだら出さないのが常識だった。極端な(でも、ごく普通の)例では、処理が成功しても、(問題がない限り)何も出さない。もちろん、うるさく出すように指定されれば(普通は"-v"(verbose)とか"-d"(debug)などのオプション)、言われたとおりいくらでも出す。どっかの窓とか林檎の会社の作法とは全く異なる。

あとは、「普通とか常識を守る」だ。とにかく、外側(例: 起動方法、表示・データ入出力の仕方、設定ファイルの書式)に関しては、作法に従ってプログラムを作る。そうじゃないのは、糞(=イケてない・ダサい)だ。これもどこぞの会社とは全く逆だ。

ではなぜ、一見アナーキーにも思えるプログラマーたちにそんな堅苦しいことを押し付けているかというと、ひたすら作業効率を上げるためだ(と僕は理解している)。ユニーク(独自)なのは、プログラムの内容だけで十分で、見てくれなんて無意味だし、使い勝手は普通が一番。そうしておけば、例えそのプログラムを使ったことがなくたって使えるし(覚えておく必要があるのは、どんなプログラムにも、簡単な使い方とかオプション一覧を出す"-h"(help)というオプションがあることと、manコマンドでマニュアルを表示することだけ)、他のプログラムと組み合わせることで、いくらでも機能を拡張できるのだ。

時は流れて、LinuxはUnixの一種といえども、段々そういう思想が薄れて来ている気がするのが、ちょっと寂しい。(16:45)

  •   0
  •   0

いろいろな苦労と発見の末、デジカメからの画像取り込みプログラム(スクリプト)の初版ができた。いろいろ改良したいことはあるが、とりあえず、落とせないポイントをすべておさえて、ちゃんとデジカメやスマフォから取り込めるようになったので、一安心だ。それについて詳しく書きたいのだが、風邪のせいかだるくなって来たので、その前に、画像より100倍重要な音楽プレーヤーについて、再び書くことにする。

以前も探したのだが、もしかして見落としがあるかも知れないと思って、さっきまで散々探していた。が、やっぱり、(僕にとって)MusicBeeを置き換えられるものはLinuxにはない。もちろん、どれも曲は再生できるし、それなりの画面は表示されるのだが、必要な機能(例: 自動プレイリストやギャップレス再生)が抜けていたり、あっても貧弱だったり、使いにくかったりするのだ。

試したうちではClementineが筋がいいように感じたのだが、なぜか、再生と表示がおかしくなってしまったので却下した。AmaroKは次点とは言えども論外で、それ以外は論外未満だ。

個々のアプリについて書くと長くなるから、以下に、全体を通して受け入れ難かったポイントを書く。

  • 音飛びする。
  • 再生がおかしい(リピートでないのに、何度も同じ曲が掛かることがある。表示と再生の曲が違う)。
  • 曲間にギャップができる。
  • 曲が認識されない、認識される曲が少ない。
  • 曲の順番が滅茶苦茶に表示される。
  • 自動プレイリスト機能がないか貧弱。
  • 通知の表示や音などが鬱陶しい。
  • ウインドウを閉じても終わらない。終わる設定がない。
  • 操作が直感的でない。マウスのボタン操作が微妙で、誤操作を誘発する。
  • 設定ができないか、項目が貧弱。
  • 表示がしょぼい。デザインの趣味が悪い。
  • (英語圏のアプリなのに)アーティスト名やタイトルの"The"が無視されずにソートされる(例: ビートルズがBでなくTのところに出る)。

無いものは仕方ないので、当面はMusicBeeを使うが、ClementineやAmarocKを試した時に、「やっぱり、ネイティブは便利でいい」感じがしたので、諦めずに探し続けよう。

それにしても、Linux陣営は、各自が好き勝手に作ってないで、共同でいいものを作ったほうが絶対に得策だと思うのだが、きっと、みんな「俺様」だから無理なんだろうな。KDEだのQtだのGnomeだの、各種流派の主張や長所はあるんだろうけど、そんなことより、数は少なくても質の高いアプリを作った方がいいと思う。「『ちょっと作ってみた』から出す」時代はもう終わっているのだ。こんな調子では、LinuxがWindowsに勝つ日は来ないかも知れない。今はいいチャンスだと思うのに。。。

その後もしつこく検索していたら、いくつか追加候補が見つかった。その中で良さそうなものを試したが、どれも肝心な点(特に、音飛びとギャップレス再生)が駄目だった。まともに動かないものもあった。それから、何となく感じるものがあって、試すまでもないと思っていたgmusicbrowserをダメモトで試したら、意外に良かった。MusicBeeの機能が全部ある訳ではないし、いろいろ荒削りではあるが、基本機能がちゃんとしていて可能性があるので、いろいろ試している。(10/18 6:37)

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88_2016-10-18_05-51-23-1-2

約1日間、gmusicbrowser(略してGMB。MusicBeeはMBなので、不思議なつながりがある)で試しに音楽を聴いているのだが、聴くだけなら全く問題はなかった。シャフルで聴いていたので、ギャップレス再生については分からないが、音飛びは全く感じなかった。それで、GMBに不足している以下の重要な機能が何とかなれば、移ろうと思っている。

  1. CDの取り込み
  2. CDDBで曲情報やアルバムアートを取得して、取り込んだ曲に付ける。
  3. 取り込んだ曲の再生ゲイン(音量)の計算
  4. 特定のプレイリストの曲を、デバイス(USBメモリ)に同期する。
  5. 同期と同時に、MP3へのフォーマット変換と音量の正規化を行う。

ところが、1-3について随分しつこく探したのだが、これというものがなかった。取り込み速度が遅過ぎたり、アルバムアートが取れなかったり、再生ゲインができなかったりというのばかりだ。

でも、よく考えると、僕はもうCDから脱却したので、頻繁に取り込むことはないだろうから、それらの機能はあまり重要ではなく、取り込む時だけMusicBeeを使えばいいのかも知れない。もう少し考えよう。

あと、4と5ができるプログラムはまだ探していないが、なければ自分で作ろうと思っている。

それから、GMBはWindows系のフォーマット(WMAとWAV)に対応していないのだが、それについては、FLACに変換すれば問題ないと思っている。それらは全部で8600曲、210GBくらいあるのだが、待てば終わるだろうから大きな問題ではない。(10/20 0:06)

  •   2
  •   2