晴れたり沼ったり

へんなものに沼ることがあります。

レヴォーグ(VN5)の USB audio の再生順をなんとかする→chatGPT偉い→『生成AIは味の素の冷凍餃子』

VN5に乗り始めた頃は、USB-audioの再生順は filename 順だったような気がするのでファイルネームの先頭にランダムな3桁数字を書くようなプログラムを書いて(*1)使っていた。これでランダムにちゃんとなっていたキオクがあるのだが、いつからか tag中のtitle 順に再生されるようになってしまった(*2)。しょうがないので、プレイヤ側のランダム再生にしてOK!とか思っていたのだけれど、このランダムがどうもいい加減すぎて99曲までしか選んでないような気がする(未検証だが何回続けて聞いてもいつまでも再生してくれない曲があったのは確か)。

さらにもともとクラシック(*3)も車では聞くのでランダムよりは指定した順に再生してほしい。

 

ということでネットで調べると

・FATで書き込んだ順である

・ファイルネーム順である

と書いてあるページとかメーカー説明が多い。うちのレヴォーグのは少なくともこれらにはあってない。てなことでもう少し探すと titlesort というタグがあって、その順になるというwebページを発見した。これをクリアすればファイルネーム順にという説明だったが特にtitlesortは該当の音楽ファイルには書かれていないので  tag のタイトル順に再生してしまう。

つまり titilesort > title (> ファイル名かFATに書き込んだ順|ファイル名順)ということ。

 

# 後ろの2つは実験してないので想像だが、tag の titlesort などが優先されることは実験済み 

 

しょうがないのでtitle にランダムで乱数を書き込むプログラムを書き始めたところだったのだがよく考えたらtitlesort というタグにランダム数字を書き込めばいいのではないか?と思いついた(気がつくのが遅いぞ、お前...)。ランダム数字3桁ではタイトル表示長すぎていやだと思ったので62進数というのを考えて2桁で収めようと思っていたのだがその必要もなかった。アホなこと考えててたなあ。

n62s='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'

 

ということでぽちぽちと python で書き始めた。

出来た〜と思ってテストを始めたらmp4ファイル(MP4タグ)はeasyID3ではダメなのが判明したところ。easymp4もあるのでそちらも入れてファイル名でどっちを使うか決めればいいだけだなのなのだが。

ここで、ちょっと飽きたのでこの記事の下書きを書き始めたのだが、現時点では rev.1 が出来て mp4でもmp3でも対応するように出来た。

こんなライブラリを利用した。

from pathlib import Path

import sys

import os

from mutagen.easyid3 import EasyID3

from mutagen.easymp4 import EasyMP4

import random

 

chatGPTである程度きれいに直してもらったソースコードを載せておく。

簡単な動作確認はしてますが、使うときは自己責任で。あと、mutagenを自分でインストールできるレベルの人が対象。

# 直してもらったのはいいのだが、コメントも剥ぎ取られてしまったし、自分で書いただけに可読性、メンテ性は自分で書いたヤツのほうがいいというのはこちらがわのスキルの問題なんだろな:-)

 

# 書き直しじゃなくて、最初から書いてもらったらどうなるのかも試してみようと思って、 ファイルネームに nnn- みたいのを付ける必要はなくなったのでタグから一括生成するやつを試しに書いてもらったら(*4)、仕様を書くのに5分。コード生成に1秒以下みたいなレベルだった。恐るべし。知り合いの大学の先生2人が有料契約をしてるというのに先日驚いたのだが、これは確かに使うなあ。データ処理用のプログラムなんて使い捨てでバンバンかけそう。しなくていいことはやらずに本業の研究や講義などに集中するというためにはこれはたしかに使うなあ。なるほどね〜。『生成AIは味の素の冷凍餃子』ですな。『手抜きじゃなくて時短』。自分で出来ないこと/アウトプットを自分で確認出来ないことをやらせたりするのはよくない。

#!/usr/bin/env python3
import os
import sys
import random
from pathlib import Path
from mutagen.easyid3 import EasyID3
from mutagen.easymp4 import EasyMP4

