前回は input_tracks() を通して、各トラックの情報を集める仕組みを確認しました。
ここまでで、CUE シート生成に必要な材料──「アルバム情報(metadata)」と「曲情報(tracks)」──が揃ったことになります。
今回はいよいよ最終段階として、これらの情報をもとにCUE シートのテキストを組み立てる generate_cue_text() を見ていきましょう。
1. はじめに
CUE シートは単なるテキストファイルだが、その構造には一定のルールがある。
generate_cue_text() は、そのルールに従って必要な行を順番に積み上げていく関数である。
2. generate_cue_text() の役割
generate_cue_text() の目的はシンプルで、「metadata と tracks を受け取り、CUE シートの文字列を作る」こと。
ここで行っている処理は主に次の三つ。
・アルバム情報(PERFORMER / TITLE / FILE 行)の生成
・各トラックの TRACK 行の生成
・INDEX 01 の計算と mm:ss:ff 形式への変換
特に INDEX の計算は、「オフセット + それまでの曲の長さの累積」というルールに基づいて行われるため、generate_cue_text() が CUE シート生成の“心臓部”と言える。
3. 関数の全体像(*A〜*F 対応)
今回も、処理の流れが分かりやすいようにコードの各部分に *A〜*F の記号を付けている。
フローチャートと 1 対 1 で対応するようにしてあるため、どの処理がどこに対応しているかが追いやすい。
以下が generate_cue_text() の全体像である。
# === CUE シートのテキスト生成 ===
def generate_cue_text(metadata: Metadata, tracks: list[dict]) -> str:
lines = []
# *A: アルバム情報(PERFORMER / TITLE)
performer = metadata["album_artist"]
album_title = metadata["album_title"]
lines.append(f'PERFORMER "{performer}"')
lines.append(f'TITLE "{album_title}"')
# *B: FILE 行(WAVE ファイル名)
filename = metadata["filename"]
lines.append(f'FILE "{filename}" WAVE')
# *C: INDEX 計算の初期値(オフセット)
current_index = metadata["offset_frames"]
# *D: 各トラックの TRACK 行を生成
for i, track in enumerate(tracks, start=1):
title = track["title"]
duration = track["duration"]
lines.append(f' TRACK {i:02} AUDIO')
lines.append(f' TITLE "{title}"')
# *E: INDEX 01 の計算(フレーム → mm:ss:ff)
index_time = frames_to_cuetime(current_index)
lines.append(f' INDEX 01 {index_time}')
current_index += duration
# *F: すべての行を結合して完成
return "\n".join(lines)

4. INDEX 計算の仕組み
INDEX 01 の値は、曲の開始位置を mm:ss:ff 形式で表したもの。
CD-DA の仕様では 1 秒 = 75 フレームであるため、内部ではフレーム数で計算し、最後に mm:ss:ff に変換している。
計算式は次の通り。
・current_index = offset_frames + それまでの曲の duration の合計
・frames_to_cuetime() で mm:ss:ff に変換
この仕組みにより、曲の長ささえ入力しておけば、INDEX は自動的に正しい値が生成される。
5. 次回予告
次回は、今回の generate_cue_text() をもとに、実際に生成される CUE シートの例を示しながら、
どの行がどの処理に対応しているのかを確認していく。
いかがだろうか?
今回は分岐がないので、特にコードを読まなくても済むと思いますが、それぞれの文法を再確認しておくといいでしょう。
=> 自分
ここまで来れば、CUE シート生成の全体像が「入力 → 確認 → テキスト生成 → 完成」という一本の流れとして完全に見えてくると思います。