kostumブログ

勉強したことやノート代わりのアウトプットに使っています。

自宅NASサーバーの構築方法

目次

こんにちは。

自宅NASサーバを構築したいと思いました。 色々と調べながらやってみたのですが、なんだかうまく構築できなかったので、順序立てて一つずつ対応していくためにやり方をまとめました。

せっかくなので記事としてまとめ、誰かのお役に立てれば良いかと思います。

要件

今回NASサーバを構築する際において、以下の要件を満たすことにしました。

  • OS: Ubuntu
  • 家にある使われていないPCを使う
  • RAID1を採用する
  • 管理・運用のためにSSH接続できるようにする
  • ファイルサーバとして他PCから共有ができるようにする

実践

というわけで、インストールから始めます。

必要なもの

その前に、NASサーバを構築するために必要なものを記載します。

  • PC
    • ファイルサーバにするためのパソコン。
    • 有線のキーボードとマウス
  • HDD × 2
    • データを保存するためのHDD。RAID1を構築するため、2つ用意します。
  • USB 16GB以上
    • Ubuntuをインストールするために必要です。後述しますが、ISOファイルを書き込む都合上、16GB以上の容量が必要になります。

Ubuntuのインストールのための準備

UbuntuをOSとして用いるため、まずはUbuntuをダウンロードします。

Ubuntuを入手する | Ubuntu | UbuntuUbuntuを入手する | Ubuntu | Ubuntu

Ubuntuは、その用途に合わせてさまざまな種類をダウンロードすることができます。 今回の場合、用途がファイルサーバなので、Ubuntu Serverを選択しても良いのですが、GUIを備えたUbuntu Desktopを選択します。

Ubuntu Desktopをダウンロードすると、ISOファイルが取得できます。
ISOファイルとは、1つの光学ディスクに保存されるデータを一つにまとめたデータ、のことです。 つまり、このISOファイルがUbuntuOSを使うためのひとまとめのデータということです。

次に、このUbuntuOSをPCにインストールします。
しかし、このISOファイルをどうやってPCにインストールすれば良いのでしょうか。
ここでは、USB Boot機能によるインストール、つまり、USBからOSを起動してインストールを行なってみます。

USBからBoot起動させるには、まずUSBにISOファイルを書き込む必要があります。
この書き込み用ソフトウェアにRufusがあります。

rufus.ie

なので、Rufusをダウンロードします。

Rufusを起動する前に、USBをフォーマットしておきましょう。
フォーマットしたら、USBをPCに挿したまま、Rufusを起動します。
PCにUSBだけを挿していれば、「デバイス」項目にそのUSBが選択されているはずです。
ブートの種類の横にある「選択」をクリックし、先ほどダウンロードしたISOファイルと選択します。その他の設定はそのままとし、「スタート」をクリックしましょう。

「スタート」をクリックすると、ハイブリッドISO形式のイメージを検出したことによる書き込む形式の確認ダイアログが表示されます。
ここでは、推奨されている「ISOイメージモードで書き込む」を選択し、「OK」をクリックします。
その後、データ消去の確認ダイアログも「OK」とし、書き込みを開始します。

この書き込みは10分くらいかかるので、気長に待ちましょう。

書き込みが完了したら、NASにしたいPCに差し替えましょう。

UbuntuOSのインストール

さて、Ubuntu OSのインストールの準備が完了しましたので、次はUbuntuOSのインストールを行います。

NASにしたいPCはシャットダウンしておきます。

そして、RufusによってUbuntuが書き込まれたUSBをPCに挿しておきます。
次に、そのPCの電源を入れるのですが、USB Boot起動をさせるために、PCをBIOS起動させます。
BIOS起動させるための方法は、PCメーカによってさまざまなため、以下のサイトを参考に起動方法を調べましょう。

www.tenorshare.jp

私の場合はASRockのマザーボードを使っているので、起動した時のメーカーロゴ画面でF11キーを押下してBIOS画面を起動しました。

BIOSに入ると、bootデバイスを選択する画面になるので、USBを選択しましょう。
すると、Ubuntuのインストール選択画面に入るため、「Try or Install Ubunt」を選択して、Ubuntuをインストールします。

そうすると、UbuntuOSのGUI画面が表示されます。
その画面の右下にインストールショートカットがあるので、それをダブルクリックしてPCへのインストールを開始します。

インストールでは、ウィザードに沿って進めてください。ただし、インストールするハードディスクは、RAID1用のHDDにはインストールしないように注意しましょう。

インストールが完了したら再起動をして、そのときにUSBを抜いておきましょう。

初期設定

SSH

NASを構築する上で、さまざまな設定を行なっていきます。 が、このままNASとして活用していくPCで操作するのは、開発者体験があまり良くありません(NAS PCがディスプレイと場所を占有してしまうのは他の作業ができなくなってしまうので)。
なので、私の場合は別のMacBookから無線アクセス、つまりSSH接続して設定していきます。

再起動したNAS PCでログインし、インストール時にWi-fiを設定していない場合はWi-fiを設定します。この後、パッケージの更新を行うときに、ネットワークが繋がっていないと更新ができないので気をつけてください。

次に、画面左下のアプリケーションメニューから「Terminals」を起動します。 そして以下を実行して、パッケージの更新を行います。

sudo apt update
sudo apt full-upgrade -y

さらに、SSH接続のためのパッケージをインストールします。

sudo apt install openssh-server

この後、NAS PCを再起動します。

%% 再起動コマンド %%
> sudo reboot

再起動が完了したらターミナルを開いて、以下のコマンドを実行してSSHの状態を確認します。

admin@file-system:~$ sudo systemctl status ssh
[sudo] password for admin:
● ssh.service - OpenBSD Secure Shell server
     Loaded: loaded (/usr/lib/systemd/system/ssh.service; enabled; preset: enabled)
     Active: active (running) since Sat 2025-06-21 21:10:18 JST; 1h 40min ago
TriggeredBy: ● ssh.socket
       Docs: man:sshd(8)
             man:sshd_config(5)
   Main PID: 1180 (sshd)
      Tasks: 1 (limit: 6931)
     Memory: 4.4M (peak: 20.6M)
        CPU: 261ms
     CGroup: /system.slice/ssh.service
             └─1180 "sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups"

上記のようになっていれば(Active: active (running))、正常に起動しています。
また以下のコマンドを実行して、NAS PCのIPアドレスなども調べておきましょう。

admin@file-system:~$ ifconfig

...

wlp7s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1440
        inet 192.168.0.215  netmask 255.255.255.0  broadcast 192.168.0.255
        inet6 fe80::4f83:1976:f576:fa2c  prefixlen 64  scopeid 0x20<link>
        inet6 2001:268:738d:eab6:a7be:cc83:caf6:e76c  prefixlen 64  scopeid 0x0<global>
        inet6 2001:268:d2df:5a2f:49a9:cc42:c170:db2e  prefixlen 64  scopeid 0x0<global>
        inet6 2001:268:d2df:5a2f:86:535e:854a:7fb8  prefixlen 64  scopeid 0x0<global>
        ether 58:a0:23:db:80:ad  txqueuelen 1000  (Ethernet)
        RX packets 62371  bytes 9698686 (9.6 MB)
        RX errors 0  dropped 1  overruns 0  frame 0
        TX packets 27767  bytes 3653553 (3.6 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

この192.168.0.215にアクセスすることになります。
このIPアドレスは、再起動するたびに変更されてしまいます。 そのため、Ubuntuのネットワーク設定でIPアドレスを固定するように設定しておきましょう。

以上、NAS PCでの操作はここまでになります。これ以降はMacbbook PCでの操作になります。

NAS PCへSSH接続できるか確かめてみましょう。
Macbook PCで、ターミナルを開きます。また、Wi-fiNAS PCと同じネットワークを使用しておきましょう。
以下のコマンドを実行すると、今までの設定がうまくいっていればログインできるかと思います。

❯ ssh admin@192.168.0.215
admin@192.168.0.215's password:
Welcome to Ubuntu 24.04.2 LTS (GNU/Linux 6.11.0-26-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

Expanded Security Maintenance for Applications is not enabled.

0のアップデートはすぐに適用されます。

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status

Last login: Sun Jun 22 21:06:47 2025 from 192.168.0.220

これで、SSHのインストールは完了です。

おまけ

SSHを使用してログインするとき、ユーザー名やIPアドレス、パスワードをいちいち入力するのは面倒です。
なので、より安全なログイン手法であるキーペアを使ったログインとショートカットコマンドの作成を行います。

まず、キーペアを作成します。 以下を実行するとキーペアが生成されます。

ssh-keygen

生成されたキーペアは、サーバー側に登録する必要があるので、さらに以下を実行します。

ssh-copy-id {ユーザー名}@{IPアドレス}

ここまで行うと、以下を実行することでキーペアを使ったログインが可能になります。

ssh -i {登録したキーペアのパス} '{ユーザー名}@{IPアドレス}'

最後に、~/.ssh/configに以下の設定値を記載することで、ショートカットコマンドを登録することができます。

Host {コマンドキー}
    HostName {ホスト名} 
    User {ユーザー名}
    Port (ポート番号)
    IdentityFile {鍵へのPATH(例えば~/.ssh/hoge)}
さらなるおまけ

SSHにログインすると、常に以下のテキストが表示されます。

Welcome to Ubuntu 24.04.2 LTS (GNU/Linux 6.11.0-26-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

Expanded Security Maintenance for Applications is not enabled.

0のアップデートはすぐに適用されます。

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status

このテキストは、/etc/update-motd.d/内にbashファイルを含めることで、好きなテキストを表示ことができます。
例えば、/etc/update-motd.d/01-welcomeを作成し、以下を作成してみます。

#!/bin/sh
printf "\n"
figlet "WELCOME TO FILE-SERVER" | /usr/games/lolcat -f

また、NASサーバで以下をインストールしておきます。

sudo apt install figlet lolcat -y

このように設定すると、以下のように表示することができます(FILE-SERVERのところは色がついています)。

Welcome to Ubuntu 24.04.2 LTS (GNU/Linux 6.11.0-26-generic x86_64)

__        _______ _     ____ ___  __  __ _____
\ \      / / ____| |   / ___/ _ \|  \/  | ____|
 \ \ /\ / /|  _| | |  | |  | | | | |\/| |  _|
  \ V  V / | |___| |__| |__| |_| | |  | | |___ _
   \_/\_/  |_____|_____\____\___/|_|  |_|_____( )
                                              |/
 _____ ___ _     _____     ____  _____ ______     _______ ____
|  ___|_ _| |   | ____|   / ___|| ____|  _ \ \   / / ____|  _ \
| |_   | || |   |  _| ____\___ \|  _| | |_) \ \ / /|  _| | |_) |
|  _|  | || |___| |__|_____|__) | |___|  _ < \ V / | |___|  _ <
|_|   |___|_____|_____|   |____/|_____|_| \_\ \_/  |_____|_| \_\


 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

Expanded Security Maintenance for Applications is not enabled.

0のアップデートはすぐに適用されます。

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status

RAID1設定

SSHによる設定ができるようになったところで、RAID1構成を構築していきます。
以下は、NASサーバーにログインして行なってください。

まず、対象となるディスクを確認するために、以下のコマンドを実行してください。

sudo fdisk -l

すると、以下のような出力が見られます(以下は、すでにRAID1を構築してしまった後の出力のため、少し違うところがあるかもです)。

Disk /dev/sdb: 7.28 TiB, 8001563222016 bytes, 15628053168 sectors
Disk model: ST8000DM004-2U91
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disklabel type: gpt
Disk identifier: 4BBFCFF6-2C8B-4FC1-B8F3-7B36208CEBA6

Disk /dev/sdc: 7.28 TiB, 8001563222016 bytes, 15628053168 sectors
Disk model: ST8000DM004-2U91
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disklabel type: gpt
Disk identifier: 75BBFE6C-ED6D-4FB4-811E-DFAA2790B2D3

自分がどのHDDを用意したのか把握しておくと、判別しやすいですね。
上記の場合では、/dev/sdb, /dev/sdcが対象のディスクになります。
また、構築の前に、対象となるディスクにデータが含まれていないか確認しておきましょう。RAIDを作成するとき、初期化が行われるので、重要なデータはすべて削除されてしまいます。

まず、partedコマンドを使用して、gptで全容量を指定したパーティションを作成します。gptなどについては、こちらを参照してください。

sudo parted --script /dev/sdb "mklabel gpt"
sudo parted --script /dev/sdc "mklabel gpt"
sudo parted --script /dev/sdb "mkpart primary 0% 100%"
sudo parted --script /dev/sdc "mkpart primary 0% 100%"

続いて、パーティションraidフラグを付与します。これを付与しないとカーネルRAIDアレイと認識されないようです。

sudo parted --script /dev/sdb "set 1 raid on"
sudo parted --script /dev/sdc "set 1 raid on"

先ほどのfdiskを実行するとフラグが立っているのが確認できます。

sudo fdisk -l

Disk /dev/sdb: 7.28 TiB, 8001563222016 bytes, 15628053168 sectors
Disk model: ST8000DM004-2U91
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disklabel type: gpt
Disk identifier: 483BCA84-082D-40A9-9014-9D31985E3C7A

Device     Start         End     Sectors  Size Type
/dev/sdb1   2048 15628052479 15628050432  7.3T Linux RAID


Disk /dev/sdc: 7.28 TiB, 8001563222016 bytes, 15628053168 sectors
Disk model: ST8000DM004-2U91
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disklabel type: gpt
Disk identifier: 6A7AB752-5E96-4954-9E61-A751D03BA223

Device     Start         End     Sectors  Size Type
/dev/sdc1   2048 15628052479 15628050432  7.3T Linux RAID

次に、RAIDを構築するために、mdadmパッケージをインストールします。

sudo apt install mdadm

インストールが完了したら、以下のコマンドを実行し、RAID1アレイを作成します。

sudo mdadm --create --verbose /dev/md0 --level=1 --raid-devices=2 /dev/sdb1 /dev/sdc1

このコマンドによって、/dev/sdb1, /dev/sdc1を使用して新たに/dev/md0というRAIDバイスを作成しています。
作成には、HDDの容量によって非常に長い時間がかかる場合があります。例えば、私の場合は8TBで12時間ほどはかかっています。
進捗を確認するには、以下のコマンドで確認できます。

cat /proc/mdstat

さて、RAID1の作成が成功したら、今度はこのRAID1構成を永続させる設定を行います。
まず、現在のRAID構成をmdadm.confに保存します。

sudo mdadm --detail --scan | sudo tee -a /etc/mdadm/mdadm.conf

次に、RAIDアレイにファイルシステムを作成します。以下の場合は、ext4として作成しています。

sudo mkfs -t ext4 /dev/md0

さらに、マウントポイントを作成します。マウントポイントとするディレクトリ名は自分の好きな名前にしてください。

sudo mkdir -p /mnt/file-server

そして、マウントしてみます。

sudo mount /dev/md0 /mnt/file-server

