kostumブログ

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

音楽メタ情報 - 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 ストリームをデコードできるべきです。

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

終わり

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

Yjs(リアルタイム共同編集を実現するためのアルゴリズムとデータ構造)について調べてみました

目次

調べたものとは?

github.com

docs.yjs.dev

今回はYjsというライブラリがあることを知ったので、このライブラリの技術要素について調べました。

そもそもYjsというのは、自動的に同期するアプリケーションを構築するためのCRDT(Conflict-free replicated data type: コンフリクトしない複製可能なデータタイプ)を高レベルで実装できるライブラリです。

では、CRDTとはどのような技術(データタイプ)なのでしょうか。

CRDTとは

qiita.com

調べようと思ったら、すでにまとめてくれている方がいますね!インターネットって便利!

CRDT

  • 実現の方法によって2種類の呼び方がある。
    • Commutative Replicated Data Type(Operation-Based)
    • Convergent Replicated Data Type(State-Based)
  • CRDTは、CAP(後述)におけるAPの両立している。
    • ネットワークが故障してしまっても、データの整合性は保証しないが、読み書きは常にできる。
    • ネットワークが安定していれば、そのうちの整合性が保証されたデータを読み書きできる

CAP定理

  • 分散システムでは、以下の3つを同時に満たすことはできず、満たせても2つまで。
    • (Partition Tolerance) ネットワークが故障しても
    • (Consistency) データの整合性を保って
    • (Availability) 読み書きが常にできる

CRDTの理論

すみません、おおまかにしか分からず、説明できるほど理解できませんでした😢 興味のある方は、論文を読んでみてください!

inria.hal.science

データタイプの種類について

Commutative-Replicated-Data-Type(Operation-Based)

  • 「操作」をやりとりする方法。

Convergent-Replicated-Data-Type(State-Based)

  • 「データの状態」をやりとりする方法。
  • 状態をマージするため、それまでの独立した「状態」がマージできるデータでないといけない。

実例

  • カウンタや集合の他にも、Map、Register、Graphといった複雑なデータもCRDTに出来る。

Webアプリケーションへの実装

ここまで調べたので、アプリケーションで使ってみたいと思います。

ここでも、先人の方がまとめてくださっているので、何が必要なのかはそちらをご覧ください。

qiita.com

共同編集テキストエディタを実装しようと思うのですが、これにはWebSocketを使うことになります。

実装自体は上記に記載していただけている通りに実装すれば、問題なく実装できます。 こちらに実装したリポジトリを挙げておきます。

github.com

lexicalというテキストエディタフレームワークを用いれば、Yjsプラグインとして組み込めます。このリポジトリにあるように、テキストだけのリアルタイム共同編集のみを実装するのはそこまで難しくなさそうです。

github.com

テキスト編集部分はlexicaで実装し、providerYjsを組み込んでいます。

providerでは、サーバ側のアドレスとデータの連携を行います。 サーバの準備は、y-websocketを実行すれば簡単にサーバを用意することができました。

終わりに

ということで、リアルタイム共同編集の実装とその技術について調べました。

GoogleDocumentのような、Web上での共同編集機能がどのように実現されているのかを知ることができました(実際にこの技術を使ってGoogleDocumentが作られているかはわかりませんが)。

アルゴリズムとしては複雑ですが、lexicalYjsによってそれらを意識することなく実装できるのはかなり便利なライブラリですね!

最後に、記事を執筆してくれた方や、ここまでお読みいただきありがとうございました。

参考

qiita.com

qiita.com

github.com

docs.yjs.dev

inria.hal.science

en.wikipedia.org

lexical.dev

ObsidianのnoteをJavaScriptで解析する

おはこんにちばんは

obsidian.md

Obsidian、使いやすいですよね。
私も、同僚に教えていただき使ってみたところ、使いやすく、メモなどに使っています。

このObsidianでは、コミュニティプラグインを用いることで、JavaScriptを実行できることができました。

ということで、本記事では、ObsidianでJavaScriptを実行するための方法を紹介していきたいと思います。

前提

以下ですが、設定の表示方法など基本的な部分を省略して記載しております。 そのため、ある程度Obsidianのことを理解している、という前提で話していきます。

準備

まずは、コミュニティプラグインをインストールしていきます。 インストールするプラグインは以下の2つです。

  • CustomJS

