Skip to content

アーキテクチャ概要

MIDI Sketchの内部アーキテクチャを解説します。

プロジェクト構造

midi-sketch/
├── src/
│   ├── core/              # コアエンジン(約16000行、46ヘッダー)
│   │   ├── generator.h/cpp        # 中央オーケストレーター
│   │   ├── harmony_context.h      # トラック間衝突検出ファサード
│   │   ├── chord_progression_tracker.h/cpp
│   │   ├── track_collision_detector.h/cpp
│   │   ├── safe_pitch_resolver.h/cpp
│   │   ├── melody_evaluator.h/cpp # 候補スコアリングシステム
│   │   ├── melody_templates.h/cpp # 7つのメロディテンプレート定義
│   │   ├── melody_embellishment.h/cpp # NCT挿入システム
│   │   ├── pitch_utils.h/cpp      # ピッチ操作
│   │   ├── chord_utils.h/cpp      # コード操作
│   │   ├── piano_roll_safety.h/cpp
│   │   ├── modulation_calculator.h/cpp
│   │   ├── preset_data.h/cpp      # スタイルプリセット
│   │   └── ...                    # 型、ユーティリティ等
│   ├── track/             # トラック生成器(約13000行、14ヘッダー)
│   │   ├── melody_designer.h/cpp  # テンプレート駆動メロディ
│   │   ├── vocal.h/cpp            # ボーカル調整
│   │   ├── aux_track.h/cpp        # Aux副旋律
│   │   ├── chord_track.h/cpp      # コードボイシング
│   │   ├── bass.h/cpp             # ベースパターン
│   │   ├── drums.h/cpp            # ドラムパターン
│   │   ├── motif.h/cpp            # バックグラウンドモチーフ
│   │   ├── guitar.h/cpp           # 伴奏ギター
│   │   ├── arpeggio.h/cpp         # アルペジオパターン
│   │   └── se.h/cpp               # セクションマーカー
│   ├── midi/              # MIDI出力(8ヘッダー)
│   ├── analysis/          # 不協和音分析
│   ├── midisketch.h       # 公開C++ API
│   └── midisketch_c.h     # C API(WASMインターフェース)
├── tests/                 # Google Testスイート(63テストファイル)
├── dist/                  # WASM配布物
└── demo/                  # ブラウザデモ

コアコンポーネント

MidiSketchクラス

高レベルAPIを提供するメインエントリーポイント:

2つの生成ワークフロー

  • ボーカル先行: generateVocal()regenerateVocal()で反復 → generateAccompaniment()で完成
  • 標準: generate() または generateFromConfig() でワンショット生成

設定はSongConfigBuilderを使って構築できます。カスケード変更検出付きの流暢なAPIで、上流の値が変更されると依存パラメータが自動的に再計算されます。

cpp
class MidiSketch {
  void generate(const GeneratorParams& params);
  void generateFromConfig(const SongConfig& config);
  void generateWithVocal(const SongConfig& config);   // Vocal-priority full generation
  void generateVocal(const SongConfig& config);
  void regenerateVocal(const VocalConfig& config);
  void generateAccompaniment(const AccompanimentConfig& config);
  void regenerateAccompaniment(uint32_t seed);
  void setVocalNotes(const SongConfig& config, const NoteInput* notes, size_t count);

  std::vector<uint8_t> getMidi() const;
  std::string getEventsJson() const;
  std::string getChordTimeline() const;               // Chord progression timeline
  const Song& getSong() const;
};

Generator

全トラック生成を統括する中央オーケストレーター(src/core/generator.h):

cpp
class Generator {
  Song generate(const GeneratorParams& params);
private:
  void buildStructure();
  void generateVocal();
  void generateAux();
  void generateMotif();
  void generateBass();
  void generateChord();
  void generateGuitar();      // Accompaniment guitar generation
  void generateArpeggio();
  void generateDrums();
  void generateSE();          // Section markers / sound effects
  void applyTransitionDynamics();
  void applyHumanization();
};

Songコンテナ

生成された全データを保持(9トラック):

cpp
struct Song {
  Arrangement arrangement;     // セクション配置
  MidiTrack vocal;            // Channel 0 - Main melody
  MidiTrack aux;              // Channel 1 - Sub-melody
  MidiTrack chord;            // Channel 2 - Harmony
  MidiTrack bass;             // Channel 3 - Foundation
  MidiTrack motif;            // Channel 4 - BackgroundMotif style
  MidiTrack guitar;           // Channel 6 - Accompaniment guitar
  MidiTrack arpeggio;         // Channel 5 - SynthDriven style
  MidiTrack drums;            // Channel 9 - Rhythm
  MidiTrack se;               // Channel 15 (markers)
};