動作確認がうまくったら、自動マウントのために/etc/fstabにUUIDで追記を行います。

sudo blkid /dev/md0

出力されたUUIDをもとに、以下のように/etc/fstabに以下を記述します。

/dev/disk/by-uuid/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /mnt/file-server ext4 defaults 0 0

最後に、

sudo update-initramfs -u

を実行して、ブート時にinitramfs(初期 RAM ディスク)から読み込ませるようにします。
これで再起動した後でも、RAID1が自動でマウントされるようになります。

WindowsPCの共有設定

このエントリもそろそろ終わりに近づいてきました。 最後は、WindowsPCからの共有を可能にしていきます。

今ままで、データを保管する場所を作成しましたので、ここからはファイルサーバとして使うための設定を行なっていきます。

ファイルサーバを構築するためには、sambaというパッケージを使用します。
以下のコマンドを実行して、sambaをインストールします。

sudo apt install samba -y

これによってsambaがインストールされますので、今度は設定を記載していきます。
sambaの設定ファイルは、/etc/samba/smb.confに記載します。なので、以下を実行して設定を記載してください。

sudo nano /etc/samba/smb.conf

[file-server]
comment = file-server
path = /mnt/file-server
browseable = yes
writeable = yes
read only = no
guest ok = no
create mode = 777
directory mode = 777

そして、Sambaでファイル共有を行う場合は、Sambaユーザーを作成する必要があります。そのため、Linux上に通常のユーザーを作成し、そのあとでSambaのパスワードを設定します。

sudo adduser hoge
sudo smbpasswd -a hoge

最後に以下のコマンドを実行してsambaを再起動して設定を反映させます。

sudo systemctl restart smbd

これによって、上記で固定したIPアドレスWindowsからアクセスし、先ほどのユーザー名tとパスワードを入力すれば、Ubuntuのfile-serverへアクセスできます。

終わり

これで自作NASサーバの構築は終わりになります。
ネットで最初に出てきた情報だけだと、RAIDの永続化やWindowsの共有設定など上手くできませんでした。
上記にまとめた内容で、自分はうまく構築することができたので、皆様の役に立てれば幸いです。

また、最後にこの構築方法に至るまでに活用させていただいた参考文献を記載しておきます。上記の方法でうまくいかない場合は、参考にしてみてください。

それでは、最後までお読みいただきありがとうございました。

参考

qiita.com

qiita.com

qiita.com

qiita.com

qiita.com

www.linux.digibeatrix.com

scrapbox.io

qiita.com

minokamo.tokyo

zenn.dev

JSConf JP 2024に参加してきました。

はじめに

2024/11/23に開催されたJSConf JP 2024に参加してきました。 そこで参加できたセッションについて、まとめとノートしたこと(少ないですが...)、感想をアウトプットさせていただきます。

jsconf.jp

アクセス

会場:九段坂上KSビル 〒102-0073 東京都千代田区九段北1-14-6 九段坂上KSビル

Timetable

jsconf.jp

目次

LT:Promise.try:シンプルで強力な同期/非同期統合の未来-実装の深層とPromiseの進化

by saku in トラックB 3F 301 at 10:10~10:20

sakupi01.github.io

  • NewPromiseCapabilityがキモ。
  • Promise.tryはJSでかkれている。
  • abortControllerはDOM API

わざわざPromiseでラップする必要がなくなるので便利そう。

LT:JavaScriptを支えるエコシステム(漫才)

by ECMAボーイ (@uhyo_) and # ECMAボーイ (@shqld) inトラックB 3F 301 at 10:20~10:30

docs.google.com

  • おかん時々おとん。

パッケージマネージャやバンどらの特徴を面白く捉えた漫才だった。

LT:Storybookとの上手な向き合い方を考える

by りんたろー in トラックC 3F 302 at 10:40~10:50

speakerdeck.com

途中から聞けた。 開発後半になると形骸化してしまうStorybookの向き合い方を教えてくれました。

LT:あなたの知らないFunction.prototype.toString()の世界

by mizdra in トラックC 3F 302 at 10:40~10:50

speakerdeck.com

ハック的な使い方。かと思いきや、みんな知ってるライブラリの中でも使われているのに驚いた。最終的には容量用法を守って使う必要があるとのことですが、面白い使い方を知れました。

WebAssemblyUnleashed:PoweringServer-SideApplications

by Chris Fuentes in トラックA 7F パーティースペース at 11:10~11:40

{資料}

頑張って英語(翻訳無し)を聞いてみましたが、分かりませんでした。。。すみません。。。

CreatingFast-FeelingWebApps

by rung Vo in トラックA 7F パーティースペース at 11:10~11:40

trungvose.notion.site

こちらは事例とかを動画で見せてくれたため、少し理解ができました。 Webアプリを早く見せるための妙を紹介してくれました。 ローディングアニメーションやライブラリを使ったり、キャッシュを実装することで早くしているのかな、と思います。

BuildingAIappswithJavaScript

by Aileen Villanueva Lecuona in トラックA 7F パーティースペース at 13:10~13:40

jsconf.jp

  • Data
    • テキスト
  • SPLIT
    • テキストを適切なObjectへ変換?
  • EMBEDDING
    • コード化(parse)
  • STORE
    • データの保存?[suspense]
  • RETRIEVE
    • KEYWARD
    • ○SEMANTIC
  • GENERATE

頑張って英語(翻訳無し)を聞いてみましたが、分かりませんでした。。。すみません。。。

WhyBuildaSecureSupplyChainManagement?

by VJ in トラックA 7F パーティースペース at 13:40~14:10

企業ブースの方に寄っていたら、時間を過ぎてしまいました。

リアルアセットとWebのシームレスな活用のためのパスキー

by maxmellon (Kento TSUJI) in トラックB 3F 301 at 14:20~14:50

{資料}

パスキー導入のための東急株式会社さんの取り組みを紹介してくれました。
パスキーの導入の仕方についてはもちろんですが、そのパスキーをどうやってユーザーに使ってもらうのかが大変だったようです。

JavaScriptのモジュール解決の相互運用性

by berlysia in トラックB 3F 301 at 14:50~15:20

speakerdeck.com

Import PathおのPath解決方法について。 cjsとesmのどちらかの方法を使うことになるのかをお話ししてくれました。

ModularMonolithMonorepo

by 坂本結衣(yui_tang) in トラックB 3F 301 at 15:30~16:00

speakerdeck.com

Modular Monolith Monorepoを実現するためにやったことをお話ししてくれました。 かなりの量のルールを決めたDesignDoc、pnpmをうまく使うことで実現されていました。

JavaScriptイテレータとイテラブルの概要と課題、未来

by Sosuke Suzuki in トラックB 3F 301 at 16:00~16:30

{資料}

JavaScriptIteratorについて分かりやすく説明していただけました。 ArrayやMapなど、for文でループすることができるものは、イテレータの機能が用いられているのは目から鱗でした。 e18eによるとジェネレータは最適化されていないため使用しない方が良い、と書かれていた気がしましたが、ユースケースに記載がある場合は、逆に使った方が良いかもと思いました。

SponsorLTx12

by ? in トラックA 7F パーティースペース at 16:40~17:50

スポンサーをしていただけた各企業様の会社紹介やLT。

啓蒙者の視点で振り返るウェブ

by Eiji Kitamura / えーじ in トラックA 7F パーティースペース at 17:50~18:30

API開発時の公開やためになるお話を聞けました。 Web Audio APIは、自分でもかなり勉強したことのあるAPIだったので、非常に興味深かったです。

まとめ

以上、私が参加しようと思っていたセッションのノート、感想でした。

JSConf JPの参加は初めてでしたが、楽しかったし勉強にもなりました。 運営の方、発表してくださった方、そして参加した皆様、ありがとうございました。

以下では、私は参加できませんでしたが、他セッションで発表していただいた方のスライドを貼っておきます。

speakerdeck.com

speakerdeck.com

scrapbox.io

speakerdeck.com

speakerdeck.com

JavaScriptにおける再代入可能な変数の扱い方

目次

調べたものとは?

再代入可能な変数を定義し、ある関数が実行されるたびにその変数に任意の数を加算する実装が必要になりました。

「状態」という概念を用いればhookscomposablesの中でuseStaterefを使い簡単に実装できます。しかし、それを使わないで実装したいな、と思ったのでchatGPTを使って調べてみました。

結果

まず問題あるのか?

JavaScriptにおいて再代入可能な変数を関数が実行されるたびに変更するのは一般的に問題ありません。

しかし、その扱いには注意が必要となります。変数が関数の外部にあり、関数実行のたびに更新されるような「状態」を持つ場合、変数の管理方法がコードの読みやすさや保守性に影響します。

再代入可能な変数の状態管理における注意点

JavaScriptでは再代入可能な変数を更新して状態を持たせることは可能ですが、以下のような注意が必要となります。

  1. スコープ管理:状態を持つ変数がグローバルスコープにある場合、予期せぬ変更や競合が生じる可能性があります。できるだけ変数のスコープを狭めることが望ましいです。
  2. 可読性:状態を持つ変数は、どこで変更されるのか分かりにくくなる可能性があります。予期せぬタイミングで変数が変わると、バグを招きやすくなります。
  3. 関数の副作用:内部で直接関係のない変数を更新する副作用を持つと、他の部分で再利用しづらくなる可能性があります。必要に応じて変数を更新する関数(例えば increment() など)を定義し、関数の実行後にその関数を呼び出すようにすると、可読性やメンテナンス性が向上します。

より良い状態管理の例

次のコード例は、外部変数 variable の管理を工夫した形です。

let variable = 0;

function hogeFunc() {
    // 関係のない処理
    console.log("Executing unrelated logic");
}

// variableを増やすための関数を別に定義する
function increment() {
    variable += 1;
}

// test関数実行後にincrementAを呼び出す
hogeFunc();
increment();
console.log(variable); // variableが+1された状態で出力

JavaScriptでの状態管理のための代替アプローチ

状況によっては、以下のような管理方法も検討すると良いでしょう。

クロージャを用いた変数のカプセル化

変数を関数内に閉じ込めて外部から直接アクセスできないようにすることで、意図しない変更を防ぎます。

function stateManager() {
  let variable = 0;
  
  return {
    increment: function () {
      return variable += 1;
    },
  };
}

オブジェクトやクラスを利用した管理:

変数variablesが増加する機能を持つオブジェクトやクラスを定義すると、状態の管理がしやすくなります。

const stateManager = {
    variable: 0,
    increment() {
        this.variable += 1;
    }
};

function hogeFunc() {
    console.log("Executing unrelated logic");
}

// hogeFuncの後にincrementを呼ぶ
hogeFunc();
stateManager.increment();
console.log(stateManager.variable); // variableが+1された状態で出力

関数引数で状態を受け渡す方法

場合によっては、状態を関数の引数として渡し、戻り値で更新する方法も有効です。これにより、関数の副作用がなくなり、this も不要になります。

function increment(value, amount = 1) {
    return value + amount;
}

let variable = 0;
variable = increment(variable); // Aに1を加える
console.log(variable); // 出力: 1

variable = increment(variable, 3); // Aに3を加える
console.log(variable); // 出力: 4

まとめ

JavaScriptで再代入可能な変数に関数実行のたびに変更を加えること自体は可能ですが、スコープ、関数の副作用、可読性を考慮し、状況に応じてクロージャやオブジェクトを活用するのが望ましいです。

Vue Fes Japan 2024に行ってきました

vuefes.jp

前回の記事 kostum.hatenablog.jp

2024/10/29に昨年も参加したVue Fes Japan 2024に参加してきました。

ということで、今年も参加したセッションでメモしたノートをアウトプットさせていただきたいと思います。
(本当は共有と言いたいところですが、そんなにレベルの高いことができなかったので。。。)

また、他セッションのスライドは公式Webサイトのタイムテーブルにリンクがあるので、興味がある方や、他セッションの内容も気になるという方は上記のリンクから見てみてください。

目次

キーノート

by Evan you in メドピアトラック at 10:40

  • Vueの歴史
  • keep evolving
    • 1.0: DOM based
    • 2.0: virtual DOM + SSR
    • 3.0: composition API , ES2015, TypeScript, compiler optimization
  • vue's future
    • 10年間で37%の成長率
    • vue3に至っては1年間で98.4%
  • Vue3 stableとimprove
    • 3.2:
    • 3.3:
    • 3.4: parser rewrite, reactivity reflection
    • 3.5:
      • reactivityの体験を良くした
      • useTemplateRef
      • SSRの改善
        • Lazy hydration
        • useId → serverとclientで共有
      • defineCustomElement
  • volar 2.0
    • IDE support
      • takeover mode
      • Hybrid mode(ゼロコンフィグ)
  • Devtoolsもパフォーマンスが良くなっている。
  • vapor mode
    • パフォーマンスの向上
  • 3.6 planning
  • vite
    • 様々なフレームワークに優れている。
    • perfectではない。妥協していたものがあった。
      • esbuild, Rollup, swcそれぞれに依存している。
      • Rolldownはunifyしていきたい。
    • viteが抱えている課題。巨大なJavaScriptのエコシステムと同じ。
      • unified toolchainが必要。
      • void(0) - next-generating tool
  • oxc
    • oxcの機能
      • parcer
      • linter
      • resolver
      • transformer
      • minifier
      • formatter
    • oxcの特徴
      • faster parser
      • faster linter
      • faster resolver
      • faster transformer
      • s,aller package/Binary size
  • Rolldown
    • Basic Bundling
    • oxc integration
    • advanced feature < vite integration

VueとViteで作るUIコンポーネントライブラリ~デザインシステムとプロダクトの理想的な分離を目指して~

by 辻 佳佑 in メドピアトラック at 11:20

speakerdeck.com

IT未経験者をVue.jsで開発できるITコンサルタントに育てあげる秘訣-フューチャーの新人研修の取り組み

by 永井 優斗 / Yuto NAGAI in メドピアトラック at 11:35

聞けなかった。

AIとともに歩んだライブラリアップデートの道のり

by 中上 裕基 in メドピアトラック at 11:50

speakerdeck.com

途中から。

Piniaの現状と今後

by 若原 緑 in MNTSQが全ての合意をフェアにするぞトラック at 12:20 - 12:40

speakerdeck.com

pnia(ぴーにゃ)と読むのが正解。 公式推奨がVuexからPiniaになったので、npm downloadsも逆転したのかも。

Oxc-The_JavaScript_Oxidation_Compiler

by Boshen Chen in メドピアトラック at 13:10 - 13:40