github.com

  • DataView

github.com

これらプラグインを必要とする理由を簡単に説明すると、

ということになります。

プラグインをインストールしたら、プラグインを有効化します。
また、CustomJSだけ設定画面でscriptを配置するフォルダを指定しておいてください。

方法

では、今回はTOC(目次)を自動作成するスクリプトを作成していきます。

任意のディレクトリにJavaScriptファイルを作成します。
そして、作成したファイルにコードを記載してください(以下参照)。

// toc.js

class TableOfContents {
    /**
     * Table of Contents(目次)の取得
     * @param path Obsidianのルートディレクトリを基準としたファイルの相対パス
     * @param fileName ファイル名
   */
    getTOC(path, fileName) {
        const fs = require("node:fs");
        
        // ファイル内のコンテンツを取得する
        const ABSOLUTE_PATH_TO_OBSIDIAN = ""
        const absolutePath = ABSOLUTE_PATH_TO_OBSIDIAN + path;
        const contents = fs.readFileSync(absolutePath, { encoding: "utf8" });
        
        // コンテンツ内の見出しのみを取得する
        const REG_EXP_HEADING = /^(# |## |### |#### ).*/gm;
        const headingList = contents.match(REG_EXP_HEADING);
        
        // obsidianでの内部リンク方法
        // [[{fileName}# {headingName}|{name}]]
        // 例:[[ObsidianのnoteをJavaScriptで解析する#前提|test]]
        const REG_EXP_HEADING_TITLE = /^(# |## |### |#### )/gm;
        const headingTitleList = headingList.map((heading) => {
            return heading.replace(REG_EXP_HEADING_TITLE, "");
        });
        const markdownList = headingList.map((heading, index) => {
            const headingHash = heading.split(" ")[0];
            const headingNumber = headingHash.length;
            const indent = "- ".repeat(headingNumber);
            
            return `${headingNumber > 0 && `${indent}##### `}[${
                headingTitleList[index]
            }](${heading.replace(" ", "")})`;
        });
    
        return { markdownList };
    }
}

(※ ここでは、コードのロジックについては、説明致しません)

コードを書く上での注意点は以下になります。

  • 必ずclassを使って書きましょう。関数を使ってimportしようとしても読み込んできれません。
  • npmなどを使って、ライブラリをインストールして使用することはできません。ただし、nodeは使うことができます。

このコードを、DataViewで読み込みます。

任意のObsinianのノートで、以下のコードブロックを記載します。

  ```dataviewjs
    const { TableOfContents } = customJS
    const name = dv.current().file.name
    const path = dv.current().file.path
    const toc = TableOfContents.getTOC(path, name)
    
    dv.header(3, "目次");
    dv.paragraph(toc.markdownList);
  ```

このコードブロックの下に###などを使って見出しを書きます。

設定などがうまく出来ていれば、目次が表示されるようになり、かつ見出しへのアンカーリンクとして使用することができます。

obsidian-toc-example
上手く認識した場合の例

終わりに

実装することはできましたでしょうか。

Obsidianでは、すでに目次を表示する機能があるので、今回のtoc.jsは不要です(私がこれを作成した時は、目次の機能に気づきませんでした。。。)。

しかし、この方法を応用して便利な使い方ができると、Obsidianを使うのがもっと楽しくなりますね!

もしかしたら、もっと簡単な方法があるかもですが、今回はここまでとさせていただきます。

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

IndexedDB APIを知る② ~ IndexedDBを使う ~

前の記事

前回記事はこちら

kostum.hatenablog.jp

基本パターン

  1. データベースを開く
  2. データベース内に、オブジェクトストアを作成する
  3. データベース操作のトランザクションを開始して、リクエストを行う
  4. 適切なDOMイベントを受け取ることにより、操作が完了するのを待つ
  5. 結果に応じた処理を行う

ストアを作成および構築する

  • 接頭辞を使用している実装は不具合がある、未完成、古い版の仕様に従っている可能性があるため、製品版で使用することは推奨されない。対応しているものとするより、未対応とする方が良い。
if (!window.indexedDB) {
    console.log("このブラウザは安定版のIndexedDB を対応していません。")
}

データベースを開く

// データベースは開く
var request = window.indexedDB.open("MyTestDatabase", 3);
  • データベースを開くリクエストは、すぐにはデータベースを開いたりトランザクションを開始したりはしない。
  • open()関数を呼び出すと、結果(成功)またはイベントとして扱うエラー値を伴うIDBDatabaseオブジェクトを返す。

IDBFactory.open - Web API | MDN

ハンドラーの生成

  • 全てが成功すると、typeプロパティが”success"であるDOMイベントが、requesttargetとして発生する。イベントが発生するとrequestonsuccess()関数が、successイベントを引数として呼び出される。
  • 何らかの問題がある場合、typeプロパティが”error"であるDOMイベントがrequestで発生する。これは、エラーイベントを引数としてonerror()関数を呼び出す。
  • IndexedDB APIはエラー処理を最小限にするよう設計されているが、データベースを開く場合、データベースを作成する許可をユーザーがウェブアプリに与えていない場合が多い。
  • ブラウザはwebアプリが初めてストレージ用にIndexedDBを開こうとした時に、ユーザーへプロンプトを表示する。ユーザーはアクセスを許可または否定できる。

データベースを作成またはデータベースのバージョンを更新する

  • 新しいデータベースを作成したり既存のデータベースのバージョンを更新したりすると、onupgradeneededイベントが発生して、request.resultに設定したonversionchangeイベントハンドラーにIDBVersionChangeEventオブジェクトが渡される。
  • upgradeneededイベントのハンドラーでは、このバージョンのデータベースで必要なオブジェクトストアを作成する。
  • onupgradeneededイベントから正常に抜けた場合は、データベースを開くリクエストのonsuccessハンドラーが実行される。

データベースを構築する

  • 値をオブジェクトストアへ保存するたびに、値がキーと関連づけられる。オブジェクトストアでキーパス(オブジェクトストアやインデックスのどこからブラウザがキーを取り出すべきかを定義する)を使用するかキージェネレータ(指定した順序で新たなキーを背制する仕組み)を使用するかに応じて、キーを供給する方法がいくつか存在する。
  • オブジェクトストアがプリミティブではなくオブジェクトを保持していれば、オブジェクトストアでインデックスを作成することもできる。
  • インデックスは、オブジェクトのキーではなく保存されたオブジェクトのプロパティの値を使用して、オブジェクトストア内に保存された値を検索することを可能にする。
  • さらにインデックスには、保存されたデータに単純な制限を強制する機能がある。
// データを保存例

const dbName = "the_name";

var request = indexedDB.open(dbName, 2);

request.onerror = function(event) {
  // エラー処理
};
request.onupgradeneeded = function(event) {
  var db = event.target.result;

  // 顧客の情報を保存する objectStore を作成する。
  // "ssn" は一意であることが保証されているので、キーパスとして使用する。
    // 第一引数:ストアの名前
    // 第二引数:引数オブジェクト(省略可能)。
    //   keyPath:ストア内で個々のオブジェクトを一意にするプロパティ
  var objectStore = db.createObjectStore("customers", { keyPath: "ssn" });

  // 顧客を名前で検索するためのインデックスを作成。
  // 重複する可能性があるので、一意のインデックスとしては使用できない。
  objectStore.createIndex("name", "name", { unique: false });

  // 顧客をメールアドレスで検索するためのインデックスを作成します。2 人の顧客が同じメールアドレスを
  // 使用しないようにしたいので、一意のインデックスを使用します。
  objectStore.createIndex("email", "email", { unique: true });

  // データを追加する前に objectStore の作成を完了させるため、
  // transaction oncomplete を使用します。
  objectStore.transaction.oncomplete = function(event) {
    // 新たに作成した objectStore に値を保存します。
    var customerObjectStore = db.transaction("customers", "readwrite").objectStore("customers");
    customerData.forEach(function(customer) {
      customerObjectStore.add(customer);
    });
  };
};
  • onupgradeneededはデータベースの構造を変えることができる唯一の場所で、オブジェクトストアの作成や削除、インデックスの構築および削除が可能。

キージェネレータを使用する

  • オブジェクトストアを作成するときにautoIncrementフラグを設定すると、そのオブジェクトストアでキージェネレータを使用できる。
  • キージェネレータを使用すると、オブジェクトストアに値を追加するのに応じて自動的にキーが生成される。
  • データベースの操作が取り消された場合を除いて、キージェネレーターの現在の値が減少することはない。

データの追加、読み取り、削除

  • データベースで何かを行えるようにする前に、トランザクションを開始しなければならない。
  • トランザクションはデータベースオブジェクトから生じており、トランザクションの対象にしたいオブジェクトストアを指定しなければならない。
  • トランザクションは、readonly, readwrite, versionchangeの3つのモードを使用できる。
    • データベースの「スキーマ」や構造を変更するには、versionchangeモードにしなければならない。このトランザクションは、versionを指定してIDBFactory.openメソッドを呼び出すことによって開く。
    • 既存のオブジェクトストアからレコードを読み出すには、トランザクションreadolyモードまたはreadwriteモードを使用できる。
    • 変更処理を行うには、readwriteモードにしなければならない。
    • このようなトランザクションは、IDBDatabase.transactionで開き、第一引数はstoreNames(アクセスしたいオブジェクトストアの配列で定義されるスコープ)で第二引数はトランザクションmodereadonlyまたはreadwrite)。
    • 適切なトランザクションのスコープとモードによって、データアクセスを高速化できる。
      • スコープを定義するときは、必要なオブジェクトストアのみ指定する。これにより、同時にスコープが重なり合うことなく複数のトランザクションを実行できる。
      • readwriteモードは、必要な場合に限り限定する。readwriteトランザクションはオブジェクトストアに対して1個しか実行できない。

データベースにデータを追加する

カーソルの使用

  • オブジェクトストア内の全ての値を取得したい場合は、カーソルを使用できる。
  • opneCursor()関数は、引数がいくつかある。
    • 第一引数:すぐに取得するキーレンジオブジェクトを使用して、読み出すアイテムの範囲を制限できる。
    • 第二引数:反復処理を行いたい方向を指定できる。
  • カーソルの成功イベントのコールバックは少し特殊で、カーソルオブジェクト自体は、リクエストのresultだが、実際のキーと値は、カーソルオブジェクトのkeyプロパティとvalueプロパティで見つかる。
  • 進み続けたい場合は、カーソルでcontinue()を呼び出す必要がある。
  • データの終端に達した(または、openCursor()リクエストに一致する項目が存在しない)場合は成功のコールバックを受け取るが、resultプロパティがundefinedになる。
    • それ以外に、このような処理を行うためにgetAll()(およびgetAllKeys())を使用することができるが、カーソルのvalueプロパティに関してパフォーマンスコストが発生する。例えば、それぞれのキーを検索することにのみ関心がある場合は、getAl()よりもカーソルを使用する方が効率的。オブジェクトストア内の全データの配列を得ようとする場合は、getAll()を使用すべき。

インデックスの使用

  • 検索するとき、正しいものが見つかるまでデータベース内の全ての値に対して反復処理を行わなければならない。この方法だと遅いため、代わりにインデックスを使用する。
  • 指定した値に該当する全ての項目にアクセスしなければならない場合は、カーソルを使用する。インデックス上で、2種類のカーソルを開くことができる。ノーマルカーソルは、インデックスのプロパティと、オブジェクトストア内のオブジェクトを紐付ける。キーカーソルはインデックスのプロパティと、オブジェクトストア内にオブジェクトを保存するために使用するキーを紐づける。
    var index = objectStore.index("name");
    
    // 顧客レコードのオブジェクト全体を得るために、ノーマルカーソルを使用する。
    index.openCursor().onsuccess = function(event) {
      var cursor = event.target.result;
      if (cursor) {
        // cursor.key は "Bill" のような名前、cursor.value はオブジェクト全体。
        console.log("Name: " + cursor.key + ", SSN: " + cursor.value.ssn + ", email: " + cursor.value.email);
        cursor.continue();
      }
    };
    
    // 顧客レコードのオブジェクトのキーを得るために、キーカーソルを使用。
    index.openKeyCursor().onsuccess = function(event) {
      var cursor = event.target.result;
      if (cursor) {
        // cursor.key は "Bill" のような名前、cursor.value は SSN 。
        // 保存されたオブジェクトの他の部分を直接取得する方法はない。
        console.log("Name: " + cursor.key + ", SSN: " + cursor.value);
        cursor.continue();
      }
    };

カーソルの範囲や方向を指定する

  • カーソルで参照する値の範囲を制限したい場合は、IDBKeyRangeオブジェクトを使用して、openCursor()またはopenKeyCursor()の第1引数として渡す
  • 一つのキーのみ許可するキーレンジ、下限または上限の片方を持つキーレンジ、あるいは下限と上限の両方を持つキーレンジを作成できる。
  • 境界はclosed(キーレンジは指定した値を含む)またはopen(キーレンジは指定した値を含まない)
  • 方向の切り替えは、openCursor()の第2引数にprevを渡す
    // "Donna" にのみ一致
    var singleKeyRange = IDBKeyRange.only("Donna");
    
    // "Bill" より先のすべてに一致。"Bill" を含みます。
    var lowerBoundKeyRange = IDBKeyRange.lowerBound("Bill");
    
    // "Bill" より先のすべてに一致。ただし "Bill" は含まない。
    var lowerBoundOpenKeyRange = IDBKeyRange.lowerBound("Bill", true);
    
    // "Donna" までのすべてに一致。ただし "Donna" は含まない。
    var upperBoundOpenKeyRange = IDBKeyRange.upperBound("Donna", true);
    
    // "Bill" から "Donna" までに一致。ただし "Donna" は含まない。
    var boundKeyRange = IDBKeyRange.bound("Bill", "Donna", false, true);
    
    // いずれかのキーレンジを使用するには、openCursor()/openKeyCursor() の第 1 引数として渡す。
    index.openCursor(boundKeyRange).onsuccess = function(event) {
      var cursor = event.target.result;
      if (cursor) {
        // 一致した場合の処理。
        cursor.continue();
      }
    };
    
    objectStore.openCursor(boundKeyRange, "prev").onsuccess = function(event) {
      var cursor = event.target.result;
      if (cursor) {
        // 項目に対して行う処理
        cursor.continue();
      }
    };
    // データを保存例
    
    const dbName = "the_name";
    
    var request = indexedDB.open(dbName, 2);
    
    request.onerror = function(event) {
      // エラー処理
    };
    request.onupgradeneeded = function(event) {
      var db = event.target.result;
    
      // 顧客の情報を保存する objectStore を作成する。
      // "ssn" は一意であることが保証されているので、キーパスとして使用する。
        // 第一引数:ストアの名前
        // 第二引数:引数オブジェクト(省略可能)。
        //   keyPath:ストア内で個々のオブジェクトを一意にするプロパティ
      var objectStore = db.createObjectStore("customers", { keyPath: "ssn" });
    
      // 顧客を名前で検索するためのインデックスを作成。
      // 重複する可能性があるので、一意のインデックスとしては使用できない。
      objectStore.createIndex("name", "name", { unique: false });
    
      // 顧客をメールアドレスで検索するためのインデックスを作成。2 人の顧客が同じメールアドレスを
      // 使用しないようにしたいので、一意のインデックスを使用する。
      objectStore.createIndex("email", "email", { unique: true });
    
      // データを追加する前に objectStore の作成を完了させるため、
      // transaction oncomplete を使用。
      objectStore.transaction.oncomplete = function(event) {
        // 新たに作成した objectStore に値を保存。
        var customerObjectStore = db.transaction("customers", "readwrite").objectStore("customers");
        customerData.forEach(function(customer) {
          customerObjectStore.add(customer);
        });
      };
    
        transaction.onerror = function(event) {
          // エラー制御
        };
    };
    
    // 「スキーマ」や構造の変更
    var transaction = db.transaction(["customers"], "readwrite");
    
    // レコードの読み出し
    var transaction = db.transaction(["customers"], "readonly");
    
    // 変更処理
    var transaction = db.transaction(["customers"], "readwrite");
    
    // データの削除
    var request = db.transaction(["customers"], "readwrite")
                    .objectStore("customers")
                    .delete("444-44-4444");
    request.onsuccess = function(event) {
      // 削除完了!
    };
    
    // データの取得
    var request = db.transaction(["customers"], "readwrite")
                    .objectStore("customers")
                    .get("444-44-4444");
    request.onerror = function(event) {
      // エラー処理!
    };
    request.onsuccess = function(event) {
      // request.result に対して行う処理
      console.log("Name for SSN 444-44-4444 is " + request.result.name);
    };
    
    // データの更新
    request.onsuccess = function(event) {
      // 更新したい、古い値を取得
      var data = request.result;
    
      // オブジェクト内の値を、希望する値に更新
      data.age = 42;
    
      // 更新したオブジェクトを、データベースに書き戻す。
      var requestUpdate = objectStore.put(data);
      requestUpdate.onerror = function(event) {
        // エラーが発生した場合の処理
      };
      requestUpdate.onsuccess = function(event) {
        // 成功 - データを更新
      };
    };
    
    // カーソルを使用したデータの取得
    objectStore.openCursor().onsuccess = function(event) {
        var cursor = event.target.result;
        if (cursor) {
            // 処理
            cursor.continue();
        }
    }
    
    // インデックス
    // 最初に、 request.onupgradeneeded の中にインデックスを生成したか確認する。
    // objectStore.createIndex("name", "name");
    // まだであれば、 DOMException が発生する。
    
    var index = objectStore.index("name");
    
    index.get("Donna").onsuccess = function(event) {
      console.log("Donna's SSN is " + event.target.result.ssn);
    };
    ```
    
    # ウェブアプリが別のタブで開かれているときにバージョンを変更する
    
    ```javascript
    var openReq = mozIndexedDB.open("MyTestDatabase", 2);
    
    openReq.onblocked = function(event) {
      // 他のタブがデータベースを読み込んでいる場合は、処理を進める前にそれらを閉じる。
      console.log("このサイトを開いている他のタブをすべて閉じてください!");
    };
    
    openReq.onupgradeneeded = function(event) {
      // 他のデータベースはすべて閉じられたため、すべての処理を行う。
      db.createObjectStore(/* ... */);
      useDatabase(db);
    };
    
    openReq.onsuccess = function(event) {
      var db = event.target.result;
      useDatabase(db);
      return;
    };
    
    function useDatabase(db) {
      // 別のページがバージョン変更を求めた場合に、通知されるようにするためのハンドラーを追加。
      // データベースを閉じなければならない。データベースを閉じると、別のページがデータベースをアップグレードできる。
      // これを行わなければ、ユーザーがタブを閉じるまでデータベースはアップグレードされない。
      db.onversionchange = function(event) {
        db.close();
        console.log("新しいバージョンのページが使用可能になりました。再読み込みしてください!");
      };
    
      // データベースを使用する処理
    }

セキュリティ

  • 同一生成元の原則。ストアとサイトの生成元を紐づけるため、他の生成元からアクセスできない

ブラウザの終了に関する警告

ロケールを意識した並べ替え

Nuxt2 のこれからと Nuxt3からNuxt4へ

おはこんにちばんわ。

表題の件についてまとめようと思います。 が、すでに先人の方がまとめてくださっていますので、それを参照するだけになります。

Nuxt2の今後について

nuxt.com

こちらにあるように、2024-06-30以降、Nuxt2はメンテナンスされなくなります(End of Life)。 なので、Nuxt3へのバージョンアップを促していますが、Nuxt2を使い続けることもできます。

少なくとも、Nuxt2の最新バージョンに上げる必要はありますが、Nuxt2の拡張サポートを購入することです。 Nuxt2は、HeroDevsによってNES(Never-Ending Support)がされることになっています。

Nuxt3からNuxt4への移行

nuxt.com

公式ドキュメントで、Nuxt3からNuxt4への移行ガイドが記載されています。 また、Nuxt3(3.12以上)で開発されている場合は、Nuxt4の多くの変更点をテストすることができます。

qiita.com

そして、その内容を日本語でまとめてくれているのが、この記事になります。 これらの記事を読みました。

このときのメモを以下に記載します。

useAsyncDatauseFetchのリアクティビティ

ja.vuejs.org

浅いリアクティブになるため、パフォーマンスが最適化される。

builder: watchフックの絶対パス

よく分からない。。。

テンプレートコンパイル

liginc.co.jp

lodash/templateを使って、テンプレートをコンパイルする機能を削除した。

IndexedDB APIを知る①

IndexedDB API

  • ファイルやバイナリデータを含む過大な構造データのクライアントサイドストレージのための低レベルAPI
  • Web Storageは小さいデータの保管に対しては役に立つが、大きいデータには向いていない。この解決策が、IndexedDBになる。
  • これは、Web Workers()内で使用可能である。

主な特徴

  • データを永続的に保存する
  • ネットワークの利用可否に関わらず、豊富なクエリ機能を備えている
  • 大量のデータを保存(貸出用図書館のDVDカタログなど)したり、インターネットへの持続的な接続を必要としない(メールクライアント、Todoリスト、メモ帳など)アプリケーションに有効。
  • keyに基づいてインデックス化されたオブジェクトを保存・取得できる
  • データベースへの変更は、全てトランザクション内で行われる。
  • 同一ポリシンポリシーに従うため、異なるドメイン間のデータにはアクセスできない。

IndexedDBデータベース

  • keyvalueの組を格納し、インデックス化される。
  • keyにはバイナリオブジェクトを使用できる

IndexedDB

  • トランザクショナルデータベースモデルに基づいて構築。
  • 常にトランザクションのコンテキストで行われる。
  • Indexed APIのオブジェクトは、それぞれ特定のトランザクションに関連づけられているため、トランザクション外でコマンドを実行したり、カーソルを開いたりできない。

IndexedDB APIは、ほとんどが非同期

  • 同期的な方法でデータベースを操作できない。
  • 代わりに、データベースの操作を「リクエスト」する。
  • 操作が終了するとDOMイベントで通知され、そのイベントの種類によって操作が成功したか失敗したかがわかる。

IndexedDBは多くのリクエストを使用する

  • リクエストは、前述の成功または失敗のDOMイベントを受け取るオブジェクト。

IndexedDBDOMイベントを使って、結果が利用可能になったことを通知する

  • DOMイベントには、必ずtypeプロパティがあり、イベントの目的地を示すtargetプロパティがある。
  • ほとんどの場合、イベントのtargetは、何らかのデータベース操作の結果として生成されたIDBRequestオブジェクトになる。成功イベントはバブルアップせず、キャンセルもできない。
  • 一方で、エラーイベントはバブリングし、キャンセルも可能。エラーイベントはキャンセルされない限り、実行中のトランザクションを中断する。

IndexedDBオブジェクト指向

  • リレーショナルデータベースではない。
  • データの種類に応じてオブジェクトストアを作成し、そのストアにJavascriptオブジェクトを永続化する必要がある。
  • 各オブジェクトストアには、クエリや反復処理を効率的に行うためのインデックスのコレクションを持つことができる。

IndexedDBSQLを使用しない

  • インデックスに対するクエリを使用してカーソルを生成し、そのカーソルを使用して結果セットを反復処理する。

IndexedDBは、同一オリジンポリシーを採用している

  • 各オリジンには、それぞれ関連するデータベースのセットがある。
  • 全てのデータベースには、オリジン内で識別するための名前がある。
  • IndexedDBにはセキュリティ境界が課せられており、アプリケーションが異なるオリジンのデータにアクセスすることを防ぐ。
    • サードパーティのウィンドウコンテンツは、ブラウザがサードパーティcookieを受け入れないように設定されていない限り、埋め込まれたオリジンのIndexedDBストアにアクセスすることができる。

制限事項

  • 以下のようなケースには対応していない
    • 国際化に対応した並び替え。全ての言語で文字列が同じように並べ替えされるわけではないため。しかし、データベースから読み取ったデータを自動で並び替えするこでできる。
    • 同期。サーバー側のデータベースとの同期を行うようには設計されていない。同期させるコードを書く必要がある。
    • 全文検索SQLLIKEに相当する演算子に相当するものがない。
  • また、以下のような条件でもブラウザがデータベースを消去することがあるので注意が必要
    • ユーザーが消去を要求した場合。
    • ブラウザがプライベートブラウジング/シークレットモードになっている場合。セッションの終了時に、ブラウザはデータベースを消去する。
    • ディスクまたはクォータの容量の上限に達した場合。
    • この機能に対して互換性のない変更が行われた場合。

同期と非同期

  • 非同期に実行するので、他のアプリケーションを妨げない。
  • ただし、同期APIはウェブ開発者から十分な要望がある場合は、再び導入される可能性がある。

ストレージの上限と破棄基準

ブラウザがどれだけの容量をウェブデータストレージに割り当てるかや、容量の上限に達したときにどのデータを削除するかのプロセスは単純ではなく、ブラウザによって異なる。

ブラウザーのストレージ制限と削除基準 - Web API | MDN

インターフェース

  • データベースへのアクセスを行いたい場合、windowオブジェクトのindexedDB属性上でopen()を呼び出す。

IndexedDB API - Web API | MDN

IndexedDBの使用

次の記事へ...