Skip to content

クリップとトラック ​

DAW で曲をアレンジするとき、実際にやっているのは小さくて見通しのよい構造を埋めていく作業です。その構造を理解すれば、アレンジ機能の全体が見通せます。このページでは Project Editing ガイドを読む前に、libsonare が使うプロジェクトモデルを平易な言葉で説明します。

ひとことで言うと

プロジェクトがトラックを持ち、各トラックがクリップを持ち、すべてのクリップは小節と拍で測る共通のタイムライン上に並びます。

プロジェクトモデルを上から下へ ​

プロジェクトは曲全体です。ほかのすべてが入る容れ物であり、保存・読み込みし、最後に バウンス して完成ファイルにする対象でもあります。

プロジェクトの中にはトラックがあります。トラックはコンテンツの 1 本のレーンで、アレンジビューの横 1 行にあたります。各トラックは、オーディオトラック、MIDI トラック、aux(ルーティング/リターン)トラックのいずれかです。

  • オーディオトラックは録音または読み込んだ音そのものを保持します(ボーカルのテイク、ギター、ドラムループなど)。
  • MIDI トラックは音ではなくノートを保持します。ノートは「いま C4 を鳴らし、1 拍伸ばす」といった指示であり、楽器が音に変換して初めて聞こえるようになります。
  • aux トラックはエフェクトリターンやサブミックスのようなルーティング用のレーンです。録音内容を置く場所ではありません。

各トラックの中にはクリップがあります。クリップはタイムライン上の特定の位置に置かれた、コンテンツの 1 ブロックです。オーディオクリップはある音声素材を指し、MIDI クリップはノートイベントのリストを持ちます。1 本のトラックに、隙間を空けたり並べたりしながら、いくつものクリップを置けます。

ENGINE · LANE MIXERIDLE
エンジンのレーンミキサー — 再生エンジン内のフェーダーとミュート

3 つの MIDI クリップがリアルタイムエンジンでループします。各トラックはレーンを 1 つ占有し、専用のチャンネルストリップを持ち、レーンの合計はエンジンの True Peak リミッターを載せたマスターストリップへ送られます。フェーダーはストリップのセッターを、ミュートは setSoloMute を呼びます。下の各バンドは各レーンをソロにしてマスターチェーンを通した個別の試聴結果で、操作のたびに renderOffline で描き直されます。

リードのフェーダー
0 dB
ベースのフェーダー
3 dB
ドラムのフェーダー
-8 dB
リードをミュート
ベースをミュート
ドラムをミュート
プロジェクト → トラック → クリップ → 楽器
ルーティングレンダリングプロジェクトオーディオトラック:Vocalsオーディオトラック:DrumsMIDI トラック: Bassクリップ: Aメロのボーカルクリップ:サビのボーカルクリップ: ドラムループクリップ: ベースラインMIDIデスティネーション楽器: NativeSynth/ SoundFont
オーディオトラックはすでに音を保持しているため、そのまま再生されます。一方、MIDI トラックのクリップが持つのはノートだけなので、トラックは別途、楽器にバインドされたデスティネーションへルーティングし、バウンス時にそこで音がレンダリングされます。

秒ではなく音楽的な時間で ​

多くの初心者がつまずくのがここです。クリップの位置は秒ではなく音楽的な時間で記録されます。

クリップは「4.27 秒の位置」から始まるのではありません。「3 小節目の 1 拍目」から始まります。その下にある単位は 4 分音符で、位置と長さは浮動小数点数で表します。1.0 がちょうど 4 分音符 1 個ぶんです。たとえば 4/4 の 1 小節は 4 分音符 4 個ぶんなので 4.0、8 分音符 1 個は 0.5、付点 4 分音符は 1.5 です。整数のティックや 960 ベースの分解能ではありません。パラメータ名に Ppq(pulses per quarter note の略。startPpq、lengthPpq など)が付いていても同様です。設定するクリップの開始位置や長さは、すべてこの 4 分音符単位(浮動小数点数)で、lengthPpq: 4 は 4 分音符 4 個ぶんの長さです。

音楽的な時間が大切な理由