https://jjdlwtezpdclgxxagxpj.supabase.co/storage/v1/object/public/common_asset/archives/boshen.pdf

  • compiler
  • oxcは誰が使うのか
    • nova emgine
    • React, shopify
  • performance engineeringとは
    • MIT OpenSource
    • 様々な属性より優先度が落ちがち
    • CPUの内部を考慮すると、メモリだけではなくCPUに任せた方が良い
  • Rust Programming
    • memory safety
  • JavaScript
    • 最適化による効率化が難しい
  • parser
    • swc, babelより高速
    • String
      • heap allocation → 遅い
    • SIMD - Single instructions, multiple data
      • Web assemblyにのみある
    • Memory Arena for AST
      • oxcでは何百万ものノードをヒープに登録する。
      • これが遅くする
      • Rustの課題としては、GCを持っていない。
  • OXlint
    • ESlintよりも高速
    • Parallelization
      • multiple core
    • transformer
      • performance
        • swc, babelより高速
        • single AST Path
      • minifier
        • benchmark
          • swcを超えない
    • test infrastructure
    • Bundler(Rolldown)
      • performance
  • Rustの学ぶ意味
    • 高速を目指すのであれば学ぶべき。
    • ただ、学ぶことが多い。
  • learn more
    • oxc.rs
    • rolldown.rs

Vue3とSvelte5のランタイムを比較する〜技術を一段深く理解する〜

by 山下 裕一朗 in MNTSQが全ての合意をフェアにするぞトラック at 13:55 - 14:25

docs.google.com

VueとSvelteの違いとして仮想DOMの使用有無の違いがありますが、それを実装レベルで知れて面白かったです。 また、そもそもそのフレームワークの様々なbuilt-inの機能をコードレベルで知ることができたのは勉強になりました。

次世代フロントエンドクロストーク

by Evan You, Boshen Chen, 太田 洋介, Sosuke Suzuki, unvalley, Kia King Ishii(通訳兼ファシリテーター) in MNTSQが全ての合意をフェアにするぞトラック at 14:40 - 15:40

JavaScriptエコシステムの未来

evan you:

既存のものと競合するようなことをしているように見えるが、JavaScriptのエコシステムを良くしていくことを目的としている。 vueやviteコミュニティでは様々な他プロジェクトに絡んできている人もいる。

昔はビルダーやフレームワークからwebエンジニアが入ってきて、フレームワークにより設定ファイルが全く異なっていた。

この4年でmetaフレームワークが出てきており、0からの再開発をせずに開発していきたいところがある。

このメタフレームワークにViteが選ばれてきている。 これによって、Viteによって基礎を築き始めている。この機会がJavaScriptも統一する気持ちになった。

sosuke:

会社のエンジニアとしては設定を書きたいわけではなく、ユーザーに価値を届けたいので、toolchainの統合は嬉しい。

なぜJavaScriptのRust化が進んでいるのか

Boshen:

toolchainという文脈で言えないが、そもそもJavaScriptで書いているのが間違っていると思っている。昔は小さいものをJavaScriptで書いていたが今は巨大になり過ぎてしまった。 さらに今はAIが出てきていても、その  には何も寄与していない。 会社のミッションとしても実行時間も減らすことができればメリットが大きい。

evan you:

JavaScriptもエンジニアが書くしかなかったから、必ずしも間違いとは言えない。 このプロセスは悪いことではない。 柔軟でよりコード自体も簡単だった それだけでなく、思想や設計とか決議を決めることができた。 他のプログラミング言語の欠点を補いかつ高速なので、合理的な判断までできる。 ただ課題もある。

o:

oxcがなかったらどうしていたのか、別のものを使うつもりだったのか?

evan you:

oxcがなかったら、swcでなんとかやっていたと思う。

これまでとこれからのJavaScriptエコシステム

pluginを加えればオーバーヘッドがかかる。
→ これを解決するアイディアがある。

  1. 互換性100%を石すること(Rollup)
  2. pluginを一部だけ使えるようにする
  3. もっと大きい有名なコア部分に
  4. JavaScriptで書く必要のない部分をサジェストさせる。

ASTの共有化 これはVoidZeroの仕事。 これを共通化することで、JavaScriptをすべて実行時になってから処理する必要がなくなるので、パフォーマンスブーストが多いと思っている。 JavaScriptの並列化 woekerを並列処理させる。 既存のものがRustに置き換わっている。

s:

言語が変わることによってコントリビュートが減るなどの影響はないと思っている。やる人はやるし、やらない人はやらない。 Rustやりたいからフロントのツールチェインに来る人もいる。

Demystifying_Vite_Internals

by Nozomu Ikuta in MNTSQが全ての合意をフェアにするぞトラック at 15:50 - 16:20

speakerdeck.com

Deep_dive_into_Nuxt_Server_Components

by wattanx in MNTSQが全ての合意をフェアにするぞトラック at 16:30 - 17:00

speakerdeck.com

終わりに

最後までお読みいただきありがとうございました。 ノート部分は、あくまで私が記載したものなので、間違っていることを記載している可能性があります。なので話半分で読んでいただければと思います。

FormKitコンポーネントをStorybookに表示させる方法

目次

おはこんにちばんわ。

今回は、Vue.jsのフォームライブラリであるFormKitをStorybookで使う(表示させる)方法を共有させていただきます。

formkit.com

storybook.js.org

内容は非常に簡単なものですが、調べても公式ドキュメントにも無く、ちょっとだけ詰まったので。。。

追記)この辺りを見れば分かったのかな。。。

※ FormKitやStorybookについての詳しい説明は割愛させていただきます。

環境と使用ライブラリ、フレームワーク

  • macOS Sequoia: 15.0
  • Google Chrome: 129.0.6668.58
  • nuxt: ^3.13.0
  • vue: ~3.5.6
  • formkit/nuxt: ^1.6.7
  • @storybook/vue3: ^8.3.2
  • @storybook/vue3-vite: ^8.3.2

設定方法

いきなり結論ですが、以下のStorybookの(デフォルト設定を変えていなければ)設定ファイルなどが置かれている.storybook/preview.tsに追記を行いました。

// .storybook/preview.ts

import type { Preview } from '@storybook/vue3';
import { setup } from '@storybook/vue3';
import { plugin, defaultConfig } from '@formkit/vue';

+ setup((app) => {
+   app.use(plugin, defaultConfig());
+ });

const preview: Preview = {
    // ~~~関係無いので省略
};

export default preview;

これだけの設定で、StorybookにFormKitコンポーネントを表示することができました。

ただ、もう少し発展させると、FormKitのconfigはStorybookだけに設定されるものではありません。
もちろんVueコンポーネントで配置されるFormKitコンポーネントにも同様の設定が適用されることが正しいことになります。
なので、ルートディレクトリに共通して持たせておいた方が良いかと思いましたので、もう少し手を加えたのが、以下になります。

// .storybook/preview.ts

import type { Preview } from '@storybook/vue3';
import { setup } from '@storybook/vue3';
import { plugin } from '@formkit/vue';
import config from '../formkit.config.mjs';

+ setup((app) => {
+   app.use(plugin, config;
+ });

const preview: Preview = {
    // ~~~ 関係無いので省略
};

export default preview;
// formkit.config.mjs

import { defaultConfig } from '@formkit/vue';

export default defaultConfig({});
// nuxt.config.ts

// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
    // ~~~ 関係無いので省略
    modules: ['@nuxt/eslint', '@formkit/nuxt'],
        formkit: {
            autoImport: true,
            defaultConfig: false,
        },
    }
    // ~~~ 関係無いので省略
);

やってることとしては、結局defaultConfigを使っているのですが、こうすることで、Storybook側のConfigとソースコンポーネント側のconfigを合わせて使うことができるかと思います。
設定を追加したくなったら、formkit.config.mjsだけを修正すれば良いわけですね!

終わりに

ということで、FormKitコンポーネントをStorybookに表示させる方法でした。

Nuxt.jsを使った時のサードパーティ製のライブラリをStorybookに導入しなければならないとき、ドキュメントとかに導入方法が記載されていなけば、このような方法でうまく導入することができるんかなー、と考えました。

最後までお読みいただき、ありがとうございました。

音楽メタ情報 - ID3v2、VorbisComment、Windows Media Metadata、id3チャンク、APEv2 -

目次

調べたものとは?

今回調べたのは、音楽ファイルのタグ情報がどのように保管されているのかを調べました。

調査する音楽ファイルは、私が普段使っている音楽ファイル形式や知っているものを詳しく調べました。

そして、それをどのように表示できるのかを、JavaScript(TypeScript)などを用いて実装していきたいと思います。

音楽ファイルに対応するタグ情報の種類

以下に、音楽ファイルのタグ情報をまとめました。

タグ名 使われている音楽ファイル形式
(拡張子)
ID3v2 MP3(.mp3), ATRAC(.oma), AAC(.aac, mp4), FLAC(.flac)
VorbisComment FLAC(.flac), OggVorbis(.ogg)
Windows Media Metadata WMA(.wma)
id3チャンク AIFF(.aiff), WAVE(.wav)
APEv2 AAC(.aac, mp4), MP3(.mp3)

※「使われている音楽ファイル形式」は、そのタグでのみ使われているわけではないので注意してください。

各タグ情報はどのような規格となっているのか見ていきます。

タグ情報について

ID3v2

ID3にはいくつかバージョンがありますが、現在最も普及している「ID3v2.3」について記載します。

ID3v2は、ID3v1.1を拡張、改良した形式です。ID3v1のネックだった字数制限が無くなり、Unicodeのサポートがされています。その代わり、タグを記録するときにファイルサイズが大きくなってしまったり、真ジャーバージョン間での前方互換性が保証されていないので、古いプレーヤーでは表示に対応していない場合があります。

ID3v2ヘッダにおける数値表現は、ビッグエンディアン(MSB first)です。

仕様

ID3v2タグは、ファイルの先頭に配置されており、大まかに以下の4領域から構成されています。

ID3v2 Tag

フィールド名 オフセット 長さ 種類 説明
ヘッダ拡張子 0 3 文字列 "ID3"(54h, 41h, 47h)
バージョン 3 2 バイナリ タグのバージョンを表す。
e.g. 03h, 00h --> 2.3.0
フラグ 5 1 フラグ BIt7: 非同期化(通常0)
BIt6: 拡張ヘッダ有
BIt5: 実験中(通常0)
BIt4: (v2.4) ID3v2 フッタ有
サイズ 6 4 Syncsafe
Integer
ID3v2ヘッダ以降のID3v2タグのサイズを示す。
注:各ビットは下位7 bitのみが有効。
全体として 7×4=28 bitの整数を表す。

SyncsafeInteger

読み込み処理は、以下の例のように行います。

uint32_t val = (((uint8_t)p[0])<<21) + (((uint8_t)p[1])<<14) + (((uint8_t)p[2])<<7) + (uint8_t)p[3];

このようなID3タグで使用される数値表現をSyncsafe Integerと呼びます。

ID3v2.3以前では、Header部分にのみSyncsafe形式が用いられますが、v2.4ではほとんどの4 Byte数値にこのSyncsafe表現を使用することになります。

ExtendedHeader

フィールド名 オフセット 長さ 種類 説明
サイズ 0 4 (v2.3) 32 bit 数値
(v2.4) Syncsafe 数値
拡張ヘッダのサイズを示す。
(v2.4)注:各ビットは下位7 bitのみが有効。
拡張フラグなど 4 > 2 ? 使わなくても良い。
(ID3v2.3とID3v2.4で異なる)

Frame

フレームは、最低でも1つ存在しなければなりません。また、すべてのフレームはID3v2フレームヘッダ部分を除き最低1 Byteのデータが存在しなければなりません。

フィールド名 オフセット 長さ 種類 説明
フレームID 0 4 テキスト フレーム内容を表す4文字のID
サイズ 4 4 (v2.3) 32 bit 数値
(v2.4) Syncsafe 数値
フレームヘッダを除いたフレームサイズを示す。
(v2.4) 注:各bitは下位7 bitのみが有効
frameSize = size + 10
フラグ 8 2 フラグ 使わなくても良い

各フレームが何を表しているのかは、4文字のフレームIDによって決定されます。

フレームID参照

ja.wikipedia.org

フレーム本体は、ヘッダで指定されたサイズ分のブロックになります。フレームサイズの数値表現はバージョンによって異なるので、注意が必要になります。

連続するフレームの終了は、フレームヘッダのフレームIDが有効な文字(A-Z, 0-9)のみで構成されているかどうかによって確認できます。

フレームIDは多数存在しますが、このうち使われるフレームIDはごく僅かです。 ID3v1タグで定義されたタグは、ID3v2では以下の通りに対応します。ID3v1タグで数字によって表現されていたタグはID3v2では可変長の文字列によって表現されます。

名前 ID3v1 ID3v2.2 ID3v2.3
曲名 O TT2 TIT2
アーティスト名 O TPI TPE1
アルバム名 O TAL TALB
O TYE TYER
コメント O COM COMM
トラック O TRK TRCK
ジャンル O TCO TCON

フレームでの文字列表現

テキストを表現する各フレームは、下表のようにテキストエンコーディングを示す1バイトとフレーム内容を示すNULL終端テキストからなります。ただし、"COMM"や"SYLT"などの一部タグは下表とは形式が異なります。

フィールド名 オフセット 長さ 種類 説明
エンコーディング 0 1 バイナリ テキストのエンコーディングを表す。
00h: ISO-8859-1
01h: UTF-16 / BOMあり
(ID3v2.4) 02h: UTF-16BE / BOMなし
(ID3v2.4) 03h: UTF-8
フレーム内容 1 7 NULL終端テキスト フレームIDで指定された内容を表すテキスト。
終端文字: "00h" (ISO-8859-1, UTF-8)
"00h 00h"(UTF-16)

上記のように、ID3v2タグではShift-JISのエンコーディングは規定されていません。ISO-8859-1はいわゆる英数字のみを規定したエンコーディングです。したがって、日本語などマルチバイト文字を使う場合はUnicodeを用いるべきであり、Shift-JISを使ってタグをつけることはフォーマット違反になります。 しかしながら、大多数のソフトウェアではエンコーディングフィールドwp00hに設定した上でShift-JISタグでタグ付けを行うことを許容しています。

XINGHeader

楽曲の長さの情報は、ID3タグには存在しません。 これは、MPEGフレーム数、もしくはビットレートとファイルサイズによってのみ規定されるからです。曲長の計算式を以下に示します。

① 曲長 [sec] = MPEG フレーム数 * ( 1152 / サンプリングレート[Hz] )

② 曲長 [sec] = 8 * (ファイルサイズ - タグ) [byte] / ビットレート [bps]

MPEGフレーム数はこれまでに述べたタグから得られます。

VBR(Variable Bit Rate)形式のMP3ファイルにおいては、全体としてのビットレート情報が存在しないため、正確な曲長を得るためにはファイルに存在する全てのMPEGフレームを走査する必要があります。これは時間がかかりすぎるので、考えられたのがXINGヘッダです。サンプリングレートはファイル内で変わらないので、上記の計算式①を適用して、XINGヘッダ内の総フレーム数と適当なMPEGフレームヘッダ内のサンプリングレートを使用すれば、楽曲の曲長が求まります。

XING ヘッダは "Xing"の4バイトから始まる、最初のMPEGオーディオフレームのオフセット32バイト(ステレオ)または17バイト(モノラル) から続く、120バイトの領域です。もちろん CBR でエンコードされたMP3ファイルには存在しません。

