Posts tagged ‘Spotify API track info’

濁音に苦労した話」の続きである。元の稿に加筆したように、SpotifyのAPIで取れる曲情報でもcombining charactersが使われている(= 本体と(半)濁点が分離している)場合があることが分かった。

確かに、APIとそれ以外で別の情報を格納して別々に出すのは無駄だし、合理的でない。

表示は文字列が綺麗に出ればいいが、APIで取った曲情報は自作の音楽再生履歴記録システム MlhiのDBに格納しているので、今ひとつ良くない。というのは、今はまだ完全でない(イマイチな)ので余り使っていないが、将来は自分が聴いた曲をフレキシブルに(要するに、手軽に思ったとおりに)検索できるようにする予定で、その時に、combining charactersの濁音や半濁音を含む曲が出て来ない可能性があるからだ。

そういえば、以前、なぜか検索出来ない曲があったが、それだったのかも知れない。

(1/27 14:55) ちなみに、DBを調べたところ、曲情報(タイトル、アーティスト名、アルバム名、アルバムアーティスト)のいずれかにcombining characters(濁音、半濁音)が使われているものは、延べ27曲だった。DB全体は約1万曲なので、割合は約0.3%未満と少ない。ただ、僕は洋楽やクラシック音楽を聴くことが多く、曲情報が英語のものが多いから、日本の曲での割合は数%くらいではないか。

これを解決するには、以下の方法が考えられる。

  1. DBに正規化した曲情報を格納する。
    • 今までにDBに格納した内容は正規化する。
  2. 検索時に、検索文字列とDBの内容を正規化して比較する。
    • DBには、取得した情報をそのまま格納する。
  3. × 検索時に、検索文字列として、正規化したものとそうでないものを両方指定する。
    • 正規化された文字を逆変換(分離)するのが面倒そうだが、uconvでできそうだ(本当?)。
    • 曲情報に正規化されている文字と そうでないものが混ざっているので、検索パターンが多くなり過ぎる。

Bが筋が良さそうだが、結構面倒そうだ(そもそも、DBに使っているSQLiteはUnicodeのこういう処理に対応しているのだろうか??※)。一方、前者は やればできそうなので、そうすることにした。

sqlean: All the missing SQLite functionsというソフトがあり、それは限定的なUnicode処理機能(upper/lower, like, unaccent)を持つので、SQLite自体は対応していないようだ。

ここで問題になったことがある。: 前の稿に書いたように、uconvで正規化すると、かな に正規化漏れが起こるのと、漢字の旧字体が新字体に変換されてしまう。前者は自作の処理(テーブル変換)で対応できているが、後者に対応するには、日本語の文字(漢字)を正規化しない必要がある。※

※幸い、日本語の漢字には濁点のような着脱可能な要素が なさそうなので、正規化対象から除外するだけで良い。ただ、例えば「辶」(しんにょう)のように、場合によって個数が違う点のような要素はある(→ 興味深い読み物があった)。が、それは純粋に文字の形の問題と思われ、意味や読みは変わらないので別件としたい。

他には、たまに、富と冨ののような着脱可能っぽい点はあるが、それらは同じ文字らしいし、相互に入れ替えて意味を変えることはないので(しんにょうと同様に)ヨシ、だ。

そもそも、今までに、(遊びをのぞいてw)漢字に かな のように点や丸を付けて意味などを変えたことはないし、そういうことを教わったこともないから、それで良さそうだ。

UnicodeやICUに不慣れなので、日本語を除外して正規化することができるのか分からなかったが、いろいろ調べたら できそうなことが分かった。

Unicodeにはscriptという概念があり、Latin文字(いわゆる英字)、カタカナ、ひらがな、漢字※のようなカテゴリ(Unicodeにはcategoryという概念があるが、それではない)を示すことができる。それを使って、uconvの正規化ルール(日本語除外フィルタのようなもの)を指定すればできそうだ。

※実際には、Unicodeの資料では漢字は"Han"と書かれており、おそらく中・日・韓の漢字を一緒にしたもの(≒ CJK Unified Ideographs)を差していると理解している。

