Skip to content

アーキテクチャ ​

libsonare の内部アーキテクチャについて説明します。

このページは、はじめにと各言語の実行環境ページに慣れてから開く内部地図です。libsonare を拡張したり、より大きなシステムに組み込んだりする人向けで、チュートリアルではありません。API を呼びたいだけなら、まずはじめにから始めてください。ここでは、公開 API が C++ コアのどこにつながるかを示します。

レイヤー図の読み方

外側の API レイヤーはアプリが呼ぶ入口です。コアレイヤーと特徴レイヤーは、再利用される信号処理の実体です。バインディングはできるだけ薄く保ち、各言語の形を同じ C++ の挙動へ橋渡しする役割に留めます。

このページで身につくこと ​

このページを読むと、次のことを判断・追跡できるようになります。

  • WASM、C、quick API、sonare.h からの公開呼び出しが、解析、ストリーミング、エフェクト、マスタリング、ミキシング、エンジンの各モジュールへどう流れるかを追える。
  • 各サブシステムの実装があるソースディレクトリを特定できる。
  • 共有 DSP、特徴抽出、リアルタイム処理、言語バインディングがどこで接続するかを理解できる。
  • 変更をコアモジュール、言語バインディング、デモコンポーネント、ドキュメントのどこに入れるべきか判断できる。

モジュール概要 ​

以下のレイヤーは上から下へ流れます。API レイヤーはアプリが呼び出す入り口です。スペクトル解析と一部のエフェクトは共通の変換基盤を使いますが、フィルター、ダイナミクス、ミキシングなどには別の信号経路があります。1 つの MusicAnalyzer は一部の中間結果を再利用しますが、別のプロセッサが独自に変換を計算することもあります。各グループは後述の「ディレクトリ構造」セクションの src/ サブディレクトリに対応します。ノードは代表例であり、完全な API 一覧は「ページ対応表」を参照してください。

モジュール概要
API レイヤーストリーミングレイヤー解析レイヤーエフェクトレイヤーインストゥルメント&MIDIマスタリング&ミキシング特徴レイヤーコアレイヤー音響シミュレーションWASM バインディング(Embind)C API (sonare_c*.h)Quick API (quick.h)sonare.h(統合ヘッダー)StreamAnalyzerStreamFrame/ FrameBufferMusicAnalyzerBPM・キー・ビート・コード・セクション・境界音色・ダイナミクス・リズム・メロディAcousticAnalyzer/ RoomEstimatorHPSS・タイムストレッチ・ピッチシフト正規化・無音トリム・プリ/ディエンファシス分解・リバーブ・クリエイティブFXRoomMorph・ボイス変換NativeSynth(17エンジンモード)SoundFontプレイヤー(SF2)MIDI・シーケンサー・SMF/UMPインストゥルメントラックMasteringChainStreamingMasteringChain/ EQMixer(ストリップ/バス/センド)RealtimeEngineメータリング(LUFS/TruePeak)PlaybackRenderer(スピーカー/バイノーラル)Mel・Chroma・CQT/VQTスペクトル・オンセット・ピッチ逆変換特徴量(再構成)AudioSpectrogram(STFT/iSTFT)FFT・窓関数・リサンプリング・I/ORoom ModelRIR SynthesizerMaterial Presets
バインディングは薄く保たれています。WASM/C/Quick/sonare.h はすべて同じ C++ コアに集約され、バインディングごとに DSP を再実装することはありません。

API レイヤーは、実行環境ごとにさらに分かれています。Python パッケージは ctypes で C ABI を呼びます。WASM(Embind)と Node ネイティブ(N-API)のバインディングは C++ のクラスを直接ラップし、一部の入口でだけ C ABI を使います。CLI はそれぞれ 1 つの実行環境の上に作られています。pip で入る sonare コマンドは Python パッケージの上に、ソースからビルドするネイティブ CLI は C++ の上にあります。

各実行環境からコアまで
CLIバインディングC ABIコアPython CLIネイティブ CLI sonare_c*.h WASMEmbindNode ネイティブN-API addonPythonctypesC++ APIsonare.hC++ コア: DSP、解析、ミキシング、エンジンどの経路も同じコアに届きます。バインディングは形を変換するだけで、DSP を再実装しません。
  • C++ のクラスを直接呼ぶ
  • C ABI を通る
C ABI を通るのは一部の経路だけです。どの経路を通っても、呼び出しは同じ C++ コアに届きます。

ページ対応表 ​

見ている領域読むページ
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 を作り、同じアナライザーで得られた中間結果を再利用します。