フィールド名 オフセット(byte) 長さ(byte) 説明
ヘッダ識別子 0 4 "Xing"
フラグ 4 4 Bit 1(00 00 00 01): フレーム数が有効
Bit 2: ファイルサイズが有効
Bit 3: TOCエントリが有効
Bit 4: Quality Indicatorが有効
フレーム数 8 4 このMPEGフレームを含む、総フレーム数
ファイルサイズ [byte] 12(標準) 4 このファイルの長さ
TOCエントリ 16(標準) 100 100個のTOCエントリ(テキスト参照)
Quality Indicator 116(標準) 4 品質を示す(あまり使われない)

フレーム数などフィールドの有効・無効はフラグによって指定されます。もしあるフィールドが無効な場合、そのフィールドはパディングされるのではなく、それ以降のフィールドが前倒しされて配置されます。つまり、フレーム数が無効な場合、ファイルサイズのオフセットは 8 となります。

もうひとつの重要なデータはTOCエントリ(Table Of Contents)です。このTOCエントリはVBRファイルのシークに利用します。具体的には、ファイルの x [%] の時間位置にシークするとき、ファイルポインタを次式

新しいファイル位置 = (ファイルサイズ * TOC[x]) / 256

にセットすることで、時間ベースのシークが可能になります。

MPEGフレームヘッダ

前述の通り、MP3 ファイルの基本構造はこのMPEG フレームヘッダです。このヘッダ情報を解析することで、ファイル全体の楽曲長さやシーク位置などが計算できます。

MPEGオーディオフレームヘッダは各MPEGオーディオフレームの先頭32bit(4 byte)を占めるヘッダ領域で、デコードに必要な情報を与えます。1つのMPEGフレームのサイズは次の計算式によって求まります(MPEG 1 Layer III の場合)。

フレームサイズ [byte] = 144 * ビットレート[bps] / サンプリングレート[Hz]
フィールド名 オフセット(bit) 長さ(bit) 説明
フレーム同期 0 11 全て1
バージョンID 11 2 11
レイヤー 13 2 01
保護ビット 15 1 1
ビットレート 16 4 0000 - 1111 :
{free, 32, 40, 48, 56, 64, 80, 96,
112, 128, 160, 192, 224, 256, 320, 予約}
サンプリングレート 20 2 00: 44100
01: 48000
10: 32000
11: 予約
パディングビット 22 1 0: パディングしない
1: パディングする(フレームサイズに +1 byteする)
チャンネル 24 2 00: ステレオ
01: ジョイントステレオ
10: デュアルチャンネル
11: モノラル
以下省略 26 6 省略

参考

ja.wikipedia.org

eleken.y-lab.org

VorbisComment

仕様

FLACファイルの場合、以下のような領域で構成されています。

!FLAC-tag.svg

詳細な構造については、以下の参考やFLAC仕様を日本語に訳した私のはてブを見てみてください。 FLACの音楽情報は、METADATA_BLOCK_VORBIS_COMMENT領域に保管されています(ちなみに、METADATA_BLOCK_PICTUREはアルバムアート情報が保管されています)。

このMETADATA_BLOCK_VORBIS_COMMENT領域は、以下のように構成されています。

!VORBIS_COMMENT.svg

この「コメントM」に、"Artist=hogehoge"といった文字列がUTF-8で保管されています。

このメタデータでは、バイト数やコメント長はリトルエンディアンで格納されていることに注意が必要です。一方で、その他の箇所はビッグエンディアンで格納されています。

参考

xiph.org

kostum.hatenablog.jp

dededemio.hatenablog.jp

www.xiph.org

Windows-Media-Metadata

マイクロソフトが開発したファイル形式にASF(Advanced Systems Format)ファイルというものがあります。このファイルはWindows Media Audio(WMA)やWindows Media Video(WMV)の標準ファイル形式となっており、音楽メタデータもこの中に保管されています。

色々と仕様書などを探してみましたが、どのように取得できるのかを把握できる資料が見つかりませんでした。。(最上位のASFファイルの構造は記載されていたのですが、それ以下の構成がわかる資料が見つかりませんでした。。)

そこで、JavaScriptライブラリであるmusic-metadataを見てみました。

github.com

その中のasf parserを担う処理を見てみると、(情報はかなり古いですが)構成がわかる資料がありました。

drang.s4.xrea.com

それによると、ASFファイルは以下のような構成になっています。

ASF Object

各Objectの詳細はこちらを参考にしてください。

drang.s4.xrea.com

音楽情報は、Content Description Object, Extended Content Description Objectに格納されています。

Content Description Object

Field name Field type Size (bits)
Object ID GUID 128
Object Size QWORD 64
Title Length WORD 16
Author Length WORD 16
Copyright Length WORD 16
Description Length WORD 16
Rating Length WORD 16
Title WCHAR Varies
Author WCHAR Varies
Copyright WCHAR Varies
Description WCHAR Varies
Rating WCHAR Varies

Extended Content Description Object

Field name Field type Size (bits)
Object ID GUID 128
Object Size QWORD 64
Content Descriptors Count WORD 16
Content Descriptors See text varies

Content Descriptors

Field Name Field Type Size (bits)
Descriptor Name Length WORD 16
Descriptor Name WCHAR varies
Descriptor Value Data Type WORD 16
Descriptor Value Length WORD 16
Descriptor Value See text varies

以下、各Objectの簡単な説明です。

Header-Object

Header Objectは、以下のセクションに列挙されている標準オブジェクトの任意の組み合わせを保持するコンテナです。File Properties Object、Header Extension Object、および少なくとも1つの Stream Properties Objectが存在することが必須です。

Header Objectは以下の構造で表されます。

Field name Field type Size (bits)
Object ID GUID 128
Object Size QWORD 64
Number of Header Objects DWORD 32
Reserved1 BYTE 8
Reserved2 BYTE 8

File-Properties-Object

グローバルファイル属性を含みます。 durationとbitrateが取得できます。

Stream-Properties-Object

デジタルメディアストリームやそれらの特性を定義しています。

Header-Extension-Object

後方互換性を維持しながら、ASFファイルに機能を追加することができます。

Content-Description-Object

書誌情報を含みます。

Script-Command-Object

再生タイムライン上で実行できるコマンドを含みます。

Maker-Object

ファイル内の名前付きジャンプポイントを提供します。

Data-Object

このオブジェクトには、ASF ファイルのすべてのデジタルメディアデータが含まれます。このデータは、Data Packetsの形で格納され、固定長で格納されます。各Data Packetsには、1つまたは複数のデジタルメディアストリームのデータが含まれます。Data Packetsは、配信されるべき時間(送信時間)に基づいて、Data Object内でソートされます。このソートにより、インターリーブされたデータ形式となります。

Index-Object

Simple Index ObjectとIndex Objectがあります。 Simple Index Objectは、ASFファイル内のビデオデータの時間ベースのインデックスを格納します。Indexエントリ間の時間間隔は一定で、Simple Index Objectに格納されます。ASFファイル内の各ビデオストリームに対して、Simple Index Objectのインスタンスが1つ存在する必要があります。これらのインスタンスがファイルに現れる順序は重要です。Simple Index Objectの順序は、ストリーム番号に基づくビデオストリームの順序と同一でなければなりません。これらのオブジェクトは、ファイル内の最後のトップレベルオブジェクトである必要があります。

Index Object というカテゴリは、Index Object、Media Object Index Object、Timecode Index Object を指し、それらのフォーマットはすべて類似しています。Index Objectは、Simple Index Objectと同様に、固定された時間間隔を持つ時間によってインデックスを付けますが、ビデオストリームに限定されません。Media Object Index Object はフレームベースのインデックスであり、フレーム(またはオブジェクト番号)によるシークを容易にします。Timecode Index Object は、タイムコードを含むコンテンツにおいて、タイムコードによるシークを容易にします。

参考

ja.wikipedia.org

learn.microsoft.com

learn.microsoft.com

id3チャンク

id3チャンクは、ID3v2タグを丸ごとid3チャンクという新たに規定したチャンク(データブロック)内に収めたものです。仕様上WAVでは、ID3v2タグはそのままでは使えません(WAVファイルは冒頭に「RIFF」という文字列が必要ですが、ID3v2は「ID3」という文字列が必要になるからです)。

APEv2

APEv2は、以下のような領域で構成されています。

APEv2 tag

APE-Tags-Header/Footer

Preamble { ‘A’, ‘P’, ‘E’, ‘T’, ‘A’, ‘G’, ‘E’, ‘X’ }
Version Number, Bits 0...7

Version Number, Bits 8...15

Version Number, Bits 16...23

Version Number, Bits 24...31
1000 = Version 1.000 (old) 2000 = Version 2.000 (new)
Tag Size, Bits 0... 7

Tag Size, Bits 8...15

Tag Size, Bits 16...23

Tag Size, Bits 24...31
APEタグ1.000と可能な限り互換性があるように、
フッターとヘッダーを除くすべてのタグ項目を含む
タグのサイズをバイト単位で表示
Item Count, Bits 0... 7

Item Count, Bits 8...15

Item Count, Bits 16...23

Item Count, Bits 24...31
タグのアイテム数 (n)
Tags Flags, Bits 0... 7

Tags Flags, Bits 8...15

Tags Flags, Bits 16...23

Tags Flags, Bits 24...31
全アイテムのグローバルフラッグ
(各アイテムのプライベートフラッグもある)
Reserved Must be zero

Ape-Tags-Flags

タグ(ビット31...)とアイテム(ビット0...)の属性を含みます。

APEタグのヘッダー、フッター、またはタグアイテムのメンバーです。

注意:APEタグ1.0はAPEタグフラグを使用しません。作成時にはすべて0に設定され、読み取り時には無視されます。

Bit 31

  • 0: タグにヘッダーがない
  • 1: タグにヘッダーが含まれる

Bit 30

  • 0: タグにフッターが含まれる
  • 1:タグにフッターがない

Bit 29

  • 0: これはフッターであり、ヘッダーではない
  • 1: これはヘッダーであり、フッターではない

Bit 28...3

  • 未定義、ゼロでなければならない

Bit ビット2...1

  • 0: 項目は UTF-8 でコード化されたテキスト情報を含む
  • 1: 項目はバイナリ情報を含む*
  • 2: 項目は外部保存情報のロケータである**。
  • 3: 予約済み

Bit ビット 0

  • 0: タグまたは項目はRead/Write
  • 1: タグまたは項目はReadOnly

[*]バイナリ情報:テキストエディタで編集してはいけない情報です。なぜなら、以下のりゆがあるからです。

  • 情報がテキストではない。
  • 制御文字を含む
  • 通常のテキストエディタでは扱えない内部制約を含む
  • 人間が簡単に解釈できない

[**] 許容されるフォーマット

注:locatorもUTF-8エンコードされます。これは特にファイル名がエンコードされている場合に起こりえます。

APE-Tag-Item

APE Tag Itemは、Keyによって割り当てられた値です。

APEタグバージョン2.0のメンバーです。

注意:

  • APE Tagのアイテムキーは大文字と小文字を区別します。
    • しかし、大文字と小文字が異なるだけのAPE Tag Item Keyを使用することは禁じられています。
    • また、Tag Readerは大文字と小文字を区別しないことを推奨します。
  • すべてのTag Item Keyは(最大でも)一度しか使用できません。
  • Tagは、ストリーミング形式で部分的または完全に繰り返すことができます。
    • これは、送信開始タイミングを逃しても、アーティスト名やタイトルを表示できるようにするためです。アーティスト/アルバム/タイトルなどの非常に重要な情報は、2分おきに送信し、さらに終了5~10秒前に送信することをお勧めします。不必要なドロップアウトを避けるため、これらの情報を頻繁に送信したり、ビットレート要求の高い間に送信しないように注意してください。
Size of the Item Value, Bits 0...7

Size of the Item Value, Bits 8...15

Size of the Item Value, Bits 16...23

Size of the Item Value, Bits 24...31
割り当てられた値の長さlen(バイト単位)
tem Flags, Bits 0...7

Item Flags, Bits 8...15

Item Flags, Bits 16...23

Item Flags, Bits 24...31
Item flags
Item Key Item Key。0x20(スペース)から0x7E(チルダ)までのASCII文字を含むことができる。
0x00 Item Key の末端
Item Value Item Value。バイナリデータまたはUTF-8文字列。

APE-Key

  • APE tag Item Keyは、オーディオファイル内の特別なメタ情報にアクセスするためのKeyです。
  • APE Tag Itemのメンバー。
  • APE Tag Item Keyは、0x20(スペース)から0x7E(チルダ)までの範囲で、2文字(を含む)から255文字(を含む)までの長さを持つことができます。
  • 一般的なKeyの長さは2~16文字です。
    • 16文字: スペース(0x20)
    • スラッシュ(0x2F)
    • 数字(0x30~0x39)
    • 文字(0x41~0x5A、0x61~0x7A)
  • 値には、バイナリデータ、値、または値のリストを含めることができます。
    • 現在定義されているKey名は、こちらを参照してください。
    • 値のリストには、UTF-8文字列と、file://...、http://www...ftp://ftp...で始まる外部参照を混在させることができます。
    • 以下のキーは使用できません: ID3、TAG、OggS、MP+。

参考

reika788.blog.fc2.com

reika788.blog.fc2.com

reika788.blog.fc2.com

github.com

終わり

ということで、(おそらく)主要な音楽メタ情報の構成について調べました。 次は、この仕様情報からJavaScriptを使用して音楽情報を表示してみます。

FLACの仕様について調べた

目次

調べたもの

オーディオメタフォーマットであるFLACの仕様ドキュメントを日本語に翻訳しました。

xiph.org

FLACとは

FLACとは、Free Lossless Audio Codecの略で、MP3に似た音声フォーマットですが、Lossless(非可逆圧縮)であるため、音質の劣化がなく音声を圧縮することができます。これはZipが機能する仕組みに似ていますが、FLACは音声専用に設計されているため、はるかに優れた圧縮を実現します。さらに、圧縮されたFLACファイルを、MP3ファイルと同様にお気に入りのプレーヤーや(車や家庭用ステレオなどの対応デバイスで)再生することができます。

FLACは、最も速く、最も広くサポートされているロスレスオーディオコーデックであり、非独占的であり、特許に縛られず、オープンソースのリファレンス実装があり、フォーマットとAPIがよく文書化されており、他にもいくつかの独立した実装が存在する唯一のコーデックです。

詳しくは「About FLAC」をご覧ください。また、「Using FLAC」では、FLACファイルの再生方法や、CDをFLACリッピングする方法などについて説明しています。

Features

FLACは、以下に対応しています。

  • タグ付け
  • カバーアート
  • 迅速なシーク

FLACは無料で利用可能であり、WindowsunixLinuxBSDSolarisOS X、IRIX)、BeOSOS/2、およびAmigaなど、ほとんどのオペレーティングシステムでサポートされています。

