// Copyright (C) 2026 Michael Wilson <mike@mdwn.dev>
//
// This program is free software: you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation, version 3.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with
// this program. If not, see <https://www.gnu.org/licenses/>.
//
syntax = "proto3";
package player.v1;
import "google/protobuf/duration.proto";
// BeatGrid contains beat positions and measure boundaries detected from
// a click track. Used for snapping loop points to musically meaningful positions.
message BeatGrid {
// Absolute time in seconds of each detected beat.
repeated double beats = 1;
// Indices into beats that mark measure boundaries (accented beats).
repeated uint32 measure_starts = 2;
}
// Section defines a named region of a song by measure boundaries.
message Section {
// Display name for this section.
string name = 1;
// Start measure (1-indexed, inclusive).
uint32 start_measure = 2;
// End measure (1-indexed, exclusive).
uint32 end_measure = 3;
}
// Song is a message that contains information about a song.
message Song {
reserved 4;
reserved "is_transcoded";
// Name is the name of the song.
string name = 1;
// Duration is the length of the song.
google.protobuf.Duration duration = 2;
// Tracks are the tracks available for the song.
repeated string tracks = 3;
// Beat grid derived from click track analysis.
BeatGrid beat_grid = 5;
// Named sections defined by measure boundaries.
repeated Section sections = 6;
}
// PlayRequest is the message for requesting the player to play the current song.
message PlayRequest {}
// PlayResponse is the response message after playing a song.
message PlayResponse {
// Song is the song that is now playing.
Song song = 1;
}
// PreviousRequest is the message for requesting the player to move to the previous
// song.
message PreviousRequest {}
// PreviousResponse is the response message after moving to the previous song.
message PreviousResponse {
// Song is the song the playlist is now pointing at.
Song song = 1;
}
// NextRequest is the message for requesting the player to move to the next
// song.
message NextRequest {}
// NextResponse is the response message after moving to the next song.
message NextResponse {
// Song is the song the playlist is now pointing at.
Song song = 1;
}
// StopRequest is the message for requesting the player to stop the currently
// playing song.
message StopRequest {}
// StopResponse is the response message after requesting the player to stop.
message StopResponse {
// Song is the song that was stopped.
Song song = 1;
}
// SwitchToPlaylistRequest is the message for requesting the player to switch
// to a different playlist.
message SwitchToPlaylistRequest {
// PlaylistName is the name of the playlist to switch to.
string playlist_name = 1;
}
// SwitchToPlaylistResponse is the response message after requesting the player
// to switch to a different playlist.
message SwitchToPlaylistResponse {}
// StatusRequest is the message for requesting a streaming status from the player.
message StatusRequest {}
// StatusResponse is the response message after requesting the status from the player.
message StatusResponse {
// PlaylistName is the name of the current playlist.
string playlist_name = 1;
// CurrentSong is the current song that the current playlist is pointing to.
Song current_song = 2;
// Playing is true while a song is playing.
bool playing = 3;
// Elapsed is the amount of time that has elapsed while playing the song.
google.protobuf.Duration elapsed = 4;
}
// PlayFromRequest is the message for requesting the player to play from a specific time.
message PlayFromRequest {
// StartTime is the time to start playback from (e.g., "1:23.456" or "45.5s").
// If not provided or zero, plays from the beginning.
google.protobuf.Duration start_time = 1;
}
// PlaySongFromRequest is the message for requesting the player to play a specific song
// from a specific time.
message PlaySongFromRequest {
// SongName is the name of the song to play.
string song_name = 1;
// StartTime is the time to start playback from.
google.protobuf.Duration start_time = 2;
}
// GetCuesRequest is the message for requesting the list of cues in the current song.
message GetCuesRequest {}
// Cue represents a single cue in the lighting timeline.
message Cue {
// Time is the time at which this cue triggers.
google.protobuf.Duration time = 1;
// Index is the index of this cue in the timeline.
uint32 index = 2;
}
// GetCuesResponse is the response containing the list of cues.
message GetCuesResponse {
// Cues is the list of cues in the current song's lighting timeline.
repeated Cue cues = 1;
}
// GetActiveEffectsRequest is the message for requesting active lighting effects.
message GetActiveEffectsRequest {}
// GetActiveEffectsResponse is the response containing active lighting effects information.
message GetActiveEffectsResponse {
// ActiveEffects is a formatted string listing all active lighting effects.
string active_effects = 1;
}
// StopSamplesRequest is the message for requesting the player to stop all triggered samples.
message StopSamplesRequest {}
// StopSamplesResponse is the response message after stopping triggered samples.
message StopSamplesResponse {}
// GetConfigRequest is the message for requesting the current configuration.
message GetConfigRequest {}
// GetConfigResponse is the response containing the current configuration.
message GetConfigResponse {
// The full configuration as a YAML string.
string yaml = 1;
// Checksum of the current configuration for optimistic concurrency.
string checksum = 2;
}
// UpdateAudioRequest is the message for updating the audio configuration.
message UpdateAudioRequest {
// The audio configuration as a JSON string.
string audio_json = 1;
// Expected checksum for optimistic concurrency.
string expected_checksum = 2;
}
// UpdateMidiRequest is the message for updating the MIDI configuration.
message UpdateMidiRequest {
// The MIDI configuration as a JSON string.
string midi_json = 1;
// Expected checksum for optimistic concurrency.
string expected_checksum = 2;
}
// UpdateDmxRequest is the message for updating the DMX configuration.
message UpdateDmxRequest {
// The DMX configuration as a JSON string.
string dmx_json = 1;
// Expected checksum for optimistic concurrency.
string expected_checksum = 2;
}
// UpdateControllersRequest is the message for updating the controllers configuration.
message UpdateControllersRequest {
// The controllers configuration as a JSON string (array).
string controllers_json = 1;
// Expected checksum for optimistic concurrency.
string expected_checksum = 2;
}
// AddProfileRequest is the message for adding a new profile.
message AddProfileRequest {
// The profile as a JSON string.
string profile_json = 1;
// Expected checksum for optimistic concurrency.
string expected_checksum = 2;
}
// UpdateProfileRequest is the message for updating a profile at a specific index.
message UpdateProfileRequest {
// The index of the profile to update.
uint32 index = 1;
// The profile as a JSON string.
string profile_json = 2;
// Expected checksum for optimistic concurrency.
string expected_checksum = 3;
}
// RemoveProfileRequest is the message for removing a profile at a specific index.
message RemoveProfileRequest {
// The index of the profile to remove.
uint32 index = 1;
// Expected checksum for optimistic concurrency.
string expected_checksum = 2;
}
// UpdateConfigResponse is the response after a configuration mutation.
message UpdateConfigResponse {
// The full configuration as a YAML string after the update.
string yaml = 1;
// New checksum after the update.
string checksum = 2;
}
// LoopSectionRequest activates section looping for a named section.
message LoopSectionRequest {
// The name of the section to loop.
string section_name = 1;
}
// LoopSectionResponse is returned after activating a section loop.
message LoopSectionResponse {}
// StopSectionLoopRequest deactivates section looping.
message StopSectionLoopRequest {}
// StopSectionLoopResponse is returned after stopping a section loop.
message StopSectionLoopResponse {}
// SectionAckRequest acknowledges the current section in reactive looping mode.
message SectionAckRequest {}
// SectionAckResponse is returned after acknowledging a section.
message SectionAckResponse {}
// PlayerService is a service for controlling the mtrack player.
service PlayerService {
// Play will play the current song in the playlist if no other songs
// are playing.
rpc Play(PlayRequest) returns (PlayResponse);
// PlayFrom will play the current song starting from a specific time.
rpc PlayFrom(PlayFromRequest) returns (PlayResponse);
// PlaySongFrom will play a specific song starting from a specific time.
rpc PlaySongFrom(PlaySongFromRequest) returns (PlayResponse);
// Previous will move the playlist to the previous song.
rpc Previous(PreviousRequest) returns (PreviousResponse);
// Next will move the playlist to the next song.
rpc Next(NextRequest) returns (NextResponse);
// Stop will stop the currently playing song.
rpc Stop(StopRequest) returns (StopResponse);
// SwitchToPlaylist will switch the player to a different playlist.
rpc SwitchToPlaylist(SwitchToPlaylistRequest) returns (SwitchToPlaylistResponse);
// Status will return the current status of the player.
rpc Status(StatusRequest) returns (StatusResponse);
// GetCues returns the list of cues in the current song's lighting timeline.
rpc GetCues(GetCuesRequest) returns (GetCuesResponse);
// GetActiveEffects returns a formatted string listing all active lighting effects.
rpc GetActiveEffects(GetActiveEffectsRequest) returns (GetActiveEffectsResponse);
// StopSamples will stop all triggered samples that are currently playing.
rpc StopSamples(StopSamplesRequest) returns (StopSamplesResponse);
// GetConfig returns the current configuration as YAML with a checksum.
rpc GetConfig(GetConfigRequest) returns (GetConfigResponse);
// UpdateAudio updates the audio configuration section.
rpc UpdateAudio(UpdateAudioRequest) returns (UpdateConfigResponse);
// UpdateMidi updates the MIDI configuration section.
rpc UpdateMidi(UpdateMidiRequest) returns (UpdateConfigResponse);
// UpdateDmx updates the DMX configuration section.
rpc UpdateDmx(UpdateDmxRequest) returns (UpdateConfigResponse);
// UpdateControllers updates the controllers configuration.
rpc UpdateControllers(UpdateControllersRequest) returns (UpdateConfigResponse);
// AddProfile adds a new hardware profile.
rpc AddProfile(AddProfileRequest) returns (UpdateConfigResponse);
// UpdateProfile updates a profile at a specific index.
rpc UpdateProfile(UpdateProfileRequest) returns (UpdateConfigResponse);
// RemoveProfile removes a profile at a specific index.
rpc RemoveProfile(RemoveProfileRequest) returns (UpdateConfigResponse);
// LoopSection activates section looping for the named section.
rpc LoopSection(LoopSectionRequest) returns (LoopSectionResponse);
// StopSectionLoop deactivates section looping. The current iteration
// finishes and the song continues from the section end.
rpc StopSectionLoop(StopSectionLoopRequest) returns (StopSectionLoopResponse);
// SectionAck acknowledges the current section in reactive looping mode,
// arming the loop so it engages at the section end.
rpc SectionAck(SectionAckRequest) returns (SectionAckResponse);
}