位置が音楽的に記録されているため、テンポを変えてもアレンジは正しいまま保たれます。120 BPM から 100 BPM に変えれば「3 小節目の 1 拍目」は秒で見れば後ろにずれますが、それでも 3 小節目の 1 拍目です。MIDI ノートがグリッドから外れて流れていくことはありません。オーディオは事情が違います。録音には固有の速さがあるので、テンポを変えてもオーディオクリップは同じ小節から始まりますが、中の録音はワープで追従させない限りネイティブの速さで再生されます。音楽的な時間から実際のサンプル位置への変換は、後でプロジェクトをコンパイル・レンダリングするときに行われます。計算方法は次の節で説明します。

秒・サンプル・PPQ の換算 ​

クリップとイベントは PPQ で置きますが、解析結果(オンセット、コード、ビート)は秒やサンプルで返り、ワープアンカーはサンプルで指定します。Project には換算用の呼び出しがないので、換算は呼び出し側で行います。換算はテンポマップから決まります。

テンポが一定の場合:4 分音符 1 つの長さは 60 / bpm 秒です。

text
seconds = ppq × 60 / bpm          ppq = seconds × bpm / 60
samples = round(seconds × sampleRate)

120 BPM、48 kHz なら、ppq: 1 は 0.5 秒、24000 サンプルです。

ランプするセグメント:ランプの中では 4 分音符が 1 つ進むごとに前より少しずつ短くなるので、ある位置までの時間は、セグメントの開始からの 60 / bpm(4 分音符 1 個あたりの秒数)の面積です。endBpm を持つテンポセグメントの中では、テンポは 4 分音符あたり一定の割合で変わり、セグメントの startPpq(p0)での bpm0 から、次のセグメントの開始位置(p1)での bpm1 へ直線的に進みます。k = (bpm1 − bpm0) / (p1 − p0) とすると、p0 からセグメント内の位置 p までの時間は次のとおりです。

text
t = (60 / k) × ln(1 + k × (p − p0) / bpm0)        逆変換:  p = p0 + bpm0 × (e^(k × t / 60) − 1) / k

これに、そのセグメントの開始時刻(それより前のセグメントの長さの合計)を足します。

テンポマップとテンポセグメントのマップ(PPQ 0 から 120 BPM、PPQ 16 から 120→140 BPM のランプ、PPQ 32 から 140 BPM)で計算してみます。最初のセグメントが 120 BPM で 4 分音符 16 個ぶんなので、PPQ 16 は 8 秒です。ランプは k = (140 − 120) / 16 = 1.25 なので、PPQ 24 は 8 + 48 × ln(130 / 120) ≈ 11.842 秒、PPQ 32 は 8 + 48 × ln(140 / 120) ≈ 15.399 秒(48 kHz で 739163 サンプル)です。同じセグメントを渡したリアルタイムエンジンの sampleAtPpq(ppq) も同じサンプル位置を返します。

ワープアンカーの場合:ワープアンカーの warpSample は、クリップ自身の先頭からのサンプル数です。PPQ 4 から始まるクリップで、録音のソースサンプル 95000 にある小節頭を PPQ 8 へ合わせるとします。テンポが 120 BPM で一定、48 kHz なら、PPQ 8 はクリップ先頭から (8 − 4) × 0.5 秒 = 2 秒後なので、アンカーは { warpSample: 96000, sourceSample: 95000 } です。テンポマップがある場合は、換算した 2 つの時刻の差を使います。

MIDI トラックが音になるまで ​

オーディオトラックは自己完結しています。すでに音を持っているので、そのまま再生されます。MIDI トラックは違います。クリップはノートイベントしか持たないため、そのノートを演奏する何かが必要です。

その何かに届けるのが、トラックの MIDI 送出先(デスティネーション)です。デスティネーションは番号の付いたルーティング枠です。内蔵シンセサイザー(NativeSynth)や SoundFont プレイヤーといった楽器をデスティネーションにバインドし、トラックをそこへルーティングすると、トラックのノートがその楽器へ送られ、実際の音声が合成されます。

手順やること結果
1MIDI トラックとクリップを追加ノートはあるが無音のレーン
2クリップにノートイベントを入れる演奏内容が書き込まれる
3トラックをデスティネーションへルーティングノートに行き先ができる
4そのデスティネーションに楽器をバインドレンダリング時にノートが音になる

この分離のおかげで、同じ MIDI クリップを、デスティネーションの楽器を入れ替えるだけで、今日はシンセ、明日はサンプル楽器で鳴らせます。

手順 2「演奏内容が書き込まれる」は、そのまま楽譜です。クリップのノートイベントは標準的な記譜法にそのまま対応します。下はあるクリップを記譜して演奏したもので、楽器を切り替えれば同じ音符が別の音色で鳴ります。

