/*
To generate material three protobuf messages must be supplied to the mmm::sample function. The specification for each of these messages (Piece, Status and SampleParam) is outlined in this document. Any field which starts with internal_ should be ignored, as these are for internal use only. Or if you really know what you are doing ;).
*/
syntax = "proto2";

import "track_type.proto";
import "enum.proto";
import "midi_internal.proto";

package midi;

/*
Specify the minimum or maximum amount of polyphony using these values. Using POLYPHONY_ANY lets the model choose the level of polyphony.
*/
enum PolyphonyLevel {
  POLYPHONY_ANY = 0;
  POLYPHONY_ONE = 1;
  POLYPHONY_TWO = 2;
  POLYPHONY_THREE = 3;
  POLYPHONY_FOUR = 4;
  POLYPHONY_FIVE = 5;
  POLYPHONY_SIX = 6;
}

/*
Specify the minimum or maximum bounds for note-duration using these values. Using DURATION_ANY lets the model choose the bounds for note duration.
*/
enum NoteDurationLevel {
  DURATION_ANY = 0;
  DURATION_THIRTY_SECOND = 1;
  DURATION_SIXTEENTH = 2;
  DURATION_EIGHTH = 3;
  DURATION_QUARTER = 4;
  DURATION_HALF = 5;
  DURATION_WHOLE = 6;
}

/*
Specify the minimum or maximum amount of note density using these values. Using DENSITY ANY lets the model choose the level of density.
*/
enum DensityLevel {
  DENSITY_ANY = 0;
  DENSITY_ONE = 1;
  DENSITY_TWO = 2;
  DENSITY_THREE = 3;
  DENSITY_FOUR = 4;
  DENSITY_FIVE = 5;
  DENSITY_SIX = 6;
  DENSITY_SEVEN = 7;
  DENSITY_EIGHT = 8;
  DENSITY_NINE = 9;
  DENSITY_TEN = 10;
}

/*
The Event Message is used to represent a MIDI note onset or offset.
*/
message Event {
  /* 
  The time of the event (either a note onset or note offset) relative to the current bar in quantized steps. Currently, most model quantize each quarter note beat into 12 subdivisions. As a result, if the event happens an eighth note after the start of the bar, this value would be 6. If the event occurs three quarter notes after the start of the bar, this value would be 3 * 12 = 36.
  */
  optional int32 time = 1 [(minval) = 0, (maxval) = 1000000];
  /*
  The MIDI velocity. This value must be 0 for note off messages.
  */
  optional int32 velocity = 2 [(minval) = 0, (maxval) = 127]; 
  /*
  The MIDI pitch value of on the range [0,128).
  */
  optional int32 pitch = 3 [(minval) = 0, (maxval) = 127];

  optional int32 internal_instrument = 4;
  optional int32 internal_track_type = 10;
  optional int32 internal_duration = 11;
}

/*
The Bar message specifies the events occuring in a bar.
*/
message Bar {
  /*
  A list of integers, which are simply the indices of the messages found in the Piece.events repeated message. Note offsets which occur at the end of the bar (i.e. event.time = 48 with a time signature of 4/4 and piece.resolution of 12) should be included in the current bar rather than the next bar. In other words, no note offsets should even have an event.time = 0, as these note offset events would belong in the previous bar.
  */
  repeated int32 events = 1 [(minval) = 0, (maxval) = 2147483647];
  /*
  Numerator for the time-signature of the bar. Note that while time signatures can vary from bar to bar, they cannot vary from track to track. In other words if the second bar in track 0 has a time signature of 4/4, the second bar in track 1 must also have a time signature of 4/4.
  */
  optional int32 ts_numerator = 8 [(minval) = 1, (maxval) = 1000000];
  /*
  Denominator for the time-signature of the bar.
  */
  optional int32 ts_denominator = 9 [(minval) = 1, (maxval) = 1000000];

  optional float internal_beat_length = 5;
  optional bool internal_has_notes = 3;
  repeated ContinuousFeature internal_feature = 10;
}

/*
The piece message contains a list of bars, and specifies the instrument and track_type.
*/
message Track {
  /*
  A list of bars. Note that each track must have the same number of bars.
  */
  repeated Bar bars = 1;
  /*
  The MIDI instrument number for the track.
  */
  optional int32 instrument = 3 [(minval) = 0, (maxval) = 127];
  /*
  This must be a value in the TRACK_TYPE enum. In most cases, using STANDARAD_TRACK and STANDARD_DRUM_TRACK will suffice to denote a non-drum instrument track and a drum track respectively.
  */
  optional TRACK_TYPE track_type = 5;
  
  repeated TRACK_TYPE internal_train_types = 6;
  repeated TrackFeatures internal_features = 7;
}

/*
The Piece message specifies the actual musical material in a track-separated event-based format, specifying the note onsets and offsets for each bar in each track.
*/
message Piece {
  /*
  Organizes MIDI events into tracks and bars. In short, each track contains a list of bars, which in turn contains a list of event indices (corresponding to the repeated events message in the Piece.
  */
  repeated Track tracks = 1;
  /*
  A list of MIDI events which the tracks and bars reference
  */
  repeated Event events = 2;
  /*
  The time resolution used to quantize / discretize musical material. Unless otherwise instructed, this should be set to 12.
  */
  optional int32 resolution = 3 [(minval) = 1, (maxval) = 12];
  /* 
  Optionally the tempo can be specified. However this is not taken into consideration by the model.
  */
  optional int32 tempo = 4 [(minval) = 1, (maxval) = 1000000];

  repeated int32 internal_valid_segments = 7;
  repeated uint32 internal_valid_tracks = 8;
  optional int32 internal_segment_length = 12;
  repeated ValidTrack internal_valid_tracks_v2 = 13;
  repeated GenreData internal_genre_data = 14;
}

