Playback JSON v1 擬似スキーマ

{
  "type": "object",
  "required": ["version", "projectId", "bpm", "tracks"],
  "properties": {
    "version": {
      "type": "string",        // 例: "1.0"
      "enum": ["1.0"]          // v1 プロトコル
    },
    "projectId": {
      "type": "string"         // プロジェクト識別子(Plugin / サーバ / Game で共通キーにしてもよい)
    },
    "bpm": {
      "type": "number",        // テンポ(グローバル)
      "minimum": 1
    },
    "timeSignature": {
      "type": "object",
      "required": ["numerator", "denominator"],
      "properties": {
        "numerator":   { "type": "integer", "minimum": 1 },  // 例: 4
        "denominator": { "type": "integer", "minimum": 1 }   // 例: 4, 8
      },
      "additionalProperties": false
    },
    "swing": {
      "type": "number",        // 将来拡張用(0.0〜1.0 推奨)v1.0 では任意・未使用
      "minimum": 0.0,
      "maximum": 1.0
    },
    "tracks": {
      "type": "array",
      "minItems": 1,
      "items": {
        "type": "object",
        "required": ["instrument", "notes"],
        "properties": {
          "instrument": {
            "type": "string"   // Roblox 側のサウンド / Synth 名とマッピングされる論理名
          },
          "channel": {
            "type": "integer"  // 任意:MIDI 的なチャンネル番号(将来のルーティング用)
          },
          "role": {
            "type": "string"   // 任意:Lead / Bass / Pad / Drums などのラベル(Playback では使わない)
          },
          "notes": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["note", "velocity", "startBeat", "durationBeats"],
              "properties": {
                "note": {
                  "type": "integer",      // MIDI ノート番号(0〜127 推奨)
                  "minimum": 0,
                  "maximum": 127
                },
                "velocity": {
                  "type": "integer",      // 1〜127 推奨(0 は無音扱いなので基本使わない)
                  "minimum": 1,
                  "maximum": 127
                },
                "startBeat": {
                  "type": "number",       // 曲頭からの拍位置(小数可)
                  "minimum": 0.0
                },
                "durationBeats": {
                  "type": "number",       // 拍単位の長さ(小数可)
                  "minimum": 0.0
                },
                "tie": {
                  "type": "boolean"       // 任意:前ノートからのタイかどうか(v1.0 では Playback 無視可)
                },
                "mute": {
                  "type": "boolean"       // 任意:true の場合は無視してもよい
                }
              },
              "additionalProperties": true // 将来: articulation, humanize などを許容
            }
          },
          "mute": {
            "type": "boolean"            // 任意:トラックごとのミュート(PlaybackEngine が対応すれば使用)
          },
          "volume": {
            "type": "number"             // 任意:0.0〜1.0 推奨(Mixing Layer に渡すためのヒント)
          },
          "pan": {
            "type": "number"             // 任意:-1.0 (L) 〜 +1.0 (R)
          }
        },
        "additionalProperties": true      // 将来: FX, busRouting などを許容
      }
    },
    "metadata": {
      "type": "object",                  // 任意:AI 側の説明・タグなど(Playback は完全無視でよい)
      "additionalProperties": true
    }
  },
  "additionalProperties": true            // version 1.1+ のフィールド追加を許容
}

OS 的な約束ごと(v1.0)

  • 必須フィールド: version, projectId, bpm, tracks
  • Roblox PlaybackEngine が最低限知るべきもの:
    **bpm / tracks[].instrument / tracks[].notes[].{note, velocity, startBeat, durationBeats}`
  • 未知フィールド: すべて無視してよい(additionalProperties: true の思想)
  • 将来拡張: swing / volume / pan / FX / automation は「入っていてもよいが、v1.0 PlaybackEngine は無視してもよい」

次のステップとしては、このスキーマを前提に「Plugin UI プリセット → Request JSON(generateSong)」の変換表を、同じ OS 言語で書き起こしていける。どのプリセットから定義していきたい?(LoveDance 系から固める?)