MIDI · SCOREIDLE
同じ MIDI を、楽譜で読む

上のピアノロールとまったく同じ3声フレーズ(旋律・分散和音・低音)を、大譜表に標準記譜したものです。音符・音価・連桁・音部記号は、エンジンが鳴らす MIDI からそのまま起こしています。楽器を切り替えれば同一の音符が別の内蔵音色で鳴り、テンポを動かせば演奏が速くなります。再生すると、鳴っている音符が鳴った瞬間に光ります。

楽器
テンポ
96 BPM

クリップとトラックに対してできること ​

構造ができてしまえば、アレンジ作業の大半は少数の操作です。

クリップ操作は日常的な動かし方です。

  • クリップを新しい位置へ移動する(必要なら別のトラックへ)。
  • ゲインを設定して、隣のクリップより大きく / 小さくする。
  • フェードイン / フェードアウトで、クリックノイズなく滑らかに出入りさせる。
  • ループさせて、短いブロックを長い区間に繰り返す。
  • 複製して、パートを別の場所へコピーする。
  • ソースの差し替えで、別の素材を指すようにする。
  • 不要になったクリップを削除する。

トラック操作はレーンそのものの管理で、トラックの追加・削除・改名、ルーティング(ミキサーストリップや出力へのバインド、MIDI デスティネーションの設定)、オーディオ・MIDI・aux の種別の変更があります。

クリップはコピーではなく参照

複数のクリップが同じ素材を指すことがあります。オーディオクリップを複製しても、音声を丸ごと複製せずソースを共有するので、プロジェクトは小さく保たれます。クリップのソースを差し替えて変わるのはそのクリップだけで、ほかのクリップは共有のソースを指したままです。パートをコピーして回すときは覚えておいてください。

フェードカーブとクロスフェード ​

フェードは、フェード長のあいだクリップに 0 から 1(フェードイン)、または 1 から 0(フェードアウト)へ変わるゲインを掛けます。フェードの進んだ割合を x とすると、setClipFade が受け付ける 4 つのカーブは次のとおりです。

カーブx でのゲイン中間点のゲインフェードインとしてフェードアウトとして
'linear'x0.5(−6 dB)振幅が一定の割合で上がる振幅が一定の割合で下がる
'equal-power'sin(x · π/2)0.707(−3 dB)早めに上がり、その後なだらかになるしばらく保ち、後半で下がる
'exponential'x²0.25(−12 dB)小さいまま進み、終盤で上がる早めに下がり、小さい音で尾を引く
'logarithmic'√x0.707(−3 dB)ごく早い段階で上がる最も長く保ち、最後に下がる

フェードアウトは同じカーブを時間方向に反転して使い、x は残りの割合を数えます。名前は形を表しているだけで、'exponential' は 2 乗、'logarithmic' は平方根の式であり、デシベルで直線になるカーブではありません。

カーブの選び方がいちばん効くのは、重なったクリップ間のクロスフェード、ループの継ぎ目、コンプの継ぎ目のように 2 つの信号が交差する場面です。決め手は、2 つの信号がどれだけ似ているかです。

  • 無関係な素材(別々のテイク、別の音):パワーが足し合わされるので、ゲインは gain_out² + gain_in² = 1 を満たすべきです。'equal-power' の組(または 'logarithmic' の組)がこれを満たし、レベルは平らに保たれます。'linear' の組では中央で約 3 dB 下がります。
  • ほぼ同じ素材(両側が同じ録音、一致する箇所を選んだループ点):波形が同相で足し合わされるので、ゲインは gain_out + gain_in = 1 を満たすべきです。'linear' の組がこれを満たし、等電力の組では 0.707 + 0.707 = 1.41 となるため中央で約 3 dB 上がります。

一般には、レベルを保つゲインは g_in² + g_out² + 2ρ·g_in·g_out = 1 を満たします。ρ は両側の相関(無関係なら 0、同一なら 1)です。noteStretch と renderNotes は継ぎ目ごとにこれを解いています。

フェードカーブとクロスフェードの合計
フェードインのゲイン00.250.50.75100.51フェードの進み xクロスフェードの合計レベル−30+3dB00.51フェードの進み x+3.0 dB−3.0 dBほぼ同じ素材無関係な素材
  • 'linear' x
  • 'equal-power' sin(x·π/2)
  • 'exponential' x²
  • 'logarithmic' √x