SUPPORTED_EXTS = [".mp3", ".MP3", ".mp4", ".aac", ".m4a"]

def get_audio_files(path):
    return [f for f in Path(path).glob('*.*') if f.suffix in SUPPORTED_EXTS]

def get_tag_handler(file_path):
    if file_path.suffix.lower() == '.mp3':
        return EasyID3(file_path)
    else:
        return EasyMP4(file_path)

def print_file_list(files):
    for file in files:
        try:
            tags = get_tag_handler(file)
            ts = tags.get('titlesort', ['   '])[0]
        except Exception as e:
            ts = 'ERR'
        print(f"{ts} // {file.name}")

def erase_titlesort(files):
    for file in files:
        try:
            tags = get_tag_handler(file)
            if 'titlesort' in tags:
                del tags['titlesort']
                tags.save()
            print(f"Erased: {file.name}")
        except Exception as e:
            print(f"Error erasing {file.name}: {e}")

def generate_random_titles(count):
    indices = list(range(count))
    random.shuffle(indices)
    return [str(1000 + i)[-3:] for i in indices]

def assign_random_titlesort(files):
    random_titles = generate_random_titles(len(files))
    for file, title in zip(files, random_titles):
        try:
            tags = get_tag_handler(file)
            tags['titlesort'] = title
            tags.save()
        except Exception as e:
            print(f"Error assigning random title to {file.name}: {e}")

def print_sorted_titlesort(files):
    records = []
    for file in files:
        try:
            tags = get_tag_handler(file)
            ts = tags.get('titlesort', [''])[0]
            records.append*1
        except Exception:
            records.append*2
    for ts, name in sorted(records):
        print(f"{ts} // {name}")

def main():
    args = sys.argv[1:]
    cwd = Path.cwd()
    files = get_audio_files(cwd)

    if not args:
        print(f"cwd = {cwd}")
        print_file_list(files)
        return

    command = args[0]
    target_files = get_audio_files(cwd) if len(args) == 1 else [Path(f) for f in args[1:]]

    if command == "erase":
        erase_titlesort(target_files)
    elif command == "rand":
        assign_random_titlesort(target_files)
        print_sorted_titlesort(target_files)
    else:
        print("Usage: uar [|erase|rand] [<file names>]")
        sys.exit(1)

if __name__ == '__main__':
    random.seed()
    main()

 

 

*1:  厳密にはその先々代の BG9 に自分で入れ替えたmp3再生機能付きのCDレシーバアンプの頃に書いたプログラムでランダムな数字がファイルネームにあるファイルをCD-Rに書き込む時にその順番でファイルを書いていたのでFATに書き込まれた順というルールに則っていたというのが正解かもしれない。BP5の純正オーディオでも同じ動作だったはず。

 

*2: 自分でバッテリー交換したタイミングだったかもしれない。何度かディーラーに出した時にそこも改善(改竄?)されたのかもしれない。なってからは実はかなり経つ。昨秋にmacに変える前にも1年位なんとかしようと思っていたけど、macに変えたら環境が変わるので細かいところが変わるから今はやめようとイイワケを拵えて放置してた :-) macに変えてからも車で長旅に出るたびに「帰宅したらさっさとやろう」と心に決めていたのだが、ブログ記事を書いている間に心の中の優先度が下がってしまって放置していたが今度という今度はちゃんと実行した。

 

*3: 車内は煩いのでピアニッシモからフォルテッシモまでちゃんと聞くのはムリなのでオケ作品よりはバロック器楽曲、とくにチェンバロなんかが主に対象。バッハの無伴奏チェロ組曲なんかもよく再生してる。

 

*4: 書いてもらったのはいいのだが、試しに使ってみたら古い時代の mp3 ファイルはそのころ使える携帯プレイヤが日本語がカタカナしかダメとか、文字コードはshift-jisのみ対応だったりしてひどい目にあった。プログラムは正しいのだが、処理する対象が仕様にあってないとひどいことになるという典型例だ。現役時代はこれでなんどかものすご〜〜〜くひどい目にあったなあ:-)

*1:ts, file.name

*2:'ERR', file.name