チャンネル共有

AuxとArpeggioはMIDIチャンネル5を共有しています。MelodyLeadスタイルではAuxが生成され、SynthDrivenスタイルではArpeggioが生成されます。両者が同時に有効になることはありません。

データフロー

標準生成(Traditionalパラダイム)

パラダイム別の生成順序

トラック生成順序はBlueprintのパラダイムによって異なります:

  • Traditional / MelodyDriven: Vocal -> Aux -> Motif -> Bass -> Chord -> Guitar -> Arpeggio -> Drums -> SE
  • RhythmSync: Motif -> Vocal -> Aux -> Bass -> Chord -> Guitar -> Arpeggio -> Drums -> SE

ボーカル先行生成

時間表現

MIDI Sketchは全体でティックベースのタイミングを使用:

cpp
using Tick = uint32_t;
constexpr Tick TICKS_PER_BEAT = 480;    // Standard MIDI resolution
constexpr Tick TICKS_PER_BAR = 1920;    // 4/4 time signature
constexpr uint8_t BEATS_PER_BAR = 4;

ティック計算

  • 4分音符 = 480 ticks
  • 8分音符 = 240 ticks
  • 16分音符 = 120 ticks
  • 1小節(4/4拍子)= 1920 ticks

ノート表現

2層のノート表現:

cpp
// Intermediate musical representation (internal)
struct NoteEvent {
  Tick startTick;      // Absolute start time
  Tick duration;       // Duration in ticks
  uint8_t note;        // MIDI note (0-127)
  uint8_t velocity;    // MIDI velocity (0-127)
};

// Low-level MIDI bytes (output only)
struct MidiEvent {
  Tick tick;           // Absolute time
  uint8_t status;      // MIDI status byte
  uint8_t data1;       // First data byte
  uint8_t data2;       // Second data byte
};

セクション定義

楽曲はセクションに分割:

cpp
struct Section {
  SectionType type;              // Intro, A, B, Chorus, Bridge, Interlude, Outro
  std::string name;              // Display name
  uint8_t bars;                  // Bar count
  Tick startBar;                 // Start position (bars)
  Tick start_tick;               // Start position (ticks)
  VocalDensity vocal_density;    // Full, Sparse, None
  BackingDensity backing_density; // Normal, Thin, Thick
};

コンポジションスタイル

3つのコンポジションスタイルが生成アプローチに影響:

スタイルVocalAuxMotifArpeggio説明
MelodyLead (0)YesYesBlueprint依存Optionalボーカルメロディが主役の伝統的なアレンジ
BackgroundMotif (1)NoYesYesOptionalVocal無効、Aux有効、Motifが主要フォーカス
SynthDriven (2)NoNoBlueprint依存Optional(手動有効化)Vocal/Aux無効、シンセ/アルペジオ主体のエレクトロニックスタイル

BGM専用モード

BackgroundMotifはVocalを無効にしますが、Auxは有効のままでMotif生成を強制します。SynthDrivenはVocalとAuxの両方を無効にし、ArpeggioはarpeggioEnabled=trueで手動で有効にする必要があります。ボーカル付きの楽曲にはMelodyLeadを使用してください。

プロダクションブループリント

ブループリントはトラック生成順序、モチーフの振る舞い、暗黙的なオーバーライドを制御する高レベルのプロダクションテンプレートです。10個のブループリント(ID 0-9)があり、ID 255でランダム選択が可能です。

IDNameParadigmRiffPolicyDrums RequiredWeight
0TraditionalTraditionalFree-42%
1RhythmLockRhythmSyncLockedYes14%
2StoryPopMelodyDrivenEvolving-10%
3BalladMelodyDrivenFree-4%
4IdolStandardMelodyDrivenEvolving-10%
5IdolHyperRhythmSyncLockedYes6%
6IdolKawaiiMelodyDrivenLockedYes5%
7IdolCoolPopRhythmSyncLockedYes5%
8IdolEmoMelodyDrivenLocked-4%
9BehavioralLoopTraditionalLockedPitch-0%*

* BehavioralLoop(ID 9)はweight 0%で、明示的に選択する必要があります(ランダム選択されません)。addictive_mode=trueRiffPolicy::LockedPitchHookIntensity::Maximumを強制します。