具体的には、以下のコマンドで日本語(正確には、漢字、ひらがな、カタカナ)以外の文字を正規化できるはずだ。

uconv -f utf-8 -t utf-8 -x '::[ [:^Katakana:] & [:^Hiragana:] & [:^Han:] ]; ::nfc;'

処理内容は、-xの最初のルール(最初の ; まで)でカタカナ、ひらがな、漢字を除外し、残ったものに正規化(NFC)を実行する("::nfc")。

実は、上の処理には欠陥がある。おそらく韓国語(ハングル文字)はcombining charactersをバリバリ使っているはずだが、それも除外されるのではないか(ハングル文字がHanに含まれているとした場合: 実際には、scriptには"Hangul"というのがあるから、ハングル文字はHanには含まれていなさそうだ)。

が、僕の好みからして、曲名などが韓国語で書かれた曲は聴かないだろうから、とりあえずは良しとする。あと、中国語には簡体字も繁体字もあるし(それは関係ないのかも知れないが)、どうなのか想像も付かないw

上の処理は、以下のテストパターンで うまく行った。

  • パ が 神: 正規化されずに出た。
  • (A + U+0304: Combining Macron): Ā (U+0100: Latin Capital Letter A with Macron)になった。※

を使ったのは たまたま最初に できた(macronなるものが付いた単体のĀがあった)からである。この文字がどういう意味なのか全く分からないし、実際に使われるのかも知らない。

と、出来そうになって気分がいいところで、「残りはあとで」にしたw

 

参考にしたページは以下である。

uconvに指定するルールの書き方は最初と2番目(特に後者)が参考になった。最後に残った謎(どうやって日本語を除外するか? そうするパターンをどうやって書くか?)を解く鍵になったのは、最後のUnicode Regular Expressionsだった。

それについては最初の2つを丹念に読めば分かったのだろうが、余りにも知らない概念が多いうえに機能が多過ぎてチンプンカンプンだった(→ 読む気が起こらなかった)。一方で、-Regular Expressionsは馴染みの正規表現の話なので良く分かった。※ 更に、Hanが かな を含むという思い違いに気付かせてくれて、上のルールが出来た。

※実際、このページを見付けた切っ掛けは、ICU(uconv)のルールが全然思ったように動かないので諦めて、grepで正規表現で除外しようと思い、「『漢字全部』の うまい指定」があればと思って"Unicode regexp"で検索したことだ。

ちなみに、grepは-PオプションでUnicodeに対応した動作(PCRE)になる。

 

PS. Unicode(ICU)の処理には、カタカナ→ローマ字みたいなものがあって(→ Script Transliterationの表の「キャンパス: kyanpasu」)、「誰が使うの?」と思う。ちゃんと動くのならいいが、正規化みたいに中途半端だったら実装するだけ無駄だし、ほとんどの開発者は知らないので、使われない気が・・・ (いやいや、僕が無知なだけだよね)

PS2. こういう細かい話は嫌いだけど好きだ。: 実装や確認は面倒でやりたくないけど、豆知識的に調べるのは好きだ。発展したのがタモリかw

  •  0
  •  0
Keys: , , ,

夏が来た!
夏が来た!

上下の違いが分かる方は居るだろうか?: 同じに見えるけど違う。ブラウザ(かなり古いもの?)やフォントによっては、分かるかも知れない。

答えは、題にあるように、濁音「が」の造りである。今まで知らなかったのだが、Unicodeのひらがな・カタカナは、濁音や半濁音を半角カナ的に 本体+(半)濁点 のように分離して記述し、なおかつ くっつけて一文字のように表示することができる。Combining charactersとかいって、日本語以外でも そういう処理ができるとのことだ。

上の例の「が」は、最初の行はcombining charactersで 「か」+「”」(← 見にくいのでダブルクォートにした) で、2番目は普通に書いてある。全く区別できない。※ 良く出来過ぎていることに、マウスでコピーする時は1文字として扱われるし、等幅フォントのエディタやターミナル(コンソール)でも くっついた状態で表示される。

※おそらく、フォントの字体としては、本体に濁点を加えたものが濁音の文字になっており、combining charactersの濁点は それを再現するような位置に表示されるようにできているのだろう。ただ、この方式ではすべての濁音で濁点が同じ位置に置かれるが、そこはフォントの 描き方でうまく合わせたのだろうか。