オーディオ解析パイプライン
入力コア特徴解析出力オーディオファイル(WAV/MP3)生バッファ (float*)AudioSTFTSpectrogramMel Spectrogram和声用の変換オンセット強度BPM 検出キー検出ビート追跡コード認識AnalysisResult
特徴量の関係を示した図です。1 つの MusicAnalyzer 内で再利用する中間結果もあれば、別の変換を計算する経路もあります。

オーディオエフェクトパイプライン ​

HPSS(音を倍音成分と打撃成分に分ける処理)とフェーズボコーダーは、どちらも複素 STFT データを処理し、逆 STFT で音声に戻します。図が示すのは処理方式の共通点であり、同じ変換インスタンスや窓・ホップ長の一致ではありません。timeStretch にはネイティブのスペクトル処理とフェーズボコーダーへのフォールバックがあり、ピッチシフトはタイムストレッチとリサンプリングを組み合わせます。

オーディオエフェクトパイプライン
入力共通変換ピッチシフトスペクトルエフェクト出力AudioSTFTタイムストレッチ複素 SpectrogramリサンプルHPSSPhase VocoderiSTFT処理後オーディオ
フェーズボコーダーとは?

フェーズボコーダーは、あるホップ幅で解析した音を、r 倍のホップ幅で合成し直して伸縮する手法です。各ビンの位相を「測定した周波数 × 新しいホップ幅」だけ進めるので、正弦波成分がフレームをまたいでも途切れません。図解はタイムストレッチとピッチシフトを参照してください。リサンプリングと組み合わせればピッチシフトにも使えます。伸縮中のピッチはおおむね保たれますが、過渡音がにじんだり、極端な倍率で不自然に聞こえたりすることがあります。libsonare の timeStretch にはフェーズボコーダーを使うバックエンドがあります。ほかの編集経路では別のスペクトル処理方式を使う場合もあります。

ストリーミングパイプライン ​

ストリーミングパイプラインは、チャンク間のオーバーラップ状態を維持しながらリアルタイムで音声を処理します。44.1 kHz を超えるストリームは、まず状態を持つリサンプラーで 44.1 kHz に変換されます。Mel・Chroma・オンセットはパワースペクトルを、スペクトル特徴はマグニチュードを入力にします。リングバッファに入ったフレーム単位の特徴量を量子化するかどうかは任意です。既定では Float32 のフル精度を保ちますが、8-bit/16-bit へのパッキングを選ぶと、精度と引き換えにバッファを転送用に縮小できます。

ストリーミングパイプライン
入力バッファリング処理出力なし8-bit16-bit音声チャンク(128–512サンプル)44.1 kHzへリサンプル(それ超の場合)オーバーラップバッファ完全フレーム (n_fft)FFTパワースペクトルマグニチュードMel フィルターバンクChromaフィルターバンクスペクトル特徴オンセット(log-melフラックス)StreamFrameリングバッファ量子化?FrameBuffer(Float32)QuantizedU8QuantizedI16

更新される推定

音声がストリーミングされるほど、パイプラインはクロマとオンセットのデータを蓄積していき、BPM/キー推定が根拠にできる材料が増えます。推定は定期的に更新され(既定値: BPM は 10 秒ごと、キーは 5 秒ごと)、ストリームが長く続くほど信頼度が高まります。キーはストリーム自身の 186 ms のクロマから推定するため、フレーム出力より最大で約 4 分の 1 秒遅れます。

主要な設計判断 ​

遅延初期化 ​

MusicAnalyzer は個別のアナライザーを遅延初期化します。必要になったタイミングで初めて中間特徴量(STFT、クロマ、オンセット包絡線など)を計算し、その後の問い合わせでは結果を再利用します。

cpp
// BPM のみ計算(オンセット包絡線まで)
float bpm = analyzer.bpm();

// キー検出はクロマ計算をトリガー
Key key = analyzer.key();

// 総合解析は残りの項目をまとめて計算
AnalysisResult result = analyzer.analyze();

何が嬉しいか

キーだけ欲しい呼び出しでは、コード認識やセクション検出までは計算しません。analyze() は同じ MusicAnalyzer で計算済みの中間結果を再利用しますが、別の特徴量を求める経路では追加の変換が行われることもあります。

ゼロコピースライシング ​

Audio は shared_ptr とオフセット/サイズでゼロコピースライシングを実現:

cpp
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_fft2048
hop_length512
n_mels128
fmin0
fmaxsr/2

サードパーティライブラリ ​

ライブラリ用途ライセンス
KissFFTFFTBSD-3-Clause
PFFFTSIMD FFT(ネイティブビルド)BSD-3-Clause
Eigen3行列演算(ビルド時のみ)MPL-2.0
dr_libsWAV デコードPublic Domain
minimp3MP3 デコード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 では無効です)。他のソフトウェアに安心して組み込めるのは、この性質があるからです。