private static void AnalyzeNotesInSentence(Sentence sentence, List <SongIssue> result) { // Find overlapping notes List <Note> sortedNotes = SongMetaUtils.GetSortedNotes(sentence); Note lastNote = null; foreach (Note note in sortedNotes) { if (lastNote != null && note.StartBeat < lastNote.EndBeat) { SongIssue issue = SongIssue.CreateError("Notes overlap", note.StartBeat, lastNote.EndBeat); result.Add(issue); } // Find pitches outside of the singable range if (note.MidiNote < MidiUtils.SingableNoteMin || note.MidiNote > MidiUtils.SingableNoteMax) { SongIssue issue = SongIssue.CreateWarning("Unusual pitch (human range is roughly from C2 to C6).", note.StartBeat, note.EndBeat); result.Add(issue); } // Check that each note has lyrics if (note.Text.IsNullOrEmpty()) { SongIssue issue = SongIssue.CreateWarning("Missing lyrics on note", note.StartBeat, note.EndBeat); result.Add(issue); } lastNote = note; } }
public void Reload() { SongMeta other = SongMetaBuilder.ParseFile(SongMetaUtils.GetAbsoluteSongMetaPath(this)); // Copy values Encoding = other.Encoding; SongHash = other.SongHash; voiceNames = other.voiceNames; voices = new List <Voice>(); Artist = other.Artist; Title = other.Title; Bpm = other.Bpm; Mp3 = other.Mp3; Background = other.Background; Cover = other.Cover; Edition = other.Edition; End = other.End; Gap = other.Gap; Genre = other.Genre; Language = other.Language; Relative = other.Relative; Start = other.Start; PreviewStart = other.PreviewStart; PreviewEnd = other.PreviewEnd; Video = other.Video; VideoGap = other.VideoGap; Year = other.Year; }
public string GetLyrics() { positionInLyricsToNoteMap.Clear(); StringBuilder stringBuilder = new StringBuilder(); List <Sentence> sortedSentences = SongMetaUtils.GetSortedSentences(songMeta); Note lastNote = null; foreach (Sentence sentence in sortedSentences) { List <Note> sortedNotes = SongMetaUtils.GetSortedNotes(sentence); foreach (Note note in sortedNotes) { foreach (char c in note.Text) { positionInLyricsToNoteMap[stringBuilder.Length] = note; stringBuilder.Append(c); } lastNote = note; } stringBuilder.Append("\n"); } if (lastNote != null) { positionInLyricsToNoteMap[stringBuilder.Length] = lastNote; } return(stringBuilder.ToString()); }
protected Note GetNoteAtBeat(int beat) { Sentence sentenceAtBeat = GetSentenceAtBeat(beat); Note noteAtBeat = SongMetaUtils.GetNoteAtBeat(sentenceAtBeat, beat); return(noteAtBeat); }
public bool CanMoveToNextSentence(List <Note> selectedNotes, Note targetNote) { if (selectedNotes.Count != 1) { return(false); } Note selectedNote = selectedNotes[0]; if (selectedNote != targetNote || selectedNote.Sentence == null) { return(false); } // Check that the selected note is the last note in the sentence. List <Note> notesInSentence = new(selectedNote.Sentence.Notes); notesInSentence.Sort(Note.comparerByStartBeat); if (notesInSentence.Last() != selectedNote) { return(false); } // Check that there exists a following sentence Sentence nextSentence = SongMetaUtils.GetNextSentence(selectedNote.Sentence); return(nextSentence != null); }
private void ShowPlayerSelectOverlay() { playerSelectOverlayContainer.ShowByDisplay(); UpdateInputLegend(); // Show lyrics for duet song bool hasMultipleVoices = SelectedSong.VoiceNames.Count > 1; leftLyricsOverlay.SetVisibleByDisplay(hasMultipleVoices); rightLyricsOverlay.SetVisibleByDisplay(hasMultipleVoices); if (hasMultipleVoices) { List <string> voiceNames = SelectedSong.VoiceNames.Values.ToList(); leftLyricsOverlay.Q <Label>(R.UxmlNames.voiceNameLabel).text = voiceNames[0]; leftLyricsOverlay.Q <Label>(R.UxmlNames.lyricsLabel).text = SongMetaUtils.GetLyrics(SelectedSong, Voice.firstVoiceName); rightLyricsOverlay.Q <Label>(R.UxmlNames.voiceNameLabel).text = voiceNames[1]; rightLyricsOverlay.Q <Label>(R.UxmlNames.lyricsLabel).text = SongMetaUtils.GetLyrics(SelectedSong, Voice.secondVoiceName); playerListControl.ShowVoiceSelection(SelectedSong); } else { playerListControl.HideVoiceSelection(); } // Focus start button, such that it can be triggered by keyboard StartCoroutine(CoroutineUtils.ExecuteAfterDelayInFrames(1, () => playerSelectOverlayContainer.Q <Button>(R.UxmlNames.startButton).Focus())); }
// Checks whether the audio and video file formats of the song are supported. // Returns true iff the audio file of the SongMeta exists and is supported. private bool CheckSupportedMediaFormats(SongMeta songMeta) { // Check video format. // Video is optional. if (!songMeta.Video.IsNullOrEmpty()) { if (!ApplicationUtils.IsSupportedVideoFormat(Path.GetExtension(songMeta.Video))) { Debug.LogWarning("Unsupported video format: " + songMeta.Video); songMeta.Video = ""; } else if (!File.Exists(SongMetaUtils.GetAbsoluteSongVideoPath(songMeta))) { Debug.LogWarning("Video file does not exist: " + SongMetaUtils.GetAbsoluteSongVideoPath(songMeta)); songMeta.Video = ""; } } // Check audio format. // Audio is mandatory. Without working audio file, the song cannot be played. if (!ApplicationUtils.IsSupportedAudioFormat(Path.GetExtension(songMeta.Mp3))) { Debug.LogWarning("Unsupported audio format: " + songMeta.Mp3); return(false); } else if (!File.Exists(SongMetaUtils.GetAbsoluteSongFilePath(songMeta))) { Debug.LogWarning("Audio file does not exist: " + SongMetaUtils.GetAbsoluteSongFilePath(songMeta)); return(false); } return(true); }
private void PasteCopiedNotes() { int minBeat = CopiedNotes.Select(it => it.StartBeat).Min(); Sentence sentenceAtBeatWithVoice = SongMetaUtils.GetSentencesAtBeat(songMeta, minBeat) .Where(it => it.Voice != null).FirstOrDefault(); // Find voice to insert the notes into Voice voice; if (copiedVoice != null) { voice = copiedVoice; } else if (sentenceAtBeatWithVoice != null) { voice = sentenceAtBeatWithVoice.Voice; } else { voice = songMeta.GetVoices().FirstOrDefault(); } // Add the notes to the voice foreach (Note note in CopiedNotes) { InsertNote(note, voice); } ClearCopiedNotes(); songMetaChangeEventStream.OnNext(new NotesAddedEvent()); }
public bool CanMoveToPreviousSentence(List <Note> selectedNotes, Note targetNote) { if (selectedNotes.Count != 1) { return(false); } Note selectedNote = selectedNotes[0]; if (selectedNote != targetNote || selectedNote.Sentence == null) { return(false); } // Check that the selected note is the first note in the sentence. List <Note> notesInSentence = new List <Note>(selectedNote.Sentence.Notes); notesInSentence.Sort(Note.comparerByStartBeat); if (notesInSentence.First() != selectedNote) { return(false); } // Check that there exists a previous sentence Sentence previousSentence = SongMetaUtils.GetPreviousSentence(selectedNote.Sentence); return(previousSentence != null); }
protected override void FillContextMenu(ContextMenu contextMenu) { if (PlatformUtils.IsStandalone) { contextMenu.AddItem("Open song folder", () => SongMetaUtils.OpenDirectory(SongMeta)); } }
// Compute the upcoming notes, i.e., the notes that have not yet been finished at the playback position. private List <Note> GetUpcomingSortedNotes(double positionInSongInMillis) { List <Note> result = SongMetaUtils.GetAllNotes(songMeta) .Where(note => BpmUtils.BeatToMillisecondsInSong(songMeta, note.EndBeat) > positionInSongInMillis) .ToList(); result.Sort(Note.comparerByStartBeat); return(result); }
protected void FirePitchEvent(BeatPitchEvent pitchEvent, int fallbackBeat) { int beat = pitchEvent != null ? pitchEvent.Beat : fallbackBeat; Sentence sentenceAtBeat = SongMetaUtils.GetSentenceAtBeat(playerControl.Voice, beat); Note noteAtBeat = SongMetaUtils.GetNoteAtBeat(sentenceAtBeat, beat); playerControl.PlayerMicPitchTracker.FirePitchEvent(pitchEvent, beat, noteAtBeat, sentenceAtBeat); }
// Returns the notes in the song as well as the notes in the layers in no particular order. public List <Note> GetAllNotes() { List <Note> result = new List <Note>(); List <Note> notesInVoices = SongMetaUtils.GetAllNotes(SongMeta); List <Note> notesInLayers = songEditorLayerManager.GetAllNotes(); result.AddRange(notesInVoices); result.AddRange(notesInLayers); return(result); }
private List <Note> GetFollowingNotesOrEmptyListIfDeactivated(List <Note> selectedNotes) { if (settings.SongEditorSettings.AdjustFollowingNotes) { return(SongMetaUtils.GetFollowingNotes(songMeta, selectedNotes)); } else { return(new List <Note>()); } }
private void SetRecordingSentence(int sentenceIndex) { RecordingSentence = playerController.GetSentence(sentenceIndex); if (RecordingSentence == null) { currentAndUpcomingNotesInRecordingSentence = new List <Note>(); BeatToAnalyze = 0; return; } currentAndUpcomingNotesInRecordingSentence = SongMetaUtils.GetSortedNotes(RecordingSentence); BeatToAnalyze = RecordingSentence.MinBeat; }
public void CheckAudioAndStartSingScene() { if (playerSelectOverlayContainer.IsVisibleByDisplay()) { StartSingScene(SelectedSong); } else if (SelectedSong.VoiceNames.Count <= 1 && playerListControl.PlayerEntryControlControls.Count == 1 && micListControl.MicEntryControls.Count == 1) { // There is one mic for only one player and only one voice to sing. // Thus, there is no choice to make and the song can be started immediately. playerListControl.PlayerEntryControlControls[0].MicProfile = micListControl.MicEntryControls[0].MicProfile; playerListControl.PlayerEntryControlControls[0].SetSelected(true); StartSingScene(SelectedSong); } else { if (SelectedSong == null) { return; } // Check that the audio file exists if (!WebRequestUtils.IsHttpOrHttpsUri(SelectedSong.Mp3)) { string audioUri = SongMetaUtils.GetAudioUri(SelectedSong); if (!WebRequestUtils.ResourceExists(audioUri)) { string message = "Audio file resource does not exist: " + audioUri; Debug.Log(message); uiManager.CreateNotificationVisualElement(message); return; } } // Check that the used audio format can be loaded. songAudioPlayer.Init(SelectedSong); if (!songAudioPlayer.HasAudioClip) { string message = $"Audio file '{SelectedSong.Mp3}' could not be loaded.\nPlease use a supported format."; Debug.Log(message); uiManager.CreateNotificationVisualElement(message); return; } ShowPlayerSelectOverlay(); } }
private void FillContextMenu(ContextMenuPopupControl contextMenu) { int beat = (int)noteAreaControl.GetHorizontalMousePositionInBeats(); int midiNote = noteAreaControl.GetVerticalMousePositionInMidiNote(); contextMenu.AddItem("Fit vertical", () => noteAreaControl.FitViewportVerticalToNotes()); Sentence sentenceAtBeat = SongMetaUtils.GetSentencesAtBeat(songMeta, beat).FirstOrDefault(); if (sentenceAtBeat != null) { int minBeat = sentenceAtBeat.MinBeat - 1; int maxBeat = sentenceAtBeat.ExtendedMaxBeat + 1; contextMenu.AddItem("Fit horizontal to sentence ", () => noteAreaControl.FitViewportHorizontal(minBeat, maxBeat)); } List <Note> selectedNotes = selectionControl.GetSelectedNotes(); if (selectedNotes.Count > 0) { int minBeat = selectedNotes.Select(it => it.StartBeat).Min() - 1; int maxBeat = selectedNotes.Select(it => it.EndBeat).Max() + 1; contextMenu.AddItem("Fit horizontal to selection", () => noteAreaControl.FitViewportHorizontal(minBeat, maxBeat)); } if (selectedNotes.Count > 0 || songEditorCopyPasteManager.CopiedNotes.Count > 0) { contextMenu.AddSeparator(); if (selectedNotes.Count > 0) { contextMenu.AddItem("Copy notes", () => songEditorCopyPasteManager.CopySelectedNotes()); } if (songEditorCopyPasteManager.CopiedNotes.Count > 0) { contextMenu.AddItem("Paste notes", () => songEditorCopyPasteManager.PasteCopiedNotes()); } } contextMenu.AddSeparator(); contextMenu.AddItem("Add note", () => addNoteAction.ExecuteAndNotify(songMeta, beat, midiNote)); if (selectedNotes.Count == 0) { contextMenu.AddSeparator(); contextMenu.AddItem("Set Gap to playback position", () => setMusicGapAction.ExecuteAndNotify()); } }
private void Start() { button.OnClickAsObservable().Subscribe(_ => { if (int.TryParse(numberOfBeatsInputField.text, out int spaceInBeats)) { List <Note> notes = selectionController.GetSelectedNotes(); if (notes.IsNullOrEmpty()) { notes = SongMetaUtils.GetAllNotes(songMeta); } spaceBetweenNotesAction.ExecuteAndNotify(notes, spaceInBeats); } }); }
private Note GetNoteForCaretPosition(string text, int caretPosition) { // Count sentence borders int relevantSentenceIndex = 0; int relevantSentenceTextStartIndex = 0; for (int i = 0; i < text.Length && i < caretPosition; i++) { if (text[i] == sentenceSeparator) { relevantSentenceIndex++; relevantSentenceTextStartIndex = i + 1; } } // Get relevant sentence List <Sentence> sortedSentences = SongMetaUtils.GetSortedSentences(Voice); if (relevantSentenceIndex >= sortedSentences.Count || sortedSentences[relevantSentenceIndex].Notes.IsNullOrEmpty()) { return(null); } Sentence relevantSentence = sortedSentences[relevantSentenceIndex]; // Count note borders int noteIndex = 0; for (int i = relevantSentenceTextStartIndex; i < text.Length && i < caretPosition; i++) { char c = text[i]; if (c == spaceCharacter || c == ShowWhiteSpaceText.spaceReplacement[0] || c == syllableSeparator) { noteIndex++; } } // Get relevant note List <Note> sortedNotes = SongMetaUtils.GetSortedNotes(relevantSentence); if (noteIndex >= sortedNotes.Count) { return(null); } return(sortedNotes[noteIndex]); }
private string GetViewModeText() { StringBuilder stringBuilder = new StringBuilder(); List <Sentence> sortedSentences = SongMetaUtils.GetSortedSentences(Voice); foreach (Sentence sentence in sortedSentences) { List <Note> sortedNotes = SongMetaUtils.GetSortedNotes(sentence); foreach (Note note in sortedNotes) { stringBuilder.Append(note.Text); } stringBuilder.Append(sentenceSeparator); } return(stringBuilder.ToString()); }
public void MoveNotesToVoice(SongMeta songMeta, List <Note> selectedNotes, string voiceName) { Voice voice = SongMetaUtils.GetOrCreateVoice(songMeta, voiceName); List <Sentence> changedSentences = new List <Sentence>(); foreach (Note note in selectedNotes) { Sentence oldSentence = note.Sentence; // Find a sentence in the new voice for the note Sentence sentenceForNote = SongMetaUtils.FindExistingSentenceForNote(voice.Sentences, note); if (sentenceForNote == null) { // Create new sentence in the voice. // Use the min and max value from the sentence of the original note if possible. if (note.Sentence != null) { sentenceForNote = new Sentence(note.Sentence.MinBeat, note.Sentence.MaxBeat); } else { sentenceForNote = new Sentence(); } sentenceForNote.SetVoice(voice); } sentenceForNote.AddNote(note); changedSentences.Add(sentenceForNote); if (oldSentence != null) { // Remove old sentence if empty now if (oldSentence.Notes.Count == 0 && oldSentence.Voice != null) { oldSentence.SetVoice(null); } else { changedSentences.Add(oldSentence); } } } // Fit changed sentences to their notes (make them as small as possible) foreach (Sentence sentence in changedSentences) { sentence.FitToNotes(); } }
protected override void FillContextMenu(ContextMenu contextMenu) { contextMenu.AddItem(I18NManager.GetTranslation(R.String.action_reloadSong), () => SongMeta.Reload()); contextMenu.AddItem(I18NManager.GetTranslation(R.String.action_openSongEditor), () => songSelectSceneController.StartSongEditorScene()); if (PlatformUtils.IsStandalone) { contextMenu.AddItem(I18NManager.GetTranslation(R.String.action_openSongFolder), () => SongMetaUtils.OpenDirectory(SongMeta)); AddPlaylistContextMenuItems(contextMenu); } contextMenu.AddSeparator(); }
public void Init(SongMeta songMeta) { this.SongMeta = songMeta; string songPath = SongMetaUtils.GetAbsoluteSongFilePath(songMeta); AudioClip audioClip = AudioManager.Instance.GetAudioClip(songPath); if (audioClip != null) { audioPlayer.clip = audioClip; DurationOfSongInMillis = 1000.0 * audioClip.samples / audioClip.frequency; } else { DurationOfSongInMillis = 0; } }
private static void AnalyzeSentencesInVoice(Voice voice, List <SongIssue> result) { // Find overlapping sentences List <Sentence> sortedSentences = SongMetaUtils.GetSortedSentences(voice); Sentence lastSentence = null; foreach (Sentence sentence in sortedSentences) { if (lastSentence != null && sentence.MinBeat < lastSentence.ExtendedMaxBeat) { SongIssue issue = SongIssue.CreateError("Sentences overlap", sentence.MinBeat, lastSentence.ExtendedMaxBeat); result.Add(issue); } lastSentence = sentence; AnalyzeNotesInSentence(sentence, result); } }
public void MoveToNextSentence(Note targetNote) { Sentence oldSentence = targetNote.Sentence; Sentence nextSentence = SongMetaUtils.GetNextSentence(targetNote.Sentence); targetNote.SetSentence(nextSentence); // Remove old sentence if not more notes left if (oldSentence.Notes.Count == 0) { oldSentence.SetVoice(null); } else { oldSentence.FitToNotes(); } }
private void InsertNote(Note note, Voice voice) { Sentence sentenceAtBeatOfVoice = SongMetaUtils.GetSentencesAtBeat(songMeta, note.StartBeat) .Where(sentence => sentence.Voice == voice).FirstOrDefault(); if (sentenceAtBeatOfVoice == null) { // Add sentence with note Sentence newSentence = new Sentence(new List <Note> { note }, note.EndBeat); newSentence.SetVoice(voice); } else { // Add note to existing sentence note.SetSentence(sentenceAtBeatOfVoice); } }
private void Start() { button.OnClickAsObservable().Subscribe(_ => { if (int.TryParse(numberOfBeatsInputField.text, out int spaceInBeats)) { List <Note> selectedNotes = selectionController.GetSelectedNotes(); if (selectedNotes.IsNullOrEmpty()) { // Perform on all notes, but per voice songMeta.GetVoices() .ForEach(voice => spaceBetweenNotesAction.ExecuteAndNotify(SongMetaUtils.GetAllNotes(voice), spaceInBeats)); } else { spaceBetweenNotesAction.ExecuteAndNotify(selectedNotes, spaceInBeats); } } }); }
private string GetEditModeText() { StringBuilder stringBuilder = new StringBuilder(); List <Sentence> sortedSentences = SongMetaUtils.GetSortedSentences(Voice); Note lastNote = null; void ProcessNote(Note note) { if (lastNote != null && lastNote.Sentence == note.Sentence) { // Add a space when the last note ended or the current note started with a space. // Otherwise use the non-whitespace syllabeSeparator as end-of-note. if (lastNote.Text.EndsWith(spaceCharacter) || note.Text.StartsWith(spaceCharacter)) { stringBuilder.Append(spaceCharacter); } else { stringBuilder.Append(syllableSeparator); } } stringBuilder.Append(note.Text.Trim()); lastNote = note; } void ProcessSentence(Sentence sentence) { List <Note> sortedNotes = SongMetaUtils.GetSortedNotes(sentence); sortedNotes.ForEach(ProcessNote); stringBuilder.Append(sentenceSeparator); } sortedSentences.ForEach(ProcessSentence); return(stringBuilder.ToString()); }
public void OnInjectionFinished() { videoAreaLabel.HideByDisplay(); if (!songMeta.Video.IsNullOrEmpty()) { ShowVideoImage(); } else if (!songMeta.Cover.IsNullOrEmpty()) { ShowCoverImage(); } else if (!songMeta.Background.IsNullOrEmpty()) { ShowBackgroundImage(); } // Init cover and background image if (!songMeta.Background.IsNullOrEmpty()) { ImageManager.LoadSpriteFromUri(SongMetaUtils.GetBackgroundUri(songMeta), sprite => songBackgroundImage.style.backgroundImage = new StyleBackground(sprite)); } // Init cover and background image if (!songMeta.Cover.IsNullOrEmpty()) { ImageManager.LoadSpriteFromUri(SongMetaUtils.GetCoverUri(songMeta), sprite => songCoverImage.style.backgroundImage = new StyleBackground(sprite)); } showVideoButton.RegisterCallbackButtonTriggered(() => ShowVideoImage()); showBackgroundButton.RegisterCallbackButtonTriggered(() => ShowBackgroundImage()); showCoverButton.RegisterCallbackButtonTriggered(() => ShowCoverImage()); dragControl = injector .WithRootVisualElement(videoImage) .CreateAndInject <GeneralDragControl>(); dragControl.AddListener(this); videoImage.RegisterCallback <PointerEnterEvent>(evt => cursorManager.SetCursorHorizontal()); videoImage.RegisterCallback <PointerLeaveEvent>(evt => cursorManager.SetDefaultCursor()); }
private void AdjustFollowingNotes(EKeyboardModifier modifier, Vector2 arrowKeyDirection, List <Note> selectedNotes) { // Moving is applied to following notes as well. // When extending / shrinking the right side, then the following notes are move to compensate. List <Note> followingNotes = SongMetaUtils.GetFollowingNotes(songMeta, selectedNotes); foreach (Note note in followingNotes) { // Moved with Shift. The following notes are moved as well. if (modifier == EKeyboardModifier.Shift) { note.MoveHorizontal((int)arrowKeyDirection.x); note.MoveVertical((int)arrowKeyDirection.y); } // Extended right side with Alt. The following notes must be moved to compensate. if (modifier == EKeyboardModifier.Alt) { note.MoveHorizontal((int)arrowKeyDirection.x); } } }