↑ ちょっと確かめたら、そう単純なことではないようだ。というのは、下の左図のように、「ヾ」と「ヷ」では濁点の大きさや形が明らかに違うからだ。ということは、フォントに濁点を付けるための情報があるのか、表示する時に、combining charactersの濁点が付く場合は自動で濁音の字に変換されるのか。見たところ、濁点の形が微妙に違う気がする(特に、線の太さ)ので前者なのかも知れないが、本当に そこまでやっているのかという気はする。どちらにしたって、「余計なところに力を入れ過ぎていないか?」と言いたくなる。 (1/22 5:18)

その後、文字に色を付けて濁点を重ねて比べたところ(下の右図)、同じ文字の濁点の形状は、combining charactersでもそうでなくても同様だが、異なる文字同士では異なっている(この例では、「ヾ」の濁点は「ヷ」のより大きい)ことが分かった。 (1/22 14:19)

僕が見付けた唯一の識別方法は、エディタ(ペーストする)で濁点側から1文字削除することだ。Combining charactersの場合は濁点だけが消えるが、そうでない場合は全部消える。

そして、今回の切っ掛けに関係することは、どういう訳か、Spotifyで出る曲情報(タイトル、アーティスト名、アルバム名)※の中に、本体と濁点・半濁点が分離して記述されたものがあるのだ。全部ではなく、たまにあるのが不思議だ。特定のレーベルという訳でもない気がするが、老舗(例: ビクター、ソニー)や昔の曲に多い気がしている(と書きつつ、1990年代の(一番下の図)もあるので関係ないかも)。

想像だが、レーベルが曲情報を電子形態に登録する際、打ち込む人の癖などで分離してしまったか、それ以前の半角カナも使うシステム(そういうものがあったかは不明)から変換した時に、濁点・半濁点を統合しないままだったのではないか。

※詳しく確認していないのだが、同じSpotifyでも、アプリから取れる情報(Dbusのイベントで取れるもの)とAPIで取れる情報の記述が異なるようだ。前者は分離していることがあるが、後者はそうではない。 ← どちらも同じように分離している場合があることが分かった。例: 飯島真理 「palette(パレット)」(2007)の「パ」。 (1/25 16:31)

それで何も問題が起こらなければ気付かずに過ごせたのだが、自作のSpotifyのミニプレーヤーMinispが駄目だった(これが発端である)。次の左図のように、combining charactersである「が」のあとに空白が出来て、半角カナ的な見苦しさになってしまっている。今見ると、本体(か)と濁点の間隔も少し広い。本来は右側のようになるべきである。

どうやら、表示に使っているTcl/Tk(wish)がcombining charactersに対応していないようだ(調べた限りではTcl/Tkのページの"Unicode combining characters"の項にはコメントだか状況説明が書いてあるだけのようだし、 そういう設定や属性などがなかったので非対応なのではないか)。

これを何とかしようと調べたら、Rubyのソフト(unicode_utils)の紹介が出て来たものの、インストールするのが面倒なので試行錯誤したら、既存のプログラム iconvでは駄目だったものの、既に入っているパッケージ icu-devtoolsのuconvでできることが分かった。

Combining charactersを まとめる(くっついた文字にする)のは「正規化」という処理で、uconvのマニュアルに例(下記)が書いてあった。

uconv -f utf-8 -t utf-8 \
  -x '::nfkc; [:Cc:] >; ::katakana-hiragana;'

上は他の処理も混ざっているので、以下の例のように、曲のタイトル、アーティスト名、アルバム名をそれぞれ正規化してから表示するようにし、上の右図のように解決した。

echo "夏が来た!" | uconv -f utf-8 -t utf-8 -x '::nfc;'
夏が来た!

※正規化処理は、マニュアルではNFKCだが、調べたらNFCのほうが良さそうなので そうした。この辺りは全く詳しくないので手探りだ。また、上のコマンドでnfc前後の :: ; は なくていいようだが、例にならって付けている。

