Skip to content

アーキテクチャ

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

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

レイヤー図の読み方

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

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

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

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

モジュール概要

以下のレイヤーは上から下へ流れます。API レイヤーはアプリが呼び出す入り口で、あらゆる呼び出しは最終的に共有の SpectrogramFFT コアへ集約されるため、どのアナライザーもエフェクトもマスタリングプロセッサも同じ変換を二重に計算しません。各グループは後述の「ディレクトリ構造」セクションの src/ サブディレクトリに対応しており、グループ内のノードは代表的なメンバーであって網羅的なクラス一覧ではありません — 各サブシステムの完全な API がどこに書かれているかは「ページ対応表」を参照してください。

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

ページ対応表

見ている領域読むページ
analysis/feature/JavaScript APIPython APIlibrosa 互換性
analysis/acoustic_analyzer.*analysis/room_estimator.*src/acoustic/effects/acoustic/ルーム音響解析アルゴリズム根拠
streaming/リアルタイムとストリーミング
mastering/マスタリングプロセッサDSP 実装解説マスタリングアシスタント
mixing/ミキシングエンジンミキシングシーン JSON
engine/, transport/, automation/, graph/, rt/リアルタイムとストリーミング、特に RealtimeEngine
midi/midi/synth/内蔵シンセサイザーSoundFont 2 プレイヤーMIDI 入力
arrangement/(編集モデル)プロジェクト編集録音とテイクプロジェクトバウンス
mir/(ワープ・グリッドスナップ・キーコンテキスト)ワープとテンポリアルタイムとストリーミング
editing/effects/編集 DSPDSP 実装解説
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           # 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/            # チェーン・レジストリ・25 プリセット・76 solo processors + pair/stereo registries
│   ├── eq/ dynamics/ spectral/ stereo/ final/
│   ├── maximizer/ multiband/ saturation/ repair/
│   ├── match/ assistant/                 # リファレンスマッチ+アシスタント/プロファイル
│   └── common/        # 共有 biquad/ラウドネスヘルパー

├── mixing/             # ミキシングエンジン
│   ├── channel_strip.* # ストリップ: トリム/インサート/パン/幅/センド
│   ├── bus.* sends.* vca_group.* panner.*
│   └── api/            # シーン JSON +シーンプリセット

├── midi/               # MIDI 入出力と内蔵インストゥルメント
│   ├── synth/          # NativeSynth のボイス群+SoundFont プレイヤー
│   │   ├── native_synth.*      # 12 物理モデル+サブトラクティブ/FM/アディティブ
│   │   ├── 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.*

├── 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 バインディング

データフロー

オーディオ解析パイプライン

どのアナライザーも同じ STFT(短時間フーリエ変換)/Spectrogram の出力を再計算せずに分岐して使います。オンセット強度は BPM とビート追跡を駆動し、クロマグラムはキーとコード認識を駆動します。そして MusicAnalyzer.analyze() は、実際に計算されたものだけをまとめて 1 つの AnalysisResult に収めます。

オーディオ解析パイプライン
入力コア特徴解析出力オーディオファイル (WAV/MP3)生バッファ (float*)AudioSTFTSpectrogramMel SpectrogramChromagramオンセット強度BPM 検出キー検出ビート追跡コード認識AnalysisResult
ファイル経由とメモリ上バッファ経由の両方のパスは Audio に収束し、そこから先はすべての特徴量とアナライザーが同じ Spectrogram を共有します。

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

HPSS(音を倍音成分と打撃成分に分ける処理)とフェーズボコーダーは、どちらも同じ複素 STFT 上で動作し、共有の iSTFT を通して再構成するため、変換パラメータが食い違うことはありません。一方タイムストレッチとピッチシフトは Audio から直接分岐する別経路をたどります。ピッチシフトは同じタイムストレッチのコアの上にリサンプラーを重ねているためです。

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

フェーズボコーダーは、目立ったアーティファクトを抑えながら音声をタイムストレッチする標準的な手法です。リサンプリングと組み合わせれば、ピッチシフトにも使えます。

まず STFT を取り、再構成の前に各周波数ビンの位相を新しい時間軸に合わせて進めます。これにより、ピッチやスペクトルの質感を保ったまま音を長く・短くできます。

libsonare は timeStretch / pitchShift や、編集 DSP のボイス系ツールでこの方法を使っています。

PARAM SWEEP · TIME STRETCHIDLE
タイムストレッチ — 音程はそのまま、長さを変える

タイムストレッチはピッチシフトのちょうど逆で、音程はそのままに、音の長さを変えます。レート(rate)をドラッグするとドラムの打点が広がったり詰まったりして波形がパネルを占める幅も変わりますが、下のスペクトルはほとんど動きません。1.0 より下では遅く長く、1.0 より上では速く短くなります。レンダーごとにピークを揃えているので、速いレートがそのまま小さく聞こえることはありません。聞こえるレベルを決めているのはデモ側で、ストレッチではありません。再生すると、チップマンク効果なしにグルーヴのテンポだけが変わるのが聴けます。

レート
1 ×

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

ストリーミングパイプラインは、チャンク間のオーバーラップ状態を維持しながらリアルタイムで音声を処理します。1 フレーム分の特徴量がリングバッファに到達すると、量子化はオプトインのトレードオフになります — デフォルトでは Float32 のフル精度を保ちますが、8-bit/16-bit へのパッキングを選ぶと、精度と引き換えにバッファを転送用に縮小できます。

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

更新される推定

音声がストリーミングされるほど、パイプラインはクロマとオンセットのデータを蓄積していき、BPM/キー推定が根拠にできる材料が増えます。推定は定期的に更新され(デフォルト: BPM は 10 秒ごと、キーは 5 秒ごと)、ストリームが長く続くほど信頼度が高まります。

主要な設計判断

遅延初期化

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

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

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

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

何が嬉しいか

キーだけ欲しい呼び出しでコード認識やセクション検出まで計算しないので、無駄な処理が発生しません。逆に analyze() を 1 回呼ぶと、すでに計算済みの中間結果がそのまま使われます。

ゼロコピースライシング

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 などのブラウザコーデックへ切り替えられます。

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
Eigen3行列演算MPL-2.0
dr_libsWAV デコードPublic Domain
minimp3MP3 デコードCC0-1.0
FFmpeg任意の拡張ファイルデコードリンクするビルドにより LGPL/GPL
r8brainリサンプリングMIT

WASM コンパイル

出力: WASM 単体 ~4,059 KB(gzip ~1,376 KB)+ JS バインディングコード。
      合計 ~4,372 KB(gzip ~1,442 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 抽象どのホスト環境にも既にあり、それらをまとめて包んでもプラットフォーム固有の依存が増えるだけです。
サンプルデータの同梱音声コーパスにはそれぞれのライセンスがあり、ライブラリ側に付いて回ります。
ディープラーニングモデル重みは大きく、ライセンスをきれいに保つのが難しく、ビルドが実行時ランタイムに依存することになります。
Windows 対応Linux・macOS・WebAssembly・WSL2 のいずれかを使ってください。
オーディオコールバックの所有コールバックと UI はいずれも呼び出し側が持ちます。実験的な macOS バックエンドだけが、この境界に対する唯一の例外です(オプトイン、未公開)。

これらをまとめて外していることで、ライブラリは依存を持たず、ライセンスも Apache-2.0 で通っています。他のものに組み込んでも安全でいられるのは、この性質があるからです。