パラダイム
  • Traditional: Vocal -> Aux -> Motif -> Bass -> Chord -> Guitar -> Arpeggio -> Drums -> SE
  • RhythmSync: Motif -> Vocal -> Aux -> Bass -> Chord -> Guitar -> Arpeggio -> Drums -> SE(Motifを座標軸として使用)
  • MelodyDriven: Vocal -> Aux -> Motif -> Bass -> Chord -> Guitar -> Arpeggio -> Drums -> SE(Traditionalと同じ順序だがMotifはメロディに追従)
RiffPolicy

APIは3つのRiffPolicy値を公開:

  • Free (0): セクションごとにMotifが変化(MotifRepeatScopeがセクション間の振る舞いを制御)
  • Locked (1): ピッチ輪郭は固定、表現は変化(内部的にはLockedContour)
  • Evolving (2): 2セクションごとに30%の確率で変化

内部的にはブループリントはより細かい粒度のセットを使用:Free(0)、LockedContour(1)、LockedPitch(2)、LockedAll(3)、Evolving(4)。

ブループリントオーバーライド

ブループリントはSongConfigの複数のパラメータをオーバーライドできます:

  • section_flowformIdをオーバーライド(存在し、かつformExplicit=falseの場合)
  • riff_policymotifRepeatScopeをオーバーライド(Freeの場合のみ)
  • drums_requireddrums_enabled=trueを強制(drumsEnabledExplicit=trueかつdrumsEnabled=falseの場合を除く)
  • drums_sync_vocalがSongConfigの設定をオーバーライド
  • mood_maskが互換性のあるムードを制限(isMoodCompatible()で確認)

パラメータ適用順序

パラメータは特定のカスケード順序で適用され、後の段階が前の段階をオーバーライドできます:

StylePreset → VocalStylePreset → MelodicComplexity → SongConfig Overrides → Master Switch
  1. StylePreset: メロディ設定を含む基本パラメータを設定
  2. VocalStylePreset: max_leap、syncopation、density、その他のボーカル特性を調整
  3. MelodicComplexity: density/leap乗数を適用(Simpleは減少、Complexは増幅)
  4. SongConfig Overrides: ユーザー指定のメロディ/モチーフオーバーライドパラメータが最高優先度
  5. Master Switch: enableSyncopation=falseでsyncopation_prob=0.0とallow_bar_crossing=falseを強制

乱数生成

メルセンヌ・ツイスターによる決定論的生成:

cpp
std::mt19937 rng(seed);  // Same seed = same output

再現性

  • seed > 0: 完全決定論的 - 同じシードと同じパラメータで常に同一の出力
  • seed = 0: ランダム - 現在時刻を使用、実行ごとに異なる結果

シードが0の場合、現在時刻がランダム化に使用されます。

WASMコンパイル

Emscripten経由でWebAssemblyにコンパイル:

  • 出力: 約555KB WASM(gzip: 約225KB)+ 約80KB JS(ラッパー + グルー)
  • 外部依存なし: 純粋なC++17
  • ES6モジュール: モジュラーJavaScriptラッパー
bash
# Build flags
-sWASM=1 -sMODULARIZE=1 -sEXPORT_ES6=1
-sALLOW_MEMORY_GROWTH=1 -sSTACK_SIZE=1048576

C APIレイヤー

WASM相互運用のため、C APIがC++クラスをラップ:

c
// Lifecycle
MidiSketchHandle handle = midisketch_create();
midisketch_generate(handle, params);
MidiSketchMidiData* midi = midisketch_get_midi(handle);
midisketch_free_midi(midi);
midisketch_destroy(handle);

主要関数:

  • midisketch_generate() - コア生成
  • midisketch_generate_vocal_from_json() - ボーカルのみの生成
  • midisketch_regenerate_vocal_from_json() - ボーカル再生成
  • midisketch_generate_accompaniment_from_json() - 伴奏生成
  • midisketch_regenerate_accompaniment_from_json() - 伴奏再生成
  • midisketch_generate_with_vocal_from_json() - ボーカル優先フル生成
  • midisketch_set_vocal_notes_from_json() - カスタムボーカル注入
  • midisketch_get_piano_roll_safety() - ピアノロール安全性分析
  • midisketch_get_chord_timeline() - コードタイムライン取得
  • midisketch_get_midi() - MIDIバイナリ出力
  • midisketch_get_events() - JSONイベントデータ
  • midisketch_get_info() - メタデータ(小節数、ティック、BPM)
  • midisketch_blueprint_count() / midisketch_blueprint_name() - ブループリント情報

Released under the MIT License.