{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Cairn timed lyrics 1.0.0",
  "description": "Recording-bound timed lyrics. Sample positions are authoritative; millisecond positions are rounded for display. Cross-field ordering and audio hashes require semantic validation.",
  "type": "object",
  "required": ["schema_version", "document_type", "release_key", "track_key", "title", "artist", "language", "time_unit", "sample_rate", "duration_samples", "duration_ms", "recording", "alignment", "cues"],
  "additionalProperties": false,
  "properties": {
    "schema_version": {"const": "1.0.0"},
    "document_type": {"const": "timed_lyrics"},
    "release_key": {"type": "string", "minLength": 1},
    "track_key": {"type": "string", "minLength": 1},
    "title": {"type": "string", "minLength": 1},
    "artist": {"type": "string", "minLength": 1},
    "language": {"type": "string", "minLength": 2},
    "time_unit": {"const": "milliseconds"},
    "sample_rate": {"type": "integer", "minimum": 1},
    "duration_samples": {"type": "integer", "minimum": 1},
    "duration_ms": {"type": "integer", "minimum": 1},
    "recording": {"$ref": "#/$defs/recording"},
    "alignment": {
      "type": "object",
      "additionalProperties": false,
      "required": ["method", "audio_zero", "render_offset_ms", "intervals", "vocal_release_ms", "reverb_tails_excluded", "manually_listening_verified"],
      "properties": {
        "method": {"const": "composer_authored_render_events"},
        "audio_zero": {"const": "first decoded PCM sample"},
        "render_offset_ms": {"type": "number", "minimum": 0},
        "intervals": {"const": "start inclusive; end exclusive"},
        "vocal_release_ms": {"type": "number", "minimum": 0},
        "reverb_tails_excluded": {"type": "boolean"},
        "manually_listening_verified": {"type": "boolean"},
        "track_offset_source": {"type": "string"}
      }
    },
    "cues": {"type": "array", "items": {"$ref": "#/$defs/cue"}}
  },
  "$defs": {
    "role": {"enum": ["lead", "harmony", "chop"]},
    "hash": {"type": "string", "pattern": "^[a-f0-9]{64}$"},
    "interval": {
      "type": "object",
      "required": ["start_ms", "end_ms", "start_sample", "end_sample"],
      "properties": {
        "start_ms": {"type": "integer", "minimum": 0},
        "end_ms": {"type": "integer", "minimum": 1},
        "start_sample": {"type": "integer", "minimum": 0},
        "end_sample": {"type": "integer", "minimum": 1}
      }
    },
    "recording": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "revision", "canonical_format", "audio_assets"],
      "properties": {
        "id": {"type": "string", "pattern": "^sha256:[a-f0-9]{64}$"},
        "revision": {"type": "integer", "minimum": 1},
        "canonical_format": {"enum": ["wav", "mp3"]},
        "audio_assets": {
          "type": "array", "minItems": 1,
          "items": {
            "type": "object", "additionalProperties": false,
            "required": ["format", "album_relative_path", "sha256"],
            "properties": {
              "format": {"enum": ["wav", "mp3"]},
              "album_relative_path": {"type": "string", "minLength": 1},
              "sha256": {"$ref": "#/$defs/hash"}
            }
          }
        }
      }
    },
    "phoneme": {
      "allOf": [{"$ref": "#/$defs/interval"}],
      "type": "object", "unevaluatedProperties": false,
      "required": ["symbol"],
      "properties": {
        "symbol": {"enum": ["IY", "IH", "EH", "AE", "AH", "AA", "AO", "UH", "UW", "ER", "AX", "AY", "EY", "OW", "AW", "OY", "M", "N", "NG", "L", "R", "W", "Y", "S", "SH", "F", "TH", "H", "V", "Z", "ZH", "DH", "P", "T", "K", "B", "D", "G", "CH", "J"]}
      }
    },
    "word": {
      "allOf": [{"$ref": "#/$defs/interval"}],
      "type": "object", "unevaluatedProperties": false,
      "required": ["id", "text", "role", "note_midi", "phonemes"],
      "properties": {
        "id": {"type": "string", "minLength": 1},
        "text": {"type": "string", "minLength": 1},
        "role": {"$ref": "#/$defs/role"},
        "note_midi": {"type": "number", "minimum": 0, "maximum": 127},
        "phonemes": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/phoneme"}}
      }
    },
    "cue": {
      "allOf": [{"$ref": "#/$defs/interval"}],
      "type": "object", "unevaluatedProperties": false,
      "required": ["id", "text", "role", "words"],
      "properties": {
        "id": {"type": "string", "minLength": 1},
        "text": {"type": "string", "minLength": 1},
        "role": {"$ref": "#/$defs/role"},
        "words": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/word"}}
      }
    }
  }
}
