アーキテクチャ
libsonare の内部アーキテクチャについて説明します。
このページは、はじめにと各言語の実行環境ページに慣れてから開く内部地図です。libsonare を拡張したり、より大きなシステムに組み込んだりする人向けで、チュートリアルではありません。API を呼びたいだけなら、まずはじめにから始めてください。ここでは、公開 API が C++ コアのどこにつながるかを示します。
レイヤー図の読み方
外側の API レイヤーはアプリが呼ぶ入口です。コアレイヤーと特徴レイヤーは、再利用される信号処理の実体です。バインディングはできるだけ薄く保ち、各言語の形を同じ C++ の挙動へ橋渡しする役割に留めます。
このページで身につくこと
このページを読むと、次のことを判断・追跡できるようになります。
- WASM、C、quick API、
sonare.hからの公開呼び出しが、解析、ストリーミング、エフェクト、マスタリング、ミキシング、エンジンの各モジュールへどう流れるかを追える。 - 各サブシステムの実装があるソースディレクトリを特定できる。
- 共有 DSP、特徴抽出、リアルタイム処理、言語バインディングがどこで接続するかを理解できる。
- 変更をコアモジュール、言語バインディング、デモコンポーネント、ドキュメントのどこに入れるべきか判断できる。
モジュール概要
以下のレイヤーは上から下へ流れます。API レイヤーはアプリが呼び出す入り口です。スペクトル解析と一部のエフェクトは共通の変換基盤を使いますが、フィルター、ダイナミクス、ミキシングなどには別の信号経路があります。1 つの MusicAnalyzer は一部の中間結果を再利用しますが、別のプロセッサが独自に変換を計算することもあります。各グループは後述の「ディレクトリ構造」セクションの src/ サブディレクトリに対応します。ノードは代表例であり、完全な API 一覧は「ページ対応表」を参照してください。
API レイヤーは、実行環境ごとにさらに分かれています。Python パッケージは ctypes で C ABI を呼びます。WASM(Embind)と Node ネイティブ(N-API)のバインディングは C++ のクラスを直接ラップし、一部の入口でだけ C ABI を使います。CLI はそれぞれ 1 つの実行環境の上に作られています。pip で入る sonare コマンドは Python パッケージの上に、ソースからビルドするネイティブ CLI は C++ の上にあります。
- C++ のクラスを直接呼ぶ
- C ABI を通る
ページ対応表
| 見ている領域 | 読むページ |
|---|---|
analysis/ と feature/ | JavaScript API、Python API、librosa 互換性 |
analysis/acoustic_analyzer.*、analysis/room_estimator.*、src/acoustic/、effects/acoustic/ | ルーム音響解析、アルゴリズム根拠 |
streaming/ | リアルタイムとストリーミング |
mastering/ | マスタリングプロセッサ、DSP 実装解説、マスタリングアシスタント |
mixing/ | ミキシングエンジン、ミキシングシーン JSON、チャンネルフォーマット(downmix) |
playback/ | 再生レンダラー、チャンネルフォーマット |
engine/, transport/, automation/, graph/, rt/ | リアルタイムエンジン |
midi/ と midi/synth/ | 内蔵シンセサイザー、SoundFont 2 プレイヤー、MIDI 入力 |
arrangement/(編集モデル) | プロジェクト編集、録音とテイク、プロジェクトバウンス |
mir/(ワープ・グリッドスナップ・キーコンテキスト) | ワープとテンポ、リアルタイムとストリーミング |
editing/ と effects/ | 編集 DSP、DSP 実装解説 |
sonare_c*.h と binding フォルダ | バインディング対応表、ネイティブバインディング、C++ API |
ディレクトリ構造
src/
├── util/ # レベル 0: 基本ユーティリティ
│ ├── types.h # MatrixView, ErrorCode, 列挙型
│ ├── exception.h # SonareException
│ └── math_utils.h # mean, variance, argmax 等
│
├── core/ # レベル 1-3: コア DSP
│ ├── convert.h # Hz/Mel/MIDI 変換
│ ├── window.h # Hann, Hamming, Blackman
│ ├── fft.h # 変換の抽象層(PFFFT/KissFFT バックエンド)
│ ├── spectrum.h # STFT/iSTFT
│ ├── audio.h # オーディオバッファ
│ ├── audio_io.h # WAV/MP3 読み込み、任意で FFmpeg 対応形式
│ └── resample.h # r8brain リサンプリング
│
├── filters/ # レベル 4: フィルターバンク
│ ├── mel.h # Mel フィルターバンク
│ ├── chroma.h # Chroma フィルターバンク
│ ├── dct.h # MFCC 用 DCT
│ └── iir.h # IIR フィルター
│
├── feature/ # レベル 4: 特徴抽出
│ ├── mel_spectrogram.h
│ ├── chroma.h
│ ├── cqt.h
│ ├── vqt.h
│ ├── inverse.h
│ ├── spectral.h
│ ├── onset.h
│ └── pitch.h
│
├── effects/ # レベル 5: オーディオエフェクト
│ ├── hpss.h
│ ├── phase_vocoder.h
│ ├── time_stretch.h
│ ├── pitch_shift.h
│ ├── normalize.h
│ ├── preemphasis.h
│ ├── silence.h
│ ├── decompose.h
│ ├── remix.h
│ ├── delay/ modulation/ reverb/
│ ├── acoustic/ # room_morph
│ └── common/
│
├── acoustic/ # 幾何ベースのルーム音響
│ ├── room_model.* room_types.* material.*
│ ├── image_source.* # 初期反射
│ ├── late_reverb.* # 決定的な後期テール
│ └── rir_synthesizer.*
│
├── analysis/ # レベル 6: 音楽解析
│ ├── music_analyzer.h
│ ├── bpm_analyzer.h
│ ├── key_analyzer.h
│ ├── beat_analyzer.h
│ ├── downbeat_analyzer.h
│ ├── meter_analyzer.h
│ ├── chord_analyzer.h
│ ├── section_analyzer.h
│ ├── boundary_detector.h
│ ├── melody_analyzer.h
│ ├── rhythm_analyzer.h
│ ├── timbre_analyzer.h
│ ├── dynamics_analyzer.h
│ ├── acoustic_analyzer.h
│ ├── room_estimator.h
│ └── ...
│
├── streaming/ # レベル 6: リアルタイムストリーミング
│ ├── stream_analyzer.h # メインストリーミングアナライザー
│ ├── stream_config.h # 設定オプション
│ └── stream_frame.h # フレームとバッファ型
│
├── mastering/ # マスタリングエンジン
│ ├── api/ # チェーン・30 プリセット・named-processor と pair/stereo のレジストリ
│ ├── eq/ dynamics/ spectral/ stereo/ final/
│ ├── maximizer/ multiband/ saturation/ repair/ utility/
│ ├── match/ assistant/ # リファレンスマッチ+アシスタント/プロファイル
│ └── common/ # 共有 biquad/ラウドネスヘルパー
│
├── mixing/ # ミキシングエンジン
│ ├── channel_strip.* # ストリップ: トリム/インサート/パン/幅/センド
│ ├── bus.* sends.* vca_group.* panner.*
│ ├── downmix.* # ITU-R BS.775 チャンネル折りたたみ
│ └── api/ # シーン JSON +シーンプリセット
│
├── midi/ # MIDI 入出力と内蔵インストゥルメント
│ ├── synth/ # NativeSynth のボイス群+SoundFont プレイヤー
│ │ ├── native_synth.* # 17 エンジンモード: 物理モデル 11 種+その他 6 種
│ │ ├── ks_/piano_/pipe_organ_/bowed_string_/reed_/brass_/flute_/... voice.*
│ │ ├── sf2_player.* sf2_file.* sf2_voice.* # SoundFont(SF2)再生
│ │ └── synth_presets.* gm_fallback_map.* gs_layer.* gs_effects.*
│ ├── sequencer.* smf.* smf2.* ump.* # シーケンサー・SMF・MIDI 2.0(UMP)
│ └── program_map.* cc_map.* midi_fx.* routing.*
│
├── playback/ # 再生レンダラー: スピーカーレイアウト・バイノーラル HRTF・ラウドネス
├── arrangement/ # 非破壊の編集モデル・編集コンパイラ・編集履歴
├── engine/ # リアルタイムエンジン: トランスポート・クリップ・インストゥルメントラック・トラックミキサー・メトロノーム・テレメトリ
├── automation/ # オートメーションレーン+カーブ形状
├── metering/ # LUFS・トゥルーピーク・位相スコープ/ゴニオメーター
├── mir/ # ワープ・グリッドスナップ・キーコンテキスト・テンポ推定ブリッジ
├── graph/ rt/ transport/ # DSP グラフ・RT セーフ基盤・トランスポート
├── editing/ # ピッチエディター・ボイスチェンジャー・ノートストレッチ
├── serialize/ # プロジェクトの直列化/復元
├── host/ # オーディオデバイス/MIDI 入出力/プラグインホストのバックエンド(ネイティブのみ)
│
├── quick.h # シンプル関数 API
├── sonare.h # 統合インクルードヘッダー
├── c_api/ # C API 実装(公開ヘッダーは include/sonare/sonare_c*.h)
└── wasm/
└── bindings.cpp # Embind バインディングデータフロー
オーディオ解析パイプライン
この図は主な特徴量の関係を示すもので、すべてが 1 つの STFT を共有するという意味ではありません。1 つの MusicAnalyzer では、通常のクロマグラムがキャッシュ済みのスペクトログラムを使い、メル表現から求めたオンセット強度を BPM とビート追跡が使います。キーとコードの解析で使う倍音/CQT クロマは、追加の変換を計算することがあります。MusicAnalyzer.analyze() は設定で有効な任意項目を含む AnalysisResult を作り、同じアナライザーで得られた中間結果を再利用します。
オーディオエフェクトパイプライン
HPSS(音を倍音成分と打撃成分に分ける処理)とフェーズボコーダーは、どちらも複素 STFT データを処理し、逆 STFT で音声に戻します。図が示すのは処理方式の共通点であり、同じ変換インスタンスや窓・ホップ長の一致ではありません。timeStretch にはネイティブのスペクトル処理とフェーズボコーダーへのフォールバックがあり、ピッチシフトはタイムストレッチとリサンプリングを組み合わせます。
フェーズボコーダーとは?
フェーズボコーダーは、あるホップ幅で解析した音を、r 倍のホップ幅で合成し直して伸縮する手法です。各ビンの位相を「測定した周波数 × 新しいホップ幅」だけ進めるので、正弦波成分がフレームをまたいでも途切れません。図解はタイムストレッチとピッチシフトを参照してください。リサンプリングと組み合わせればピッチシフトにも使えます。伸縮中のピッチはおおむね保たれますが、過渡音がにじんだり、極端な倍率で不自然に聞こえたりすることがあります。libsonare の timeStretch にはフェーズボコーダーを使うバックエンドがあります。ほかの編集経路では別のスペクトル処理方式を使う場合もあります。
ストリーミングパイプライン
ストリーミングパイプラインは、チャンク間のオーバーラップ状態を維持しながらリアルタイムで音声を処理します。44.1 kHz を超えるストリームは、まず状態を持つリサンプラーで 44.1 kHz に変換されます。Mel・Chroma・オンセットはパワースペクトルを、スペクトル特徴はマグニチュードを入力にします。リングバッファに入ったフレーム単位の特徴量を量子化するかどうかは任意です。既定では Float32 のフル精度を保ちますが、8-bit/16-bit へのパッキングを選ぶと、精度と引き換えにバッファを転送用に縮小できます。
更新される推定
音声がストリーミングされるほど、パイプラインはクロマとオンセットのデータを蓄積していき、BPM/キー推定が根拠にできる材料が増えます。推定は定期的に更新され(既定値: BPM は 10 秒ごと、キーは 5 秒ごと)、ストリームが長く続くほど信頼度が高まります。キーはストリーム自身の 186 ms のクロマから推定するため、フレーム出力より最大で約 4 分の 1 秒遅れます。
主要な設計判断
遅延初期化
MusicAnalyzer は個別のアナライザーを遅延初期化します。必要になったタイミングで初めて中間特徴量(STFT、クロマ、オンセット包絡線など)を計算し、その後の問い合わせでは結果を再利用します。
// BPM のみ計算(オンセット包絡線まで)
float bpm = analyzer.bpm();
// キー検出はクロマ計算をトリガー
Key key = analyzer.key();
// 総合解析は残りの項目をまとめて計算
AnalysisResult result = analyzer.analyze();何が嬉しいか
キーだけ欲しい呼び出しでは、コード認識やセクション検出までは計算しません。analyze() は同じ MusicAnalyzer で計算済みの中間結果を再利用しますが、別の特徴量を求める経路では追加の変換が行われることもあります。
ゼロコピースライシング
Audio は shared_ptr とオフセット/サイズでゼロコピースライシングを実現:
auto full = Audio::from_file("song.mp3");
// 両方とも同じバッファを共有
auto intro = full.slice(0, 30); // 0-30 秒
auto chorus = full.slice(60, 90); // 60-90 秒WASM 互換性
「デコード済みサンプル」とは、.mp3 や .wav ファイルのバイト列ではなく、生のオーディオ振幅値(Float32Array)を指します。デコードとは、圧縮されたファイルをその値へ変換する工程のことです。ほとんどの WASM 呼び出しは、すでにデコードされたサンプルを受け取ります。
npm / WebAssembly パッケージは主にサンプルベースの API を公開しています。多くの呼び出しはデコード済みのモノラル Float32Array サンプルを受け取ります。エンコード済みバイト列については、Audio.fromMemory(...) が WAV/MP3 をメモリ内でデコードし、Audio.fromMemoryWithBrowserFallback(...) は同じサンプルベース API に渡す前に Web Audio API などのブラウザコーデックへ切り替えられます。どちらもマルチチャンネルのファイルは ITU-R BS.775 の規則でモノラルに折りたたみます。decodeChannels(...) は全チャンネルを保持し、downmix(...) は同じ規則を必要なときに適用します。
WASM ビルドでは、ネイティブのファイル I/O や FFmpeg ベースのデコードは使いません。実行はシングルスレッドです。
librosa 互換性
多くの DSP パラメータは librosa で一般的な値に寄せていますが、libsonare は librosa の完全なドロップイン置き換えではありません。特に libsonare は通常、呼び出し側がサンプルレートを渡します。librosa.load() のように標準で 22050 Hz へ暗黙にリサンプルするわけではありません。
| パラメータ | 既定値 |
|---|---|
| sample_rate | ユーザー指定 |
| n_fft | 2048 |
| hop_length | 512 |
| n_mels | 128 |
| fmin | 0 |
| fmax | sr/2 |
サードパーティライブラリ
| ライブラリ | 用途 | ライセンス |
|---|---|---|
| KissFFT | FFT | BSD-3-Clause |
| PFFFT | SIMD FFT(ネイティブビルド) | BSD-3-Clause |
| Eigen3 | 行列演算(ビルド時のみ) | MPL-2.0 |
| dr_libs | WAV デコード | Public Domain |
| minimp3 | MP3 デコード | CC0-1.0 |
| FFmpeg | 任意の拡張ファイルデコード | リンクするビルドにより LGPL/GPL |
| r8brain | リサンプリング | MIT |
| SADIE II HRTF サブセット | 再生レンダラーの既定のバイノーラル HRTF セット(コードではなくデータ) | Apache-2.0 |
1 つのヘッダーの裏に 2 つの FFT バックエンド
core/fft.h は特定ライブラリへの結び付きではなく、変換の抽象層です。ネイティブビルドでは、PFFFT が因数分解できる変換長をすべて受け持ち、残りを KissFFT が受け持ちます。どちらが使われても受け付ける n_fft の範囲は同じで、違うのは速度だけです。KissFFT のみのネイティブビルドにしたい場合は -DSONARE_USE_PFFFT=OFF を指定します。
WebAssembly ビルドは意図的に KissFFT のみです。2 つの変換を同時に抱えると、解析専用モジュールのサイズ予算を超えてしまううえ、ネイティブ SIMD が使えない環境では速度差も出ないためです。
Eigen は利用側の依存ではありません
Eigen はコア自身の翻訳単位の内部で使われ、インストールされるヘッダーはどれも Eigen を include しません。したがって利用側がリンクしたり include したりするものではなく、ビルド時の依存です。インストール済みの libsonare に対してビルドするのに必要なのは C++17 コンパイラだけです。
WASM コンパイル
出力: WASM 単体 ~7,808 KB(gzip ~2,656 KB)+ JS バインディングコード。
JS ローダーと API バンドルを含めて ~8,500 KB(gzip ~2,792 KB)。src/wasm/meta.json を参照
ビルド: Emscripten + Embind
フラグ: -sWASM=1 -sMODULARIZE=1 -sEXPORT_ES6=1バンドルサイズが大きいのは、マスタリング・ミキシング・解析の全機能を含むためです。公開済みの @libraz/libsonare/analysis ビルドは、マスタリング・ミキシング・リアルタイム・プロジェクトの各サーフェスを外しているため、かなり小さくなります。
やらないこと
libsonare はヘッドレスのエンジンであって、アプリケーションではありません。この境界は意図的なもので、以下はすべてその外側です。
| 含まないもの | 理由 |
|---|---|
| UI・DAW ワークフロー | エンジンが返すのはデータ構造です。アレンジ・編集・再生の UI は、その上に載るアプリケーション側の領分です。 |
| サードパーティプラグインのホスティング(VST/CLAP) | ホストするということは、別のプラグイン ABI とそのスレッドモデル、そしてライセンス条件を引き受けるということです。 |
| クロスプラットフォームのリアルタイム I/O 抽象 | どのホスト環境にも既にあり、それらをまとめて包んでもプラットフォーム固有の依存が増えるだけです。 |
| 音声サンプルや楽器サンプルライブラリの同梱 | 音声コーパスにはそれぞれのライセンスがあり、ライブラリ側に付いて回ります。同梱するデータは、上に挙げた小さな HRTF セットだけです。 |
| ノートレベルの作曲 | メロディやハーモニーを書くことは、解析やレンダリングとは別の関心事です。別プロジェクトの @libraz/libcantus が担います。 |
| ディープラーニングモデル | 重みは大きく、ライセンスをきれいに保つのが難しく、ビルドが実行時ランタイムに依存することになります。 |
| Windows 対応 | Linux・macOS・WebAssembly・WSL2 のいずれかを使ってください。 |
| オーディオコールバックの管理 | コールバックと UI はいずれも呼び出し側が持ちます。この境界の唯一の例外は実験的な macOS バックエンドです(明示的に有効化した場合のみ、未公開)。 |
これらをまとめて外していることで、ライブラリは依存を持たず、ライセンスも互換性のある寛容なものに収まっています(FFmpeg は任意で、WASM では無効です)。他のソフトウェアに安心して組み込めるのは、この性質があるからです。