FLACをサポートするプログラムやデバイスは多数あります。ここで紹介するFLACのコアプロジェクトでは、フォーマットを維持し、FLACファイルを操作するためのプログラムやライブラリを提供しています。公式のFLACツールのダウンロードとインストールの手順については「Getting FLAC」を参照してください。

FLACが「Free(フリー)」であるというとき、それは単に無料で利用できるという意味以上のものを含んでいます。FLACのフォーマット仕様は、あらゆる目的で利用できるように完全に公開されており(FLACプロジェクトはFLAC仕様を設定し、準拠性を認証する権利を留保しています)、FLACフォーマットや実装されたエンコード/デコードの方法は、既知の特許に基づくものではありません。また、すべてのソースコードオープンソースライセンスの下で利用可能です(詳細はライセンスページを参照してください)。

FLACの主な特徴:

  • ロスレス:音声(PCM)データのエンコードには情報の損失がなく、デコードされた音声はエンコーダーに入力されたものとビット単位で同一です。各フレームには伝送エラーを検出するための16ビットCRCが含まれています。音声データの整合性は、ファイルヘッダーに元の非エンコード音声データのMD5署名を格納することでさらに保証され、デコードやテスト時にこれを比較することができます。
  • 高速FLACはデコード速度を重視する非対称型であり、デコードには整数演算のみを使用し、ほとんどの知覚コーデックよりも計算負荷がはるかに少ないです。リアルタイムのデコード性能は、控えめなハードウェアでも容易に達成できます。
  • ハードウェア対応FLACは、携帯プレーヤーから家庭用ステレオ機器、車載ステレオまで、数十種類の民生用電子機器でサポートされています。
  • 柔軟なメタデータFLACメタデータシステムは、タグ、カバーアート、シークテーブル、キューシートに対応しています。アプリケーションはIDを登録することで独自のAPPLICATIONメタデータを書き込むことができます。新しいメタデータブロックは、古いストリームやデコーダーを壊すことなく、FLACの将来のバージョンで定義および実装することができます。
  • シーク可能FLACは高速でサンプル精度の高いシークに対応しています。これは再生に役立つだけでなく、FLACファイルを編集アプリケーションで使用するのにも適しています。
  • ストリーム可能:各FLACフレームには、そのフレームをデコードするのに十分なデータが含まれています。FLACは前後のフレームに依存しません。FLACは、シンクコードとCRCMPEGなど他のフォーマットと類似)を使用し、フレーミングとともに、ストリームの途中から最小限の遅延でデコーダーが再開できるようにします。
  • アーカイブに適しているFLACはオープンフォーマットであり、将来別のフォーマットにデータを変換する必要があっても世代劣化がありません。フレームCRCMD5署名に加えて、エンコードプロセスと並行してエンコードされたストリームをデコードし、元の音声と比較して不一致があればエラーで中止する「verify」オプションもあります。
  • 便利なCDアーカイブFLACには、CDの目次とすべてのトラックとインデックスポイントを格納するための「キューシート」メタデータブロックがあります。たとえば、CDを1つのファイルにリッピングし、エンコード中に抽出されたキューシートをインポートして、CD全体を表す1つのファイルを作成することができます。元のCDが破損している場合は、後でキューシートをエクスポートして正確なコピーを焼くことができます。
  • エラー耐性FLACフレーミングのおかげで、ストリームエラーは発生したフレームのダメージに限定され、通常はデータのほんのわずかの部分(1秒未満)に影響します。これに対し、他の一部のロスレスコーデックでは、1つのエラーがストリームの残りを破壊する可能性があります。

FLACではないもの:

  • ロッシーFLACロスレス圧縮専用であり、すでにVorbis、MPC、MP3などの優れたロッシーフォーマットが多数存在します(優れたオープンソース実装であるLAMEを参照してください)。
  • DRM:コピー防止方法を追加する意図はありません。もちろん、他のコンテナでFLACストリームを暗号化すること(たとえば、AppleAACをMP4でFairPlayで暗号化する方法)は止められませんが、それはユーザーの選択です。

FormatOverview

FLACストリームの基本構造は以下の通りです:

最初の4バイトはFLACストリームを識別するためのものです。その後に続くメタデータは、オーディオデータ自体を除くすべてのストリーム情報を含んでいます。メタデータの後にはエンコードされたオーディオデータが続きます。

メタデータ

FLACは、いくつかの種類のメタデータブロックを定義しています(完全なリストについてはフォーマットページを参照してください)。メタデータブロックは任意の長さにでき、新しいものも定義できます。デコーダーは理解できないメタデータの種類をスキップすることが許可されています。必須のブロックは1つだけで、それがSTREAMINFOブロックです。このブロックにはサンプルレート、チャンネル数などの情報や、デコーダーがバッファを管理するのに役立つ最小・最大データレートや最小・最大ブロックサイズなどのデータが含まれています。また、STREAMINFOブロックには、非エンコード音声データのMD5署名も含まれており、これはストリーム全体の伝送エラーをチェックするのに役立ちます。

その他のブロックでは、パディング、シークテーブル、タグ、キューシート、およびアプリケーション固有のデータを格納することができます。FLACには、PADDINGブロックを追加したり、シークポイントを指定したりするオプションがあります。シークにはシークポイントは必要ありませんが、シークの速度を上げたり、編集アプリケーションでのキューイングに使用したりできます。

また、カスタムメタデータブロックが必要な場合は、自分で定義し、IDをここでリクエストすることができます。その後、エンコード時に正しいサイズのPADDINGブロックを予約し、エンコード後にAPPLICATIONブロックでパディングブロックを上書きします。この結果得られるストリームはFLAC互換であり、あなたのメタデータを認識しているデコーダーはそれを利用し、それ以外のデコーダーは安全に無視します。

オーディオデータ

メタデータの後にエンコードされたオーディオデータが続きます。オーディオデータとメタデータはインターリーブされていません。ほとんどのオーディオコーデックと同様に、FLACは非エンコードのオーディオデータをブロックに分割し、各ブロックを個別にエンコードします。エンコードされたブロックはフレームにパックされ、ストリームに追加されます。リファレンスエンコーダーはストリーム全体に対して単一のブロックサイズを使用しますが、FLACフォーマットはこれを必須とはしていません。

ブロッキング

ブロックサイズはエンコードにおける重要なパラメータです。サイズが小さすぎると、フレームのオーバーヘッドが圧縮率を下げてしまいます。サイズが大きすぎると、コンプレッサーのモデリングステージが効率的なモデルを生成できなくなります。FLACモデリングを理解することで、入力の種類に応じてブロックサイズを変えることで圧縮率を向上させることができます。一般的な場合、44.1kHzオーディオに線形予測を使用する場合、最適なブロックサイズは2~6kサンプルの間になります。この場合、flacはデフォルトで4096のブロックサイズを使用します。高速固定予測子を使用する場合は、フレームヘッダーが小さいため、通常は小さいブロックサイズが好ましいです。

インターチャンネルデコリレーション

ステレオ入力の場合、データがブロック化された後、オプションでインターチャンネルデコリレーションステージを通過します。 左チャネルと右チャネルは、以下の変換によってセンターとサイドチャネルに変換されます:

mid = (left + right) / 2

side = left - right

これは、ジョイントステレオとは異なり、ロスレスなプロセスです。通常のCDオーディオでは、これにより大幅な追加圧縮が可能です。flacには、-mオプション(ブロックの左右バージョンとミッドサイドバージョンの両方を常に圧縮し、最小のフレームを取る)と、-Mオプション(左右とミッドサイドを適応的に切り替える)の2つのオプションがあります。

モデリング

次のステージでは、エンコーダーが信号を関数で近似し、その近似を差し引いた結果(残差、残留、エラーと呼ばれる)がエンコードするのに必要なビット数が少なくなるようにします。関数のパラメータも送信しなければならないため、節約を食い潰さないように複雑すぎないものである必要があります。 FLACには、近似を形成するための2つの方法があります: 1. 信号に単純な多項式をフィッティングすること 2. 一般的な線形予測符号(LPC)。ここでは詳細には触れませんが、エンコードオプションに関する一般的な内容をいくつか説明します。

まず、固定多項式予測(-l 0で指定)ははるかに高速ですが、LPCよりも精度が低くなります。LPCの最大次数が高いほど、モデルは遅くなりますが、より正確になります。ただし、次数が増えるにつれてその効果は減少します。また、通常は次数9付近でエンコーダーが使用するべき最適な次数を見誤り、圧縮率が若干低下し始めるため、その場合は、非常に遅くなりますが、完全探索オプション-eを使用する必要があります。

次に、固定予測子のパラメータは3ビットで送信できますが、LPCモデルのパラメータはビットパーサンプルとLPC次数に依存します。これにより、使用する方法と次数に応じてフレームヘッダーの長さが変わり、最適なブロックサイズに影響を与える可能性があります。

残留符号化

モデルが生成されると、エンコーダーは近似から元の信号を引いて残差(エラー)信号を得ます。このエラー信号はロスレスで符号化されます。FLACは、このエラー信号が一般的にラプラシアン(二項)分布を持つことを利用し、これらの信号を迅速に効率よくエンコードするための辞書を必要としない特殊なハフマンコード、ライスコードと呼ばれる一連のコードを使用します。

ライス符号化は、信号の分布に一致する単一のパラメータを見つけ、そのパラメータを使用してコードを生成することを伴います。分布が変化すると、最適なパラメータも変化するため、FLACはパラメータが必要に応じて変化するメソッドをサポートしています。残差は複数のコンテキストやパーティションに分割でき、それぞれにライスパラメータが設定されます。flacは、-rオプションを使用してパーティショニングを指定できます。残差は2^nパーティションに分割でき、-r n, nオプションを使用して指定します。nパラメータはパーティション順序と呼ばれます。さらに、-r m, nオプションを指定することで、mからnまでのパーティション順序を調べ、最適なものを取るようにエンコーダーを設定できます。一般的に、nの選択はエンコード速度に影響しませんが、m,nの選択は影響します。mとnの差が大きいほど、エンコーダーが最適な順序を探すのに時間がかかります。ブロックサイズも最適な順序に影響を与えます。

フレーミング

オーディオフレームの前にはフレームヘッダーがあり、その後にはフレームフッターが続きます。ヘッダーは同期コードで始まり、サンプルレート、ビットパーサンプルなど、ストリームを再生するためにデコーダーが必要とする最小限の情報を含んでいます。また、ブロック番号やサンプル番号、フレームヘッダーの8ビットCRCも含まれています。同期コード、フレームヘッダーCRC、およびブロック/サンプル番号により、シークポイントがない場合でも再同期とシークが可能です。フレームフッターには、エンコードされたフレーム全体の16ビットCRCが含まれ、エラー検出に使用されます。リファレンスデコーダーがCRCエラーを検出した場合、サイレントブロックを生成します。

その他

参考までに、リファレンスデコーダーはID3v2タグをスキップする方法を知っています。ただし、FLAC仕様では、準拠した実装にID3をサポートすることを要求していないため、その使用は強く推奨されていません。

flacには-Vオプションがあり、エンコード中に出力を検証します。このオプションでは、エンコーダーと並行してデコーダーが実行され、その出力が元の入力と比較されます。違いが見つかった場合、flacはエラーで停止します。

Format

FLAC形式に関するさまざまな側面について、ソフトウェア開発者の視点から説明します。つまり、FLACファイル内のどのビットやバイトがどのような情報を含んでいるのか、またそれらをどのようにエンコードまたはデコードすべきかについて説明します。ユーザー向けの概要については、「FLACフォーマットについて」をご覧ください。

FLAC形式およびそのコンテナフォーマットへのマッピングに関する説明は、以下のドキュメントに分かれています:

  • IETFのCELLAR作業部会によるFLAC仕様書の最新草案。このより公式な仕様は、ページの下部にある形式説明を改善したもので、無駄なビット、ステレオデコリレーションの使用によるサブフレームのビット深度への影響、ライスコーディングで使用される実際のシンボルの説明、さまざまなデコードの包含などの概念をよりよく説明しています。
  • FLAC-in-OggマッピングFLACOggコンテナにカプセル化される方法の説明。
  • FLAC-in-MP4マッピングFLACがMP4コンテナ(またはISOベースメディアファイル形式)にカプセル化される方法を説明するドキュメント。
  • Matroskaメディアコンテナコーデック仕様、FLACMatroskamkv)コンテナにカプセル化される方法を説明しています。

以下に、長年このページに掲載されており、上記のリストで最初に挙げたCELLAR作業部会の草案の基礎となったFLAC形式の説明を示します。詳細に欠ける部分があり、完全に理解するためにはlibFLACのソースコードで詳細を確認する必要がありますが、有用な概要および歴史的な参考資料として役立ちます。 デコーダーがここで説明されているFLAC形式のすべての機能を正しく実装しているかどうかをテストするための、FLAC形式の適合性テストファイルのセットが利用可能です。

範囲

すべての入力を無損失で圧縮できるアルゴリズムは存在しないという事実が知られています。したがって、ほとんどの圧縮器は有用なドメインに制限し、そのドメイン内でできるだけ効率的に動作しようとします。FLACドメインは音声データです。任意の入力を無損失でコード化できますが、特定の種類の入力のみが圧縮されます。FLACは、音声データが通常、サンプル間の相関が高いという事実を利用しています。

音声ドメイン内には多くのサブドメインが存在します。たとえば、低ビットレートの音声、高ビットレートのマルチチャネル音楽などです。FLAC自体は特定のサブドメインを対象としていませんが、リファレンスエンコーダーの多くのデフォルトパラメータは、CD品質の音楽データ(つまり44.1kHz、2チャネル、16ビット/サンプル)に調整されています。エンコードパラメータが異なる種類の音声データに与える影響については、後で説明します。

アーキテクチャ

多くの音声コーダーと同様に、FLACエンコーダーには次のステージがあります:

  • ブロッキング。入力が多数の連続したブロックに分割されます。FLACでは、ブロックのサイズは可変です。最適なブロックサイズは、サンプルレート、時間にわたるスペクトル特性など、多くの要因によって影響を受けます。FLACは、ストリーム内でブロックサイズが可変であることを許可していますが、リファレンスエンコーダーは固定ブロックサイズを使用します。
  • チャネル間デコリレーション。ステレオストリームの場合、エンコーダーは左チャネルと右チャネルの平均値および差分に基づいてミッドおよびサイド信号を生成します。その後、エンコーダーは信号の最適な形式を次のステージに渡します。
  • 予測。ブロックは予測ステージに渡され、エンコーダーは信号の数学的な説明(通常は近似)を見つけようとします。この説明は通常、元の信号自体よりもはるかに小さくなります。予測方法がエンコーダーデコーダーの両方に既知であるため、予測子のパラメータのみを圧縮ストリームに含める必要があります。FLACは現在、4つの異なるクラスの予測子を使用していますが(予測セクションで説明)、追加の方法を追加するためのスペースが予約されています。FLACは、ブロックごとに、またはブロックのチャネル内でも予測子のクラスを変更することができます。
  • 残差コーディング。予測子が信号を正確に説明しない場合、元の信号と予測信号の差分(エラーまたは残差信号と呼ばれる)を無損失でコード化する必要があります。予測子が効果的であれば、残差信号には元の信号よりもビット数が少なくて済みます。FLACは現在、残差をエンコードする方法として1つの方法しか使用していませんが(残差コーディングセクションを参照)、追加の方法を追加するためのスペースが予約されています。FLACは、ブロックごとに、またはブロックのチャネル内でも残差コーディング方法を変更することができます。

