private void HandleNoteHit()
        {
            double dspTime         = AudioSettings.dspTime;
            double closestHalfNote = RoundToMetronome(dspTime);
            double closestFullNote = RoundToMetronomeFull(dspTime);

            _lastNoteTimeAbs = closestHalfNote;
            float noteTimeDiff       = (float)(_lastNoteTimeAbs - dspTime);
            float noteTimeDiffToFull = (float)(closestFullNote - dspTime);
            bool  canCreateBeat      = !HasBeat || _currentBeatRunTime >= 8 * NOTE_TIME - FAIL_TOLERANCE;

            if (canCreateBeat && Mathf.Abs(noteTimeDiffToFull) <= FAIL_TOLERANCE)
            {
                _currentBeatRunTime      = noteTimeDiffToFull;
                _currentBeatStartTimeAbs = RoundToMetronome(dspTime);
            }
            NoteQuality hitNoteQuality = CalcNoteQuality(noteTimeDiff);

            if (hitNoteQuality != NoteQuality.Miss)
            {
                _currentNotes.Add(Mathf.RoundToInt((float)_currentBeatRunTime / HALF_NOTE_TIME) * .5f);
                _currentQualities.Add(hitNoteQuality);
            }

            HasBeat = true;
            float[]     notesAsArray  = _currentNotes.ToArray();
            List <Song> matchingSongs = _songService.CheckSongs(notesAsArray);

            if (matchingSongs.Count == 1 && matchingSongs[0].Matches(notesAsArray))
            {
                ExecuteSong(hitNoteQuality, noteTimeDiff, matchingSongs[0]);
                return;
            }
            if (matchingSongs.Count == 0)
            {
                hitNoteQuality = NoteQuality.Miss;
                Debug.Log("No songs detected with that beat! Current beat was " + string.Join("-", _currentNotes));
            }
            NoteHit?.Invoke(hitNoteQuality, noteTimeDiff);
            if (hitNoteQuality == NoteQuality.Miss)
            {
                HandleBeatLost();
            }
        }
        private void ExecuteSong(NoteQuality hitNoteQuality, float beatTimeDiff, Song matchingSong)
        {
            NoteHit?.Invoke(hitNoteQuality, beatTimeDiff);
            if (_beatInputHandler == Constants.Noop)
            {
                // don't execute song if game has been finished with the last note
                return;
            }
            _streakScore += _currentQualities.Aggregate(0, (total, curQuality) => total + (int)curQuality);
            _streakPower  = Mathf.Min(_streakScore / Constants.REQUIRED_STREAK_SCORE, Constants.MAX_STREAK_POWER);
            _currentSong  = matchingSong;
            int curStreakPower = _streakPower;

            _currentSong.ExecuteCommand(hitNoteQuality, curStreakPower);
            _coroutineProvider.StartCoroutine(Coroutines.ExecuteAfterSeconds(HALF_NOTE_TIME, () => {
                ExecutionStarted?.Invoke(matchingSong, curStreakPower);
                DisableTouchHandler();
            }));
            ExecutionStarting?.Invoke(_currentSong, curStreakPower);
            _currentCommandUpdate = _currentSong.ExecuteCommandUpdate;
            ResetBeatAfterSeconds(NOTE_TIME * 4);
        }