左:各カーブでフェードインしたときのゲインです。点は表の中間点のゲインを示します。右:同じカーブのフェードアウトとフェードインを足し合わせた結果です。実線はほぼ同じ素材で振幅として足され、破線は無関係な素材でパワーとして足されます。linear は前者で平坦、後者で 3 dB 下がり、等電力はその逆になります。

同じ比較を耳で確かめられます。重なった2つのクリップをエンジンでクロスフェードし、つなぎ目を通して合計のレベルを測ります。

CROSSFADE · FADE CURVESIDLE
クロスフェードのカーブ — つなぎ目の合計

網掛けの区間で、クリップ A がフェードアウトし、クリップ B がフェードインします。上段はエンジンが掛けたゲイン、下段はその合計を測ったレベルです。両側が同じ録音なら振幅で足し合わされます。linear は平らなままで、equal-power と logarithmic はつなぎ目で約 3 dB 膨らみ、exponential は約 6 dB へこみます。別々のコードならパワーで足し合わされます。linear は約 3 dB へこみ、equal-power と logarithmic は平らなままで、exponential は約 9 dB へこみます。フェードを長くすると、へこみや膨らみを聴き取りやすくなります。

カーブ
素材
フェード長
2 s

ループ継ぎ目のクロスフェード(loopCrossfadePpq)とコンプ継ぎ目のクロスフェード(コンプセグメントの crossfadePpq)は常に等電力で、無関係な素材に向いています。単独のフェードインやフェードアウトは交差する相手がないので、カーブはフェードをどう聞かせたいかで選びます。

ソースのメタデータと Undo 履歴 ​

個々のクリップより一段上に位置する編集がもう 2 つあります。

オーディオソースのメタデータ。複数のクリップが 1 つのソースを共有できるため(上記の警告を参照)、情報を持たせるべきなのはクリップではなくソース側であることがよくあります。setAudioSourceMetadata(sourceId, contentHash, externalStemRole) は、1 回の undo 可能な編集として、ソースに 2 つの情報文字列を付与します。contentHash は、同じ音声実体が別のソース id で再登場したことをホスト側が認識するために使えます(重複インポートのスキップ、古い参照の検出など)。externalStemRole は、外部で分離されたステムが何を表すか(たとえば「vocals」や「drums」)を記録するもので、ステム分離を前提としたプロジェクトが保存・再読み込みの後もステムを区別できるようにします。

Undo/Redo 履歴のバイト上限。このモデルにおけるあらゆる編集(moveClip、setClipGain、addAutomationLane など)は undo スタックに保持され、undo() / redo() でセッションを前後に移動できます。各編集コマンドは自身の保持バイト数を報告し、Project.setMaxHistoryBytes(bytes) は undo と redo 両方のスタックが保持できる合計メモリ量に上限を設けます。上限に達すると、古いエントリから順に破棄されて空きが作られます。上限を 0 にすると履歴の保持自体が無効になります。setMaxUndoDepth は同じ履歴を別の軸(バイト数ではなくエントリ数)で制限するもので、2 つの上限は併用されます。

libsonare での実装

このモデルは編集エンジンの Project クラスにあります。addTrack がトラック(audio または midi)を作り、addMidiClip(startPpq, lengthPpq) は新しい MIDI トラック + クリップに対して { trackId, clipId } を返します。addClip は既存トラックへオーディオ / MIDI クリップを追加します。すべての位置は 4 分音符単位(浮動小数点数、1.0 = 4 分音符 1 個)なので、moveClip(clipId, newStartPpq, newTrackId?)、setClipGain(clipId, gain)、setClipFade(clipId, fadeIn, fadeOut)、setClipLoop、duplicateClip、setClipSource、removeClip はすべて音楽的な時間で動きます。トラックのライフサイクルは removeTrack、renameTrack、setTrackKind、setTrackRoute です。MIDI トラックは setTrackMidiDestination(trackId, destinationId) で楽器へつながります。コンパイラが各 MIDI クリップにその id を付与するため、エンジンはそのデスティネーションにバインドされた楽器へイベントを送ります。プロジェクト全体は toJson() / Project.fromJson(json) で JSON を往復し、音楽的な時間は compile() / bounce() のときに初めてサンプルへ変換されます。

関連: Project Editing、テイクとコンピング、ミキシングの基礎