さらに、FLACにはメタデータシステムが指定されており、ストリームの開始時にストリームに関する任意の情報を含めることができます。

定義

多くのエンコーディングスキームでは、「ブロック」や「フレーム」などの用語が異なる意味で使用されています。たとえば、MP3のフレームは複数のチャンネルにわたる多数のサンプルに対応しますが、S/PDIFのフレームは各チャンネルに1つのサンプルを表します。FLACで使用される定義は以下の通りです。ブロックやサブブロックについて話すときは、エンコーダへの入力である生の未エンコードのオーディオデータを指し、フレームやサブフレームについて話すときは、FLACエンコードされたデータを指しています。

  • ブロック (Block): 複数のチャンネルにまたがる1つまたは複数のオーディオサンプル。
  • サブブロック (Subblock): チャンネル内の1つまたは複数のオーディオサンプル。したがって、ブロックは各チャンネルに1つのサブブロックを含み、すべてのサブブロックは同じ数のサンプルを含んでいます。
  • ブロックサイズ (Blocksize): ブロックのサブブロックの中にあるサンプルの数。たとえば、44.1KHzでサンプリングされた1秒間のブロックは、チャンネルの数に関係なくブロックサイズが44100になります。
  • フレーム (Frame): フレームヘッダーと1つ以上のサブフレームで構成されるもの。
  • サブフレーム (Subframe): サブフレームヘッダーと、特定のチャンネルからエンコードされた1つ以上のサンプルで構成されるもの。フレーム内のすべてのサブフレームは、同じ数のサンプルを含んでいます。

ブロッキング

オーディオデータをブロッキングする際に使用されるサイズは、圧縮率に直接影響を与えます。ブロックサイズが小さすぎると、多くのフレームが生成されるため、フレームヘッダーに余分なビットが浪費されます。逆にブロックサイズが大きすぎると、信号の特性が大きく変動する可能性があり、エンコーダが良い予測子を見つけることができなくなります。エンコーダ/デコーダの設計を簡素化するために、FLACは最小ブロックサイズを16サンプル、最大ブロックサイズを65535サンプルに制限しています。この範囲は、FLACがサポートするすべてのオーディオデータに対して最適なサイズをカバーしています。

現在のリファレンスエンコーダは、入力のサンプルレートに基づいて最適化された固定ブロックサイズを使用しています。将来のバージョンでは、信号の特性に応じてブロックサイズを変更する可能性があります。

ブロッキングされたデータは、サブブロック(チャンネル)ごとに予測ステージに渡されます。各サブブロックは独立してサブフレームにエンコードされ、サブフレームはフレームに連結されます。各チャンネルが別々にエンコードされるため、ステレオフレームの1つのチャンネルは定数サブフレームとしてエンコードされ、もう1つのチャンネルはLPCサブフレームとしてエンコードされることもあります。

チャンネル間のデコリレーション(Interchannel-Decorrelation)

ステレオストリームでは、左チャンネルと右チャンネルの間に利用可能な相関があることがよくあります。FLACは、ステレオストリームのフレームが異なるチャンネル割り当てを持つことを許可しており、エンコーダはフレームごとに最適な表現方法を選択できます。

  • 独立 (Independent): 左チャンネルと右チャンネルが独立してエンコードされます。
  • ミッドサイド (Mid-side): 左チャンネルと右チャンネルがミッドチャンネルとサイドチャンネルに変換されます。ミッドチャンネルは左信号と右信号の中点(平均)であり、サイドは差分信号(左 - 右)です。
  • 左サイド (Left-side): 左チャンネルとサイドチャンネルがエンコードされます。
  • 右サイド (Right-side): 右チャンネルとサイドチャンネルがエンコードされます。

驚くべきことに、左サイドと右サイドの形式は、多くのフレームにおいて最も効率的である場合があります。これは、元の信号に必要なビット数が独立エンコーディングやミッドサイドエンコーディングよりもわずかに多くなるにもかかわらずです。

予測 (Prediction)

FLACは入力信号のモデリングに4つの方法を使用しています。

  • 逐次エンコード (Verbatim): これは信号のゼロ次予測子です。予測された信号はゼロであり、残差は信号そのものであり、圧縮はゼロです。これは、他の予測子と比較するための基準です。エンコーダにランダムなデータを入力すると、逐次エンコードがすべてのサブブロックで使用される可能性が高いです。元の信号は実際には残差符号化ステージを通過しないため、エンコーディング結果はゼロ次の線形予測子と同じではありません。
  • 定数 (Constant): この予測子は、サブブロックが純粋なDC(デジタルサイレンス)、つまり常に一定の値を持つ場合に使用されます。信号はランレングスエンコードされ、ストリームに追加されます。
  • 固定線形予測子 (Fixed linear predictor): FLACは計算効率の良い固定線形予測子のクラスを使用しています(良い説明はaudiopakShortenを参照してください)。FLACは、Shortenで使用される0次から3次の予測子に加えて、4次の予測子を追加しています。予測子は固定されているため、圧縮ストリームに保存する必要があるのは予測子の順序のみです。エラー信号はその後、残差符号化器に渡されます。
  • FIR線形予測 (FIR Linear prediction): より正確なモデリングのために(エンコードが遅くなるコストがかかりますが)、FLACは最大32次のFIR線形予測をサポートしています(再び、線形予測に関する情報はaudiopakShortenを参照してください)。リファレンスエンコーダは、Levinson-Durbin法を使用して自己相関係数からLPC係数を計算し、その係数を量子化してから残差を計算します。Shortenのようなエンコーダが入力全体に対して固定量子化を使用していたのに対し、FLACはサブフレームごとに量子化された係数の精度を変えることができます。FLACリファレンスエンコーダは、ブロックサイズと元の信号のダイナミックレンジに基づいて使用する最適な精度を推定します。

残差符号化

FLACは現在、予測ステージからのエラー信号を符号化するために、2つの類似した方法を定義しています。エラー信号はライス符号を使用して次の2つの方法のいずれかで符号化されます。

1) エンコーダは残差の分散に基づいて1つのライスパラメータを推定し、このパラメータを使用して残差全体をライス符号化します。 2) 残差は連続するサンプルのいくつかの等長の領域に分割され、各領域はその領域の残差の分散に基づいて独自のライスパラメータを持ちます。

最初の方法は、1つの分割による2つ目の方法の特別なケースであり、ライスパラメータは分散ではなく平均に基づいています。

FLACフォーマットには、他の符号化方法のための予約領域があります。ボランティアにとっての可能性としては、ライスパラメータのより良いコンテキストモデリングやハフマン符号化の探求があります。複数のユニバーサル符号の説明については、LOCO-Ipucrunchを参照してください。

フォーマット

このセクションでは、FLACビットストリームのフォーマットを指定します。FLACにはバージョン情報はありませんが、いくつかの場所に予約スペースがあります。将来のフォーマットバージョンでは、この予約スペースを安全に使用することができ、古いストリームのフォーマットを壊すことはありません。古いデコーダは、新しい方法でエンコードされたデータのデコードを中止したり、スキップしたりすることができます。予約されたパターンとは別に、フォーマットでは無効なパターンが指定されている場所があります。これらのパターンは、以前のバージョン、現在のバージョン、および将来のバージョンのフォーマットのいずれにおいても、有効なビットストリームに現れることはありません。これらの無効なパターンは、同期メカニズムをより堅牢にするために使用されることがよくあります。

FLACビットストリームで使用されるすべての数値は整数であり、浮動小数点表現はありません。すべての数値はビッグエンディアンで符号化されます。特に指定されていない限り、すべての数値は符号なしです。

ストリームの正式な説明に入る前に、概要が役立つかもしれません。

  • FLACビットストリームは、ストリームの先頭にある「fLaC」マーカー、その後に続く必須のメタデータブロック(STREAMINFOブロックと呼ばれる)、任意の数の他のメタデータブロック、そしてオーディオフレームで構成されます。
  • FLACは最大128種類のメタデータブロックをサポートしており、現在以下のものが定義されています。
    • STREAMINFO: このブロックには、サンプルレート、チャンネル数、総サンプル数など、ストリーム全体に関する情報が含まれています。このブロックはストリーム内の最初のメタデータブロックとして存在しなければなりません。他のメタデータブロックが続く場合があり、デコーダが理解しないブロックはスキップされます。
    • APPLICATION: このブロックはサードパーティのアプリケーションで使用されるものです。唯一必須のフィールドは32ビットの識別子です。このIDは、アプリケーションがFLACメンテナによって要求された際に付与されます。残りの部分は登録されたアプリケーションによって定義されます。アプリケーションのIDをFLACに登録したい場合は、登録ページをご覧ください。
    • PADDING: このブロックは任意の量のパディングを可能にします。PADDINGブロックの内容には意味がありません。このブロックは、エンコード後にメタデータが編集されることが分かっている場合に便利です。ユーザーはエンコーダに対して十分なサイズのPADDINGブロックを予約するよう指示でき、メタデータが追加されたときに、既存のファイルに挿入する必要なくパディングを単に上書きすることができ、相対的に高速です。
    • SEEKTABLE: これは、シークポイントを格納するためのオプションのブロックです。シークテーブルがなくてもFLACストリームの任意のサンプルにシークすることは可能ですが、ビットレートがストリーム内で大きく変動する可能性があるため、遅延が予測できないことがあります。ストリームにシークポイントを追加することで、この遅延を大幅に短縮できます。各シークポイントは18バイトを占めるため、ストリーム内で1%の解像度を持たせても2kB未満の容量しか必要ありません。ストリーム内には1つのSEEKTABLEしか存在できませんが、テーブルには任意の数のシークポイントを含めることができます。また、デコーダによって無視される「プレースホルダ」シークポイントもあり、将来のシークポイント挿入のためにスペースを確保するために使用できます。
    • VORBIS_COMMENT: このブロックは、読み取り可能な名前/値のペアのリストを格納するためのものです。値はUTF-8を使用してエンコードされます。これはVorbisコメント仕様の実装です(フレーミングビットなし)。FLACで公式にサポートされている唯一のタグ付けメカニズムです。ストリームには1つのVORBIS_COMMENTブロックしか存在できません。一部の外部ドキュメントでは、混乱を避けるためにVorbisコメントがFLACタグと呼ばれます。
    • CUESHEET: このブロックは、キューシートに使用できるさまざまな情報を格納するためのものです。トラックやインデックスポイントをサポートしており、Red Book CDデジタルオーディオディスクと互換性があります。また、メディアカタログ番号やトラックISRCなどの他のCD-DAメタデータもサポートしています。CUESHEETブロックはCD-DAディスクのバックアップに特に便利ですが、再生のための一般的なキューイングメカニズムとしても使用できます。
    • PICTURE: このブロックは、ファイルに関連付けられた画像を格納するためのもので、最も一般的にはCDのカバーアートです。ファイル内に複数のPICTUREブロックを含めることができます。画像の形式はID3v2のAPICフレームに似ています。PICTUREブロックには、ID3v2と同様に、タイプ、MIMEタイプ、UTF-8説明があり、URLを介した外部リンクをサポートしていますが、これは推奨されていません。違いとして、説明フィールドには一意性の制約がなく、MIMEタイプは必須です。FLAC PICTUREブロックには解像度、色深度、およびパレットサイズも含まれているため、クライアントはすべての画像をスキャンすることなく適切な画像を検索できます。
  • オーディオデータは1つ以上のオーディオフレームで構成されています。各フレームはフレームヘッダーで構成されており、そこには同期コード、ブロックサイズ、サンプルレート、チャンネル数などのフレームに関する情報、そして8ビットのCRCが含まれています。フレームヘッダーには、フレーム内の最初のサンプルのサンプル番号(可変ブロックサイズストリームの場合)またはフレーム番号(固定ブロックサイズストリームの場合)も含まれています。これにより、迅速なサンプル精度のシークが可能になります。フレームヘッダーの後には、各チャンネルごとにエンコードされたサブフレームが続き、最後にフレームはバイト境界にゼロパディングされます。各サブフレームには、サブフレームのエンコード方法を指定する独自のヘッダーがあり、その後にそのチャンネルのエンコードされたオーディオデータが続きます。
  • デコーダがストリームの途中でデコードを開始する可能性があるため、フレームの開始位置を特定する方法が必要です。各フレームの最初には14ビットの同期コードがあります。同期コードはフレームヘッダー内の他の場所には表示されません。しかし、これはサブフレーム内に表示される可能性があるため、デコーダは同期が正しいことを確認するために他の2つの方法を持っています。最初は、フレームヘッダーの残りが無効なデータを含まないことを確認することです。しかし、これは完全な方法ではありません。なぜなら、サブフレーム内で有効なヘッダーパターンが依然として発生する可能性があるからです。デコーダの最終チェックは、フレームヘッダーの8ビットのCRCを生成し、これをフレームヘッダーの終わりに格納されているCRCと比較することです。
  • 繰り返しになりますが、デコーダがストリーム内の任意のフレームでデコードを開始できるため、フレームヘッダーには、ストリームに関する基本情報が含まれている必要があります。デコーダがストリームの先頭にあるSTREAMINFOメタデータブロックにアクセスできない可能性があるためです。この情報には、サンプルレート、サンプルあたりのビット数、チャンネル数などが含まれます。フレームヘッダーは純粋なオーバーヘッドであるため、圧縮率に直接影響します。フレームヘッダーをできるだけ小さく保つために、FLACはフレームパラメータの最も一般的に使用される値に対してルックアップテーブルを使用します。たとえば、フレームヘッダーのサンプルレート部分は4ビットで指定されます。ビットパターンの8つは、8/16/22.05/24/32/44.1/48/96 kHzの一般的に使用されるサンプルレートに対応しています。ただし、奇数のサンプルレートは、「ヒント」ビットパターンの1つを使用して指定され、フレームヘッダーの最後で正確なサンプルレートを見つけるようデコーダに指示します。同様の方法が、ブロックサイズおよびサンプルあたりのビット数の指定にも使用されます。このようにして、最も一般的な形式のオーディオデータに対して、フレームヘッダーのサイズを小さく保つことができます。
  • 各フレーム内の個々のサブフレーム(各チャンネルごとに1つ)は、フレーム内で別々に符号化され、ストリーム内に連続して現れます。言い換えれば、エンコードされたオーディオデータはチャンネル間でインターリーブされません。これにより、デコードバッファが大きくなる必要があるというコストがかかりますが、デコーダの複雑さが軽減されます。各サブフレームには、そのサブフレームの属性(予測方法、順序、残差符号化パラメータなど)を指定する独自のヘッダーがあり、その後にそのチャンネルのエンコードされたオーディオデータが続きます。
  • FLACは、自身のサブセットとしてのフォーマットを指定しています。これは、「ストリーム可能」なストリーム、つまりストリーム内でシークできないデコーダでも、ストリームの途中から拾ってデコードを開始できることを保証することを目的としています。また、デコーダのバッファサイズや他のリソース要件を容易に決定できるようにするため、ハードウェアデコーダの実装をより現実的にします。flacはデフォルトでサブセットストリームを生成しますが、「--lax」コマンドラインオプションを使用しない限りです。サブセットは、ストリームで使用されるものに次のような制限を加えます:
    • フレームヘッダーのブロックサイズビットは0001-1110でなければなりません。ブロックサイズは<=16384でなければなりません。サンプルレートが<=48000Hzの場合、ブロックサイズは<=4608でなければなりません。
    • フレームヘッダーのサンプルレートビットは0001-1110でなければなりません。
    • フレームヘッダーのビット毎サンプルビットは001-111でなければなりません。
    • サンプルレートが<=48000Hzの場合、LPCサブフレームのフィルタ順序は12以下でなければなりません。つまり、サブフレームヘッダーのサブフレームタイプビットは101100-111111であってはなりません。
    • ライス符号化された残差セクションのライス分割順序は8以下でなければなりません。