/*
The StatusTrack message specifies per-track information for generation.
*/
message StatusTrack {
  /*
  The index of a track in the Piece message. For a track to be seen by the model, it must be referenced by a StatusTrack message via the track_id field. Tracks that are not referenced by a StatusTrack message will not be considered by the model.
  */
  optional int32 track_id = 1 [(minval) = 0, (maxval) = 1000000];
  /*
  This must be a value in the TRACK_TYPE enum. This should be equivalent to the TRACK_TYPE specified in the corresponding Track, unless you are giving the model an option to choose either a drum or instrument track. In this case use the STANDARAD_BOTH value here, and the TRACK_TYPE in the piece will be ignored.
  */
  optional TRACK_TYPE track_type = 2;
  /*
  This must be a value in the GM_TYPE enum. It specifies the set of possible instruments that the model may choose from. The mapping between GM_TYPE and instrument numbers can be found in src/mmm_api/enum/gm.h. For example, using  midi::GM_TYPE::piano will allow the model to use any piano instrument.
  */
  optional GM_TYPE instrument = 3;
  /*
  A list of boolean values which specifies whether a bar is to be generated (true) or conditioned on (false). This must be the same length as the number of bars in the corresponding Track message.
  */
  repeated bool selected_bars = 5;
  /*
  Indicates whether or not to use auto-regressive sampling. Note that you can only use auto-regressive sampling when each value in selected_bars is true (i.e. the entire track is being generated). Note that you do not have to use auto-regressive sampling when all selected bars is all true.
  */
  optional bool autoregressive = 6;
  /*
  This indicates that the track should be ignored. The model will not be conditioned on this track, and it will in no way effect the generated outcome.
  */
  optional bool ignore = 7;

  optional DensityLevel density = 4;
  optional PolyphonyLevel min_polyphony_q = 10;
  optional PolyphonyLevel max_polyphony_q = 11;
  optional NoteDurationLevel min_note_duration_q = 12;
  optional NoteDurationLevel max_note_duration_q = 13;

  repeated int32 internal_ts_numerators = 14;
  repeated int32 internal_ts_denominators = 15;
  repeated ContinuousFeature internal_embeds = 8;
  optional string internal_genre = 9;
}

/*
The Status message specifies which bars or tracks are to be generated/conditioned on, and provides extra information about conditioning such as instrument, density, polyphony and note-duration. 
*/
message Status {
  repeated StatusTrack tracks = 1;
}

/*
The SampleParam message specifies hyper-parameters for generation.
*/
message SampleParam {
  /*
  For multi-step generation (typically employed when the entire piece is too large to be considered by the model simultaneously) this parameter specifies the number of tracks that are generated in each step. 
  */
  optional int32 tracks_per_step = 1 [(minval) = 1, (maxval) = 12];
  /*
  For multi-step generation this parameter specifies the number of bars that are generated in each step. This value should be set in relation to model_dim. If bars_per_step = model_dim, then there will be no horizontal conditioning, which will typically produce inferior results. A good rule of thumb is to use bars_per_step == model_dim / 2.
  */
  optional int32 bars_per_step = 2 [(minval) = 1, (maxval) = 8];
  /*
  The size of the model. In most cases this will be 4.
  */
  optional int32 model_dim = 3 [(minval) = 1, (maxval) = 8];
  /*
  The percentage of the selected material (selected bars in the Status message) that will be generated.
  */
  optional int32 percentage = 5 [(minval) = 1, (maxval) = 100];
  /*
  The number of outputs to be generated. Currently we only support batch_size=1.
  With multi-step sampling its is likely more efficient to simply make several calls in series.
  */
  optional int32 batch_size = 7 [(minval) = 1, (maxval) = 1];
  /*
  Allows for the entropy of generation to be adjusted. When temperature=1, the probability distributions output by the model are unaltered. When temperature<1 the probability distribution is increasingly biased towards the most probable tokens. With a very small temperature value this would be equivalent to argmax sampling. When temperature>1 the probability distribution moves towards a random uniform distribution. It is recommended to keep this value close to 1 in most cases.
  */
  optional float temperature = 6 [(fminval) = 0.5, (fmaxval) = 2.0];
  /*
  The max number of tokens to generate before terminating generation. Can be used to avoid memory overload. When this value is set to zero it is ignored, and no limitations are set of the number of generated tokens.
  */
  optional int32 max_steps = 13 [(minval) = 0, (maxval) = 2048];
  /*
  Sets a hard limit on the polyphony accross all tracks. This is implemented by keeping a record of all the currently sounding notes, and preventing the model from generating note-onset tokens when the limit is reached.
  */
  optional int32 polyphony_hard_limit = 14 [(minval) = 0, (maxval) = 100];
  /*
  When shuffle=true the generation steps are randomly ordered. For obvious reasons, auto-regressive sampling cannot be used with shuffle=true, as it would cease to be auto-regressive.
  */
  optional bool shuffle = 4;
  /*
  Mainly for debugging purposes.
  */
  optional bool verbose = 8;
  /*
  The path to the ckpt, which should either be an absolute path or relative to the executable.
  */
  optional string ckpt = 9;

  optional bool internal_skip_preprocess = 12;
  optional bool internal_random_sample_mode = 15;
  optional bool internal_disable_masking = 16;
}