今 上の参照ページを読んだら、NFCでも今ひとつな場合(「例6: 神と神」)があって気に入らないが、仕方ない(レーベルも、旧字体は余り使わないだろう)。一番いいのは、正規化しなくてもwishで綺麗に表示できるようにすることか。

あるいは、(一番最初に考えた方式の、)uconvでなくプログラムで変換するのがいいか。ひらがな とカタカナだけの対応になるが、濁音・半濁音と そうでない音の文字の順序には概ね規則性(元の音のコード= 濁音-1, 半濁音-2)があるので、それでcombining charactersを くっついた文字に置換するのだ。これなら漢字の旧字体は問題ない。

というか、いちいち計算するのでなく、今の追加処理のテーブル方式で全部処理するほうが良さそうだ。濁音・半濁音は多くないから、テーブルを作るのは容易だ。 → 基本的に、uconvを使うのを止めて追加処理だけにすればできるので、早速そうした。

この方式なら、あとから追加変換が必要になことが分かっても、変換テーブルに追加するだけなので容易だ。 (1/21 17:05)

ところがどっこい!w

下の左図の「グ」のように、なぜか、頑固に正規化できない文字がある(図で「ベ」は くっついているので、処理されていない訳ではないことが分かる)。

更に調べたら、どうやらuconv(使っているのは uconv v2.1 ICU 66.1)が正規化しない(素通しする)文字があるようだ。ひらがな・カタカナの濁音・半濁音について調べたところ、以下の文字が駄目だった(正規化されずにそのまま出て来た)。

ど ば べ ぼ ぱ ぺ ぽ ガ ギ グ ズ ゼ ゾ ダ ヷ ヾ

ところで、上の最後のほうにある見慣れない文字、Unicodeにある「ヷ」、「ヸ」、「ヹ」、「ヺ」って実際に使われたのだろうか? どういう読み?? 謎だ。 (→ 4/1に出た、参考になるページがあった。)

その後、PHPの国際化関数のTransliteratorのtransliterator_transliterate()でuconvと同様の正規化処理を試したら、なぜか上の文字も正規化できた。よって、下ではICUやUnicodeのライブラリの問題と書いているが、uconvの問題の可能性が高い。だが、どうしたらこういう抜けが起こるのか見当がつかない。 (1/27 16:08)

実際には、原因はuconvではなく、それが使っているICUやUnicodeのライブラリの問題だろう(日本語に詳しくない人が作った??)から、パッケージを(Linuxディストリビューションのものでない、)オリジナルの最新に入れ替えるか、設定とか調整すれば直せる気はするのだが、パッケージの交換は あとあと面倒になるのでせず、設定・調整はuconvなどのマニュアルを読んでも分からない用語が多くて どうすればいいか分からなかったので、以下のような力技で対応した。

  1. 起動時(初期化)
    1. uconvで正規化できない濁音・半濁音を調べてリストを作る。
    2. それらの正規化後の文字のリストを作る。
  2. 曲情報(タイトル、アーティスト名、アルバム名)を表示する前
    1. uconvで正規化する。
    2. uconvで正規化できなかった文字がある場合は、正規化した文字に置換する。

これなら、将来ICUやUnicodeのライブラリが更新されて、すべての濁音・半濁音が正規化できるようになれば(なんか、永遠にそうならない気が・・・w)、自動的に追加処理が不要になる。

処理を実装し、上の右図のように、駄目だった文字が ちゃんと表示され、(今のところは)一件落着となった。ただ、いつものように、他にも何か問題がありそうなので、表示やログを注視しながら音楽を聴いている。

これ、日本語以外でも あったらお手上げだが、読めなくて気付かないだろうから良し?www

 

PS. 中程("1/21 17:05"の辺り)に書いたように、これを書いたあとで、実は、uconvを使うのでなく一番最初に思い付いた単純なテーブル変換方式が一番良かったというオチだった(今のところは)。Unicodeの考えは基本的には いいと思うが、(何かで必要なのだろうが)複雑な概念をいろいろ作り出して、それが日本語や実際の用途に合わない、あるいは完全な実装ができていないために、いろいろなところで苦労してそうなのが残念だ。

  •  0
  •  0
Keys: , , ,