次の表は、FLACフォーマットの正式な説明を構成しています。角括弧内の数字は、特定のフィールドに使用されるビット数を示しています。

STREAM

<32> "fLaC"、FLACストリームマーカーのASCII表記で、ストリームのバイト0は0x66で、次に0x4C 0x61 0x43が続きます。
METADATA_BLOCK これは必須のSTREAMINFOメタデータブロックで、ストリームの基本的なプロパティを持っています。
METADATA_BLOCK* ゼロ個以上のメタデータブロック
FRAME+ 1つ以上のオーディオフレーム

METADATA_BLOCK

METADATA_BLOCK_HEADER メタデータブロックデータのタイプとサイズを指定するブロックヘッダー。
METADATA_BLOCK_DATA

METEDATA_BLOCK_HEADER

<1> 最後のメタデータブロックフラグ: このブロックがオーディオブロックの前の最後のメタデータブロックであれば '1'、そうでなければ '0'。
<7> ブロックタイプ

- 0 : STREAMINFO
- 1 : PADDING
- 2 : APPLICATION
- 3 : SEEKTABLE
- 4 : VORBIS_COMMENT
- 5 : CUESHEET
- 6 : PICTURE
- 7-126 : 予約済み
- 127 : 無効(フレーム同期コードとの混同を避けるため)
<24> 続くメタデータの長さ(METADATA_BLOCK_HEADERのサイズは含まない)

METADATA_BLOCK_DATA

METADATA_BLOCK_STREAMINFO
 METADATA_BLOCK_PADDING
 METADATA_BLOCK_APPLICATION
 METADATA_BLOCK_SEEKTABLE
 METADATA_BLOCK_VORBIS_COMMENT
 METADATA_BLOCK_CUESHEET
 METADATA_BLOCK_PICTURE
ブロックデータは、ブロックヘッダーのブロックタイプと一致する必要があります。

METADATA_BLOCK_STREAMINFO

<16> ストリームで使用される最小ブロックサイズ(サンプル単位)。
<16> ストリームで使用される最大ブロックサイズ(サンプル単位)。最小ブロックサイズと最大ブロックサイズが同じ場合、固定ブロックサイズのストリームであることを意味します。
<24> ストリームで使用される最小フレームサイズ(バイト単位)。値が不明であることを示すために0を指定することができます。
<24> ストリームで使用される最大フレームサイズ(バイト単位)。値が不明であることを示すために0を指定することができます。
<20> サンプルレート(Hz単位)。20ビットが使用可能ですが、フレームヘッダーの構造により最大サンプルレートは655350Hzに制限されています。また、値が0であることは無効です。
<3> (チャンネル数)-1。FLACは1から8チャンネルまでをサポートします。
<5> (ビット毎サンプル)-1。FLACは4ビットから32ビットまでのサンプルをサポートします。
<36> ストリーム内の総サンプル数。「サンプル」とは、チャネル間のサンプルを意味し、たとえば、44.1kHzのオーディオの1秒間にはチャンネル数に関係なく44100のサンプルが含まれます。ここで値が0の場合、総サンプル数が不明であることを意味します。
<128> エンコードされていないオーディオデータのMD5署名。これにより、ビットストリームが無効にならないエラーが発生した場合でも、デコーダがオーディオデータにエラーが存在するかどうかを判断できます。
NOTES

- FLACは最小ブロックサイズを16、最大ブロックサイズを65535に指定しているため、最小ブロックサイズおよび最大ブロックサイズフィールドで0から15に対応するビットパターンは無効です。

METADATA_BLOCK_PADDING

<n> n個の「0」ビット(nは8の倍数でなければならない)

METADATA_BLOCK_APPLICATION

<32> 登録済みのアプリケーションID。(FLACにIDを登録するには、登録ページをご覧ください。)
<n> アプリケーションデータ(nは8の倍数でなければなりません)

METADATA_BLOCK_SEEKATABLE

SEEKPOINT+ 1つ以上のシークポイント。
NOTE

- シークポイントの数はメタデータヘッダーの「長さ」フィールドによって暗示されます。つまり、長さ / 18に等しいです。

SEEKPOINT

<64> 対象フレームの最初のサンプルのサンプル番号、またはプレースホルダーポイントの場合は0xFFFFFFFFFFFFFFFF。
<64> 対象フレームのヘッダーの最初のバイトまでの、最初のフレームヘッダーの最初のバイトからのオフセット(バイト単位)。
<16> 対象フレーム内のサンプル数。
NOTES

- プレースホルダーポイントの場合、2番目と3番目のフィールド値は未定義です。
- テーブル内のシークポイントは、サンプル番号で昇順にソートされている必要があります。
- テーブル内のシークポイントは、プレースホルダーポイントを除いてサンプル番号ごとに一意である必要があります。
- 前述の2つの注意点から、プレースホルダーポイントの数に制限はありませんが、それらはすべてテーブルの末尾に配置される必要があります。

METADATA_BLOCK_VORBIS_COMMENT

<n> 別名FLACタグとも呼ばれる、こちらで指定されているVorbisコメントパケットの内容(フレーミングビットを除く)。Vorbisコメント仕様では約264バイトのデータを許容していますが、FLACメタデータブロックは224バイトに制限されています。ただし、Vorbisコメントの目的、つまり人間が読めるテキスト情報を提供することを考えると、この制限は大きな問題にはならないでしょう。また、Vorbis仕様に従って32ビットのフィールド長はリトルエンディアンで符号化されていることに注意してください。これは、FLACの他の固定長整数が通常ビッグエンディアンで符号化されているのとは異なります。

METADATA_BLOCK_CUESHEET

<128*8> メディアカタログ番号。ASCIIの表示可能文字で0x20-0x7eの範囲で表されます。一般的に、メディアカタログ番号は0から128バイトの長さになりますが、未使用の文字はNUL文字で右詰めされます。CD-DAの場合、これは13桁の数字で表され、その後に115バイトのNUL文字が続きます。
<64> リードインサンプルの数。このフィールドはCD-DAキューシートにのみ意味があります。他の用途では0にするべきです。CD-DAの場合、リードインは目次が保存されるTRACK 00エリアです。正確には、メディアの最初のサンプルから最初のトラックの最初のインデックスポイントのサンプルまでのサンプル数を指します。レッドブックによると、リードインは無音でなければならず、CDリッピングソフトウェアは通常これを保存しません。また、リードインは少なくとも2秒間以上でなければなりませんが、さらに長くてもかまいません。これらの理由から、リードインの長さがここに保存されており、最初のトラックの絶対位置を計算することができます。ここに保存されるリードインは、必ずしも最初のトラックのINDEX 01までではなく、最初のトラックの最初のインデックスポイントまでのサンプル数です。最初のトラックにもINDEX 00のデータが含まれている可能性があります。
<1> CUESHEETがコンパクトディスクに対応する場合は1、それ以外の場合は0。
<7+258*8> 予約済み。すべてのビットはゼロに設定されなければなりません。
<8> トラックの数。リードアウトトラックが必要なため、少なくとも1つ以上でなければなりません。CD-DAの場合、この数は最大で100まで(99の通常トラックと1つのリードアウトトラック)にする必要があります。
CUESHEET_TRACK+ 1つ以上のトラック。CUESHEETブロックにはリードアウトトラックが必要で、常にCUESHEETの最後のトラックになります。CD-DAの場合、リードアウトトラック番号はレッドブックで指定されているように170でなければなりません。その他の場合は255でなければなりません。

CUESHEET_TRACK

<64> FLACオーディオストリームの開始からのトラックオフセット(サンプル単位)。これはトラックの最初のインデックスポイントまでのオフセットです。(CD-DAと異なり、CD-DAではトラックのTOCにおけるオフセットはトラックのINDEX 01のもので、INDEX 00があってもトラックのOFFSETではありません。)CD-DAの場合、オフセットは588サンプル(588サンプル = 44100サンプル/秒 * 1/75秒)で割り切れる必要があります。
<8> トラック番号。トラック番号が0の場合はCD-DA規格と衝突するため許可されていません。CD-DAの場合、番号は1から99まで、またはリードアウトのために170でなければなりません。非CD-DAの場合、リードアウトのためには255でなければなりません。推奨されるのはトラック1から始めて順に増加させることです。トラック番号はCUESHEET内で一意でなければなりません。
<12*8> トラックISRC。これは12桁の英数字コードです。詳細はこちらおよびこちらをご覧ください。ISRCが存在しない場合は12のASCII NUL文字を使用できます。
<1> トラックタイプ:0はオーディオ、1は非オーディオ。これはCD-DA Qチャネル制御ビット3に対応します。
<1> プレエンファシスフラグ:0はプレエンファシスなし、1はプレエンファシスあり。これはCD-DA Qチャネル制御ビット5に対応します;詳細はこちらをご覧ください。
<6+13*8> 予約済み。すべてのビットはゼロに設定されなければなりません。
<8> トラックインデックスポイントの数。リードアウトトラックを除き、すべてのトラックには少なくとも1つのインデックスが必要です。CD-DAの場合、この数は100を超えてはいけません。
CUESHEET_TRACK_INDEX+ リードアウトトラックを除くすべてのトラックの1つ以上のトラックインデックスポイント。

CUESHEET_TRACK_INDEX

<64> インデックスポイントのオフセット(サンプル単位)、トラックオフセットに対する相対位置。CD-DAの場合、オフセットは588サンプル(588サンプル = 44100サンプル/秒 * 1/75秒)で割り切れる必要があります。オフセットはトラックの開始からのものであり、オーディオデータの開始からではありません。
<8> インデックスポイント番号。CD-DAの場合、インデックス番号0はトラックのプレギャップに対応します。トラック内の最初のインデックスは0または1でなければならず、その後のインデックス番号は1ずつ増加する必要があります。インデックス番号はトラック内で一意でなければなりません。
<3*8> 予約済み。すべてのビットはゼロに設定されなければなりません。

METADATA_BLOCK_PICTURE

<32> D3v2 APICフレームに従った画像のタイプ:

- 0 - その他
- 1 - 32x32ピクセルの「ファイルアイコン」(PNGのみ)
- 2 - その他のファイルアイコン
- 3 - 表紙(前面)
- 4 - 表紙(背面)
- 5 - パンフレットページ
- 6 - メディア(例: CDのラベル面)
- 7 - リードアーティスト/リードパフォーマー/ソリスト
- 8 - アーティスト/パフォーマー
- 9 - 指揮者
- 10 - バンド/オーケストラ
- 11 - 作曲家
- 12 - 歌詞作家/テキスト作家
- 13 - 録音場所
- 14 - 録音中
- 15 - パフォーマンス中
- 16 - 映画/ビデオのスクリーンキャプチャ
- 17 - 明るい色の魚
- 18 - イラスト
- 19 - バンド/アーティストのロゴタイプ
- 20 - 出版社/スタジオのロゴタイプ

その他は予約されており、使用すべきではありません。タイプ1と2の画像は、ファイル内にそれぞれ1つずつのみ存在することができます。
<32> MIMEタイプ文字列の長さ(バイト単位)。
<n*8> MIMEタイプ文字列(印刷可能なASCII文字 0x20-0x7e)。MIMEタイプは、データ部分が画像データそのものではなく、画像のURLであることを示すために --> とすることもできます。
<32> 説明文字列の長さ(バイト単位)。
<n*8> 画像の説明(UTF-8)。
<32> 画像の幅(ピクセル単位)。
<32> 画像の高さ(ピクセル単位)。
<32> 画像の色深度(ビット/ピクセル)。
<32> インデックスカラー画像(例: GIF)の場合、使用されている色数、または非インデックス画像の場合は 0。
<32> 画像データの長さ(バイト単位)。
<n*8> バイナリ形式の画像データ。

FRAME

FRAME_HEADER
SUBFRAME+ チャンネルごとに1つのサブフレーム。
<\?> バイトアラインメントへのゼロパディング
FRAME_FOOTER

FRAME_HEADER

<14> 同期コード '11111111111110'
<1> 予約: [1]

- 0 : 必須値
- 1 : 将来の使用のために予約
<1> ブロック戦略: [2] [3]

- 0 : 固定ブロックサイズストリーム; フレームヘッダーがフレーム番号をエンコード
- 1 : 可変ブロックサイズストリーム; フレームヘッダーがサンプル番号をエンコード
<4> インター チャンネルサンプルでのブロックサイズ:

- 0000 : 予約済み
- 0001 : 192 サンプル
- 0010-0101 : 576 * (2n-2) サンプル、すなわち 576/1152/2304/4608
- 0110 : ヘッダーの最後から 8 ビット (blocksize-1) を取得
- 0111 : ヘッダーの最後から 16 ビット (blocksize-1) を取得
- 1000-1111 : 256 * (2n-8) サンプル、すなわち 256/512/1024/2048/4096/8192/16384/32768
<4> サンプルレート:

- 0000 : STREAMINFO メタデータブロックから取得
- 0001 : 88.2kHz
- 0010 : 176.4kHz
- 0011 : 192kHz
- 0100 : 8kHz
- 0101 : 16kHz
- 0110 : 22.05kHz
- 0111 : 24kHz
- 1000 : 32kHz
- 1001 : 44.1kHz
- 1010 : 48kHz
- 1011 : 96kHz
- 1100 : ヘッダーの最後から 8 ビット サンプルレート (kHz) を取得
- 1101 : ヘッダーの最後から 16 ビット サンプルレート (Hz) を取得
- 1110 : ヘッダーの最後から 16 ビット サンプルレート (十の Hz) を取得
- 1111 : 無効、同期を欺くための 1 の文字列を防ぐ
<4> チャンネル割り当て

