// Copyright 2016 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////

syntax = "proto3";

package magenta;

// A message containing a symbolic music sequence. The design is largely
// based on MIDI but it should be able to represent any music sequence.
// For details see https://www.midi.org/specifications.
// Note that repeated fields in this proto are not guaranteed to be sorted
// by time.
// Next tag: 22
message NoteSequence {
  // Unique id.
  string id = 1;
  // The path of the file relative to the root of the collection.
  string filename = 2;
  // A unique id to differentiate multiple pieces taken from the same input
  // file.
  int64 reference_number = 18;
  // The collection from which the file comes. This can be shorthand e.g.
  // "bach". One purpose is to allow for easy selection of all or some files
  // from a particular source.
  string collection_name = 3;

  // MIDI ticks per quarter note, also known as resolution or PPQ ("pulses per
  // quarter").
  // There is no widely-used default. A default of 220 is assumed per the choice
  // made in third_party/py/pretty_midi.
  int32 ticks_per_quarter = 4;
  // Lacking a time signature, 4/4 is assumed per MIDI standard.
  repeated TimeSignature time_signatures = 5;
  // Lacking a key signature, C Major is assumed per MIDI standard.
  repeated KeySignature key_signatures = 6;
  // Lacking a tempo change, 120 qpm is assumed per MIDI standard.
  repeated Tempo tempos = 7;
  // A Note combines a MIDI NoteOn and NoteOff into one event with duration.
  repeated Note notes = 8;
  // The total time of the Sequence in seconds.
  // Currently the total time is defined as the end time of the last note in the
  // sequence, and any control changes or rests that occur after the end of the
  // last note are not included in this time.
  // Note: In the future, this time will be allowed to extend beyond the last
  // note end time in order to represent padding. Magenta.js already uses this
  // interpretation of the field.
  // TODO(adarob): Update existing code to allow for this new interpretation.
  double total_time = 9;
  // The total time of the sequence in quantized steps.
  // This has the same meaning as the total_time field.
  // Note: In the future, steps will be allowed to extend beyond the last note
  // end steps in order to represent padding. Magenta.js already uses this
  // interpretation of the field.
  // TODO(adarob): Update existing code to allow for this new interpretation.
  int64 total_quantized_steps = 16;

  // MIDI-specific events that are generally relevant for performance, metadata
  // storage or re-synthesis but not for processing the music score.
  repeated PitchBend pitch_bends = 10;
  repeated ControlChange control_changes = 11;

  // Score-related information about parts.
  repeated PartInfo part_infos = 12;

  // Source-related information.
  SourceInfo source_info = 13;

  // Arbitrary textual annotations.
  repeated TextAnnotation text_annotations = 14;

  // Annotations indicating sections within a piece.
  repeated SectionAnnotation section_annotations = 20;

  // Instructions on how to play back the sections within a piece.
  repeated SectionGroup section_groups = 21;

  // Information about how/if this sequence was quantized.
  QuantizationInfo quantization_info = 15;

  // Information about how this sequence was extracted from a larger source
  // sequence (if that was the case).
  SubsequenceInfo subsequence_info = 17;

  // Sequence metadata.
  SequenceMetadata sequence_metadata = 19;

  // information about instrument type.
  repeated InstrumentInfo instrument_infos = 23;

  // Next tag: 15
  message Note {
    // MIDI pitch; see en.wikipedia.org/wiki/MIDI_Tuning_Standard for details.
    int32 pitch = 1;
    // The notated pitch spelling in the score.
    PitchName pitch_name = 11;
    // Velocity ranging between 0 and 127.
    int32 velocity = 2;
    // Start time in seconds.
    double start_time = 3;
    // Quantized start time in steps.
    int64 quantized_start_step = 13;
    // End time in seconds.
    double end_time = 4;
    // Quantized end time in steps.
    int64 quantized_end_step = 14;
    // Score-relative note length. E.g. a quarter note is 1/4.
    int32 numerator = 5;
    int32 denominator = 6;
    // For MIDI source data, an instrument stores all events in a track having
    // the same program and channel, as done by pretty-midi.
    int32 instrument = 7;
    // A program selects an instrument's sound.
    // Note that the General MIDI documentation is 1-based, but this field is
    // 0-based. So GM documents program 12 as vibraphone, but this field would
    // be set to 11 for that instrument.
    // See www.midi.org/specifications/item/gm-level-1-sound-set.
    int32 program = 8;
    // When true, the event is on an instrument that is a drum (MIDI channel 9).
    bool is_drum = 9;
    // The part index if this came from a score. Otherwise, just 0.
    // For example, a score may have separate parts for different instruments in
    // an orchestra.
    // If additional information is available about the part, a corresponding
    // PartInfo should be defined with the same index.
    int32 part = 10;
    // The voice index if this came from a score. Otherwise, just 0.
    // For example, within a part, there may be multiple voices (e.g., Soprano,
    // Alto, Tenor, Bass).
    // Note that while voices indexes must be unique within a part, they are not
    // guaranteed to be unique across parts.
    int32 voice = 12;
  }

  // Adopted from Musescore with start enum shifted to 0; see
  // https://musescore.org/en/plugin-development/tonal-pitch-class-enum
  // for details.
  enum PitchName {
    UNKNOWN_PITCH_NAME = 0;
    F_FLAT_FLAT = 1;
    C_FLAT_FLAT = 2;
    G_FLAT_FLAT = 3;
    D_FLAT_FLAT = 4;
    A_FLAT_FLAT = 5;
    E_FLAT_FLAT = 6;
    B_FLAT_FLAT = 7;
    F_FLAT = 8;
    C_FLAT = 9;
    G_FLAT = 10;
    D_FLAT = 11;
    A_FLAT = 12;
    E_FLAT = 13;
    B_FLAT = 14;
    F = 15;
    C = 16;
    G = 17;
    D = 18;
    A = 19;
    E = 20;
    B = 21;
    F_SHARP = 22;
    C_SHARP = 23;
    G_SHARP = 24;
    D_SHARP = 25;
    A_SHARP = 26;
    E_SHARP = 27;
    B_SHARP = 28;
    F_SHARP_SHARP = 29;
    C_SHARP_SHARP = 30;
    G_SHARP_SHARP = 31;
    D_SHARP_SHARP = 32;
    A_SHARP_SHARP = 33;
    E_SHARP_SHARP = 34;
    B_SHARP_SHARP = 35;
  }

  message TimeSignature {
    // Time in seconds.
    double time = 1;
    int32 numerator = 2;
    int32 denominator = 3;
  }

  message KeySignature {
    // Time in seconds.
    double time = 1;
    Key key = 2;
    Mode mode = 3;

    enum Key {
      option allow_alias = true;

      C = 0;
      C_SHARP = 1;
      D_FLAT = 1;
      D = 2;
      D_SHARP = 3;
      E_FLAT = 3;
      E = 4;
      F = 5;
      F_SHARP = 6;
      G_FLAT = 6;
      G = 7;
      G_SHARP = 8;
      A_FLAT = 8;
      A = 9;
      A_SHARP = 10;
      B_FLAT = 10;
      B = 11;
    }

    enum Mode {
      MAJOR = 0;
      MINOR = 1;
      NOT_SPECIFIED = 2;
      MIXOLYDIAN = 3;
      DORIAN = 4;
      PHRYGIAN = 5;
      LYDIAN = 6;
      LOCRIAN = 7;
    }
  }

  message Tempo {
    // Time in seconds when tempo goes into effect.
    double time = 1;
    // Tempo in quarter notes per minute.
    double qpm = 2;
  }

  // Stores MIDI PitchBend data. See the MIDI specification for details.
  message PitchBend {
    // Time in seconds.
    double time = 1;
    // Pitch bend amount in the range (-8192, 8191).
    int32 bend = 2;
    int32 instrument = 3;
    int32 program = 4;
    bool is_drum = 5;
  }

  // Stores MIDI Control Change data. See the MIDI specification for details.
  message ControlChange {
    // Time in seconds.
    double time = 1;
    // Quantized time in steps.
    int64 quantized_step = 7;
    // Control (or "controller") number e.g. 0x4 = Foot Controller.
    int32 control_number = 2;
    // The value for that controller in the range (0, 127).
    int32 control_value = 3;
    int32 instrument = 4;
    int32 program = 5;
    bool is_drum = 6;
  }

  // Stores score-related information about a particular part.
  // See usage within Note for more details.
  message PartInfo {
    // The part index.
    int32 part = 1;
    // The name of the part. Examples: "Piano" or "Voice".
    string name = 2;
  }

  // Stores information about an instrument name
  // See usage within Note for more details.
  message InstrumentInfo {
    // The instrument index.
    int32 instrument = 1;
    // The name of the instrument. Examples: "Piano" or "bass".
    string name = 2;
  }

  // Stores source-related information.
  message SourceInfo {
    // The type of source, if it was score-based or performance-based.
    SourceType source_type = 1;
    // The encoding type used in the source file.
    EncodingType encoding_type = 2;

    // That parser that was used to parse the source file.
    Parser parser = 3;

    // The type of source that was encoded in the original file.
    enum SourceType {
      UNKNOWN_SOURCE_TYPE = 0;
      // If the source was some kind of score (e.g., MusicXML, ABC, etc.).
      // We can expect perfect timing alignment with measures and complete
      // TimeSignature and KeySignature information.
      SCORE_BASED = 1;
      PERFORMANCE_BASED = 2;
    }

    // Enum for all encoding types, both score_based and performance_based.
    enum EncodingType {
      UNKNOWN_ENCODING_TYPE = 0;
      MUSIC_XML = 1;
      ABC = 2;
      MIDI = 3;
      MUSICNET = 4;
    }

    // Name of parser used to parse the source file.
    enum Parser {
      UNKNOWN_PARSER = 0;
      MUSIC21 = 1;
      PRETTY_MIDI = 2;
      // Magenta's built-in MusicXML parser.
      MAGENTA_MUSIC_XML = 3;
      // Magenta's parser for MusicNet data.
      MAGENTA_MUSICNET = 4;
      // Magenta's parser for ABC files.
      MAGENTA_ABC = 5;
      // Javascript Tonejs/MidiConvert.
      TONEJS_MIDI_CONVERT = 6;
    }
  }

  // Stores an arbitrary text annotation associated with a point in time.
  // Next tag: 5
  message TextAnnotation {
    // Time in seconds.
    double time = 1;
    // Quantized time in steps.
    int64 quantized_step = 4;
    // Text of the annotation.
    string text = 2;
    // Type of the annotation, to assist with automated interpretation.
    TextAnnotationType annotation_type = 3;

    enum TextAnnotationType {
      // Unknown annotation type.
      UNKNOWN = 0;
      // Chord symbol as used in lead sheets. We treat text as the "ground
      // truth" format for chord symbols, as the semantic interpretation of
      // a chord symbol is often fuzzy. We defer this interpretation to
      // individual models, each of which can translate chord symbol strings
      // into model input in whatever way is deemed most appropriate for that
      // model.
      //
      // Some examples of chord symbol text we consider reasonable: 'C#', 'A7',
      // 'Fm7b5', 'N.C.', 'G(no3)', 'C/Bb', 'D-9(b5)', 'Gadd2', 'Abm(maj7)'.
      CHORD_SYMBOL = 1;
      // Annotation used to indicate a "beat" within a performance. This is
      // useful when beat information cannot be derived from the time signature
      // and tempo, as is the case for live performances.
      // This annotation does not imply that the beat is a downbeat, and it is
      // undefined what kind of metrical value the beat has (e.g., quarter
      // note).
      // The text content of this annotation can be application-specific.
      BEAT = 2;
    }
  }

  // Information about how/if this sequence was quantized.
  message QuantizationInfo {
    oneof resolution {
      // How many quantization steps per quarter note of music.
      int32 steps_per_quarter = 1;
      // How many quantization steps per second.
      int32 steps_per_second = 2;
    }
  }

  // Information about the location of the sequence in a larger source sequence.
  message SubsequenceInfo {
    // Time in seconds from the start of the source sequence to the start of
    // this sequence.
    double start_time_offset = 1;
    // Time in seconds from the end of this sequence to the end of the source
    // sequence.
    double end_time_offset = 2;
  }

  // Information about a section within a piece.
  // A section is considered to be active from its indicated time until either a
  // new section is defined or the end of the piece is reached.
  message SectionAnnotation {
    // Time in seconds.
    double time = 1;
    // The id of the section.
    // Section ids must be unique within a piece.
    int64 section_id = 4;
  }

  // A section.
  // Either a section_id, which references a SectionAnnotation or a nested
  // SectionGroup.
  message Section {
    oneof section_type {
      int64 section_id = 1;
      SectionGroup section_group = 2;
    }
  }

  // A group of sections and an indication of how many times to play them.
  // Note that a SectionGroup may contain nested SectionGroups. This is to
  // capture some of the more complex structure in ABC files with part
  // directives like P:((AB)3(CD)3)2.
  message SectionGroup {
    repeated Section sections = 1;
    int32 num_times = 2;
  }
}

// Stores metadata associated with a sequence.
message SequenceMetadata {
  // Title of the piece.
  string title = 1;

  // Primary artist of the sequence.
  string artist = 2;

  // Genre(s) of the sequence.
  repeated string genre = 3;

  // Composer of the sequece. Some pieces have multiple composers.
  repeated string composers = 4;
}

// Stores an inclusive range of velocities.
message VelocityRange {
  int32 min = 1;
  int32 max = 2;
}