- 0000-0111 : (独立チャンネル数)-1. 定義されている場合、チャンネル順序は SMPTE/ITU-R 推奨に従います。 割り当ては次のとおりです:
- 1 チャンネル: モノ
- 2 チャンネル: 左、右
- 3 チャンネル: 左、右、センター
- 4 チャンネル: 前左、前右、後左、後右
- 5 チャンネル: 前左、前右、前センター、後/サラウンド左、後/サラウンド右
- 6 チャンネル: 前左、前右、前センター、LFE、後/サラウンド左、後/サラウンド右
- 7 チャンネル: 前左、前右、前センター、LFE、後センター、サイド左、サイド右
- 8 チャンネル: 前左、前右、前センター、LFE、後左、後右、サイド左、サイド右
- 1000 : 左/サイド ステレオ: チャンネル 0 は左チャンネル、チャンネル 1 はサイド (差分) チャンネル
- 1001 : 右/サイド ステレオ: チャンネル 0 はサイド (差分) チャンネル、チャンネル 1 は右チャンネル
- 1010 : ミッド/サイド ステレオ: チャンネル 0 はミッド (平均) チャンネル、チャンネル 1 はサイド (差分) チャンネル
- 1011-1111 : 予約
<3> サンプルサイズ (ビット):

- 000 : STREAMINFO メタデータブロックから取得
- 001 : 8 ビット サンプル
- 010 : 12 ビット サンプル
- 011 : 予約済み
- 100 : 16 ビット サンプル
- 101 : 20 ビット サンプル
- 110 : 24 ビット サンプル
- 111 : 32 ビット サンプル
<1> 予約:

- 0 : 必須値
- 1 : 将来の使用のために予約|
<?> (可変ブロックサイズの場合)
<8-56>: "UTF-8" コーディング サンプル番号 (デコードされた番号は 36 ビット) 4
それ以外の場合
<8-48>: "UTF-8" コーディング フレーム番号 (デコードされた番号は 31 ビット) 4
<?> i(ブロックサイズビットが 011x の場合)
8/16 ビット (blocksize-1)
<?> (サンプルレートビットが 11xx の場合)
8/16 ビット サンプルレート
<8> CRC-8 (多項式 = x8 + x2 + x1 + x0、初期値 0) のすべての内容、同期コードを含む、CRC の前のすべて
NOTES

1. このビットは、FLAC フレームの最初の 15 ビットが MPEG オーディオ フレームの開始と区別できるように 0 にしておく必要があります (詳細)。
2. 「ブロック戦略」ビットは、ストリーム全体で同じでなければなりません。
3. 「ブロック戦略」ビットは、フレーム内の最初のサンプルのサンプル番号を計算する方法を決定します。ビットが 0 (固定ブロックサイズ) の場合、フレームヘッダーはフレーム番号を上記のようにエンコードし、フレームの開始サンプル番号はフレーム番号にブロックサイズを掛けたものになります。ビットが 1 (可変ブロックサイズ) の場合、フレームヘッダーがフレームの開始サンプル番号自体をエンコードします。 (固定ブロックサイズストリームの場合、最後のブロックのみがストリームのブロックサイズよりも短い場合があります。その場合の開始サンプル番号は、フレーム番号に前のフレームのブロックサイズを掛けたもの、または最初のフレームである場合はゼロになります)。
4. サンプル/フレーム番号に使用される「UTF-8」コーディングは、圧縮 UCS-2 を保存するために使用される可変長コードと同じもので、大きな入力に対応するために拡張されています。
<16> CRC-16 (多項式 = x16 + x15 + x2 + x0、初期値 0) は、CRC の前のすべての内容、フレームヘッダー同期コードを含むまでの内容の CRC です。

SUBFRAME

SUBFRAME_HEADER
SUBFRAME_CONSTANT
SUBFRAME_FIXED
SUBFRAME_LPC
SUBFRAME_VERBATIM
SUBFRAME_HEADER では、どのタイプのサブフレームが使用されるかを指定します。

SUBFRAME_HEADER

<1> ゼロビットのパディング、同期を欺く1の文字列を防ぐため
<6> サブフレームタイプ:

- 000000 : SUBFRAME_CONSTANT
- 000001 : SUBFRAME_VERBATIM
- 00001x : 予約済み
- 0001xx : 予約済み
- 001xxx : xxx <= 4 の場合 SUBFRAME_FIXED、xxx=order ; それ以外は予約済み
- 01xxxx : 予約済み
- 1xxxxx : SUBFRAME_LPC、xxxxx=order-1
<1+k> 「無駄なビットごとのサンプル」フラグ:

- 0 : ソースサブブロックに無駄なビットごとのサンプルはなし、k=0
- 1 : ソースサブブロックに k の無駄なビットごとのサンプルがあり、k-1 が続く、ユニコードで符号化される。例えば、k=3 の場合は 001 が続き、k=7 の場合は 0000001 が続く。

SUBFRAME_CONSTANT

<n> サブブロックのエンコードされていない定数値、n = フレームのビットごとのサンプル。

SUBFRAME_FIXED

<\n> エンコードされていないウォームアップサンプル (n = フレームのビットごとのサンプル * 予測子の順序)
RESIDUAL エンコードされた残差

SUBFRAME_LPC

<n> エンコードされていないウォームアップサンプル (n = フレームのビットごとのサンプル * LPC の順序)
<4> 量子化された線形予測子係数の精度(ビット単位)-1(1111 = 無効)
<5> 量子化された線形予測子係数のシフトに必要なビット数(注:この数値は符号付きの二進補数表現)
<n> エンコードされていない予測子係数 (n = 量子化された予測子係数の精度 * LPC の順序)(注:係数は符号付きの二進補数表現)
RESIDUAL エンコードされた残差

SUBFRAME_VERBATIM

<n*i> エンコードされていないサブブロック; n = フレームのビットごとのサンプル, i = フレームのブロックサイズ

RESIDUAL

<2> 残差コーディング方式:

- 00 : 4ビットRiceパラメーターを使用したパーティショニングRiceコーディング; RESIDUAL_CODING_METHOD_PARTITIONED_RICEが続く
- 01 : 5ビットRiceパラメーターを使用したパーティショニングRiceコーディング; RESIDUAL_CODING_METHOD_PARTITIONED_RICE2が続く
- 10-11 : 予約済み
RESIDUAL_CODING_METHOD_PARTITIONED_RICE |
RESIDUAL_CODING_METHOD_PARTITIONED_RICE2

RESIDUAL_CODING_METHOD_PARTITIONED_RICE

<4> パーティションオーダー。
RICE_PARTITION+ 2orderパーティションが存在します。

RICE_PARTITION

<4(+5)> エンコーディングパラメータ:

- 0000-1110: Riceパラメータ。
- 1111: エスケープコードで、パーティションがnビットのサンプルごとの未エンコードのバイナリ形式であることを意味します。nは5ビットの数値として続きます。
<?> エンコードされた残差。パーティション内のサンプル数(n)は次のように決定されます:

- パーティションオーダーがゼロの場合、n = フレームのブロックサイズ - 予測器のオーダー
- それ以外の場合、これがサブフレームの最初のパーティションでない場合、n = (フレームのブロックサイズ / (2^パーティションオーダー))
- それ以外の場合、n = (フレームのブロックサイズ / (2^パーティションオーダー)) - 予測器のオーダー

RESIDUAL_CODING_METHOD_PARTITIONED_RICE2

<4> パーティションオーダー。
RICE2_PARTITION+ 2orderパーティションが存在します。

RICE2_PARTITION

<5(+5)> エンコーディングパラメーター:

- 00000-11110 : Riceパラメーター。
- 11111 : エスケープコードで、パーティションがnビットのサンプルごとの未エンコードのバイナリ形式であることを意味します。nは5ビットの数値として続きます。
<?> エンコードされた残差。パーティション内のサンプル数 (n) は以下のように決定されます:

- パーティションオーダーがゼロの場合、n = フレームのブロックサイズ - 予測子オーダー
- それ以外の場合でサブフレームの最初のパーティションでない場合、n = (フレームのブロックサイズ / (2^パーティションオーダー))
- それ以外の場合、n = (フレームのブロックサイズ / (2^パーティションオーダー)) - 予測子オーダー

Ogg-FLAC-マッピング

この項目では、圧縮された FLAC データが Ogg トランスポートレイヤー内でどのようにカプセル化されるかを説明しています。FLAC フォーマットと Ogg 構造およびフレーミングの基本的な知識が前提とされています。

FLACネイティブフォーマットとOggカプセル化

元々の FLAC フォーマットは非常にシンプルなトランスポートシステムを含んでいます。このシステムは「ネイティブ FLAC」と呼ばれ、圧縮された FLAC オーディオデータと薄いトランスポートが混在しています。このトランスポートはオーディオフレームのヘッダーとフッターを含み、同期パターン、タイムコード、チェックサム(ただしフレーム長は含まれていません)、およびメタデータシステムを持っています。これは非常に軽量で、複数の論理ストリームなどのより高度なトランスポートメカニズムをサポートしていませんが、その目的には適していました。

ネイティブ FLAC トランスポートは、標準的なコーデック設計における「レイヤー」とは異なり、ペイロードから完全に切り離すことができません。メタデータシステムは分離できますが、フレームヘッダーにはトランスポートに関連するデータ(同期パターン、タイムコード、チェックサム)と圧縮パケットに関連するデータ(オーディオパラメータ、チャンネル設定、サンプルレートなど)が含まれています。

これにより、FLAC を他の真のトランスポートレイヤーにカプセル化する際に問題が生じます。冗長性と複雑さの選択が必要です。正確性を追求するために、ネイティブ FLAC からトランスポートデータを削除し、残りのフレームヘッダー情報をオーディオパケットに統合するマッピングを作成することが考えられます。しかし、このアプローチでは現在のネイティブ FLAC デコーダーソフトウェアが使用できなくなるため、別のデコーディング実装を作成し維持する必要があります。または、Ogg FLAC デコーダーが Ogg FLAC パケットからネイティブ FLAC フレームを合成し、それをネイティブ FLAC デコーダーに渡す必要があります。

別の方法として、ネイティブ FLAC フレームを Ogg パケットとして扱い、トランスポートの冗長性を受け入れることができます。この方法では最大 12 バイトの冗長性が生じますが、一般的なステレオ CD オーディオが 4096 サンプルのブロックサイズでエンコードされている場合、圧縮フレームは 4-16 Kバイトになります。冗長性はごくわずかです。

シンプルさと迅速さを重視して、最初の公式 FLAC->Ogg マッピングにはこの方法が選ばれました。将来、冗長性の少ないマッピングが定義できるように、最初のパケットにはマッピングバージョンが含まれています。

FLAC-to-Oggマッピングバージョン1.0

FLAC-to-Ogg マッピング バージョン 1.0 は、以下のようにシンプルな識別ヘッダーと純粋なネイティブ FLAC データで構成されています:

ストリームの最初のパケットは以下で構成されています:

  1. パケットタイプ: 1 バイトで、値は 0x7F
  2. ASCII シグネチャ: 4 バイトで、"FLAC"(0x46, 0x4C, 0x41, 0x43)
  3. マッピングバージョン番号: 1 バイトのバイナリ形式で、例えばマッピングバージョン 1.0 の場合は 0x01
  4. マッピングのマイナーバージョン番号: 1 バイトのバイナリ形式で、例えばマッピングバージョン 1.0 の場合は 0x00
  5. ヘッダーパケット数: 2 バイトのビッグエンディアン形式で、ヘッダー(非オーディオ)パケットの数を示します(このパケットを含まない)。この数が 0x0000 の場合は「不明」を意味しますが、一部のデコーダーがこのようなストリームを処理できない場合があることに注意してください。
  6. ネイティブ FLAC シグネチャ: 4 バイトの ASCII 形式で、"fLaC"(FLAC フォーマット仕様に従う)
  7. STREAMINFO メタデータブロック: ストリーム用の STREAMINFO メタデータブロック

この最初のパケットはストリームの最初のページにのみ存在し、これにより、論理ストリームの最初に正確に 79 バイトの Ogg ページが生成されます。この最初のページはページフラグで「ストリームの始まり」とマークされます。

ヘッダーパケット

最初のパケットに続いて、一つ以上のヘッダーパケットが続きます。各パケットには 1 つのネイティブ FLAC メタデータブロックが含まれます。これらの最初のメタデータパケットは VORBIS_COMMENT ブロックでなければなりません。これらのパケットはページ境界を跨る可能性がありますが、最後のパケットは終了ページを完了させ、最初のオーディオパケットが新しいページの開始となります。メタデータパケットの最初のバイトもパケットタイプとして機能し、その合法的な範囲は (0x01-0x7E, 0x81-0xFE) です。

これらの最初のページにはグラニュール位置がゼロとして設定されています。

オーディオパケット

論理ストリームの最初のオーディオパケットは新しい Ogg ページの開始を示します。ネイティブ FLAC オーディオフレームはストリーム内の後続のパケットとして現れます。各パケットは 1 つの FLAC オーディオフレームに対応しています。各パケットの最初のバイトはパケットタイプを示し、オーディオパケットは常に 0xFF です(ネイティブ FLAC フォーマット仕様に従う)。

ページとパケットの境界

  • 最後のページ: 「ストリームの終わり」としてマークされます。
  • FLAC パケットのページ境界: FLAC パケットはページ境界を跨る可能性があります。
  • FLAC オーディオを含むページのグラニュール位置: Ogg-カプセル化 Vorbis の場合と同じ意味論に従います。

STREAMINFOパケットの冗長フィールド

STREAMINFO パケット内の冗長フィールドはゼロに設定することができます(ネイティブ FLAC では「不明」を示す)。これにより、単一パスエンコーディングが容易になります。これらのフィールドには、最小および最大フレームサイズ、合計サンプル数、および MD5 シグネチャが含まれます。これらのフィールドの「不明」値は、準拠するネイティブ FLAC または Ogg FLAC デコーダーがストリームをデコードするのを妨げることはありません。

FLAC-to-Oggマッピングのバージョン構造

すべての FLAC-to-Ogg マッピングのバージョンにおいて、最初の 6 バイトは同じ構造を共有することが意図されています。具体的には、以下の 6 バイトです:

この構造は、異なるバージョンの FLAC-to-Ogg マッピングにおいても共通しています。

デコーダーへの暗黙のヒント

マッピングバージョン番号にはデコーダーへの暗黙のヒントが含まれています。同じメジャーバージョン番号を持つマッピングバージョンは、同じメジャーバージョン番号のデコーダーによってデコード可能であるべきです。たとえば、バージョン 1.x の Ogg FLAC デコーダーは、x < y の場合でもバージョン 1.y の Ogg FLAC ストリームをデコードできるべきです。

もしマッピングがこの前方互換性を破る場合は、メジャーバージョン番号が増加します。

終わり

ここまでお読みいただきありがとうございました。