/// <summary> /// Gets the nearest snap time at a given direction. /// </summary> /// <param name="map"></param> /// <param name="direction"></param> /// <param name="snap"></param> /// <param name="time"></param> /// <returns></returns> /// <exception cref="AudioEngineException"></exception> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> public static double GetNearestSnapTimeFromTime(Qua map, Direction direction, int snap, double time) { if (map == null) { throw new ArgumentNullException(nameof(map)); } // Get the current timing point var point = map.GetTimingPointAt(time); if (point == null) { return(0); } // Get the amount of milliseconds that each snap takes in the beat. var snapTimePerBeat = 60000 / point.Bpm / snap; // The point in the music that we want to snap to pre-rounding. double pointToSnap; switch (direction) { case Direction.Forward: pointToSnap = time + snapTimePerBeat; break; case Direction.Backward: pointToSnap = time - snapTimePerBeat; break; default: throw new ArgumentOutOfRangeException(nameof(direction), direction, null); } var nearestTick = Math.Round((pointToSnap - point.StartTime) / snapTimePerBeat) * snapTimePerBeat + point.StartTime; if ((int)Math.Abs(nearestTick - time) <= (int)snapTimePerBeat) { return(nearestTick); } if (direction == Direction.Backward) { return((Math.Round((pointToSnap - point.StartTime) / snapTimePerBeat) + 1) * snapTimePerBeat + point.StartTime); } return((Math.Round((pointToSnap - point.StartTime) / snapTimePerBeat) - 1) * snapTimePerBeat + point.StartTime); }
/// <summary> /// </summary> /// <param name="gameTime"></param> public void Update(GameTime gameTime) { if (!AudioEngine.Track.IsPlaying || Qua.TimingPoints.Count == 0) { return; } var time = AudioEngine.Track.Time + ConfigManager.GlobalAudioOffset.Value; var point = Qua.GetTimingPointAt(time); if (time < point.StartTime) { LastBeat = -1; return; } // Get the total amount of beats that'll be played for the timing point. // This can depend on if the user wants 8 beats or 4. var totalBeats = (time - point.StartTime) / (point.MillisecondsPerBeat / (ConfigManager.EditorMetronomePlayHalfBeats.Value ? 2 : 1)); CurrentTotalBeats = (int)Math.Floor(totalBeats); CurrentBeat = (int)totalBeats % 4; // Play samples if (CurrentTotalBeats == 0 && LastTotalBeats < 0 || CurrentBeat != LastBeat) { if (CurrentBeat == 0) { MeasureTickSample.CreateChannel().Play(); } else { BeatTickSample.CreateChannel().Play(); } } // Keep track of the last beat & last total beats in the current frame, so we know when to play // the next sample. LastBeat = CurrentBeat; LastTotalBeats = CurrentTotalBeats; }
/// <summary> /// Seeks to the nearest snap(th) beat in the audio based on the /// current timing point's snap. /// </summary> /// <param name="map"></param> /// <param name="direction"></param> /// <param name="snap"></param> public static void SeekTrackToNearestSnap(Qua map, Direction direction, int snap) { if (Track == null || Track.IsDisposed || Track.IsStopped) { throw new AudioEngineException("Cannot seek to nearest snap if a track isn't loaded"); } if (map == null) { throw new ArgumentNullException(nameof(map)); } // Get the current timing point var point = map.GetTimingPointAt(Track.Time); // Get the amount of milliseconds that each snap takes in the beat. var snapTimePerBeat = 60000 / point.Bpm / snap; // The point in the music that we want to snap to pre-rounding. double pointToSnap; switch (direction) { case Direction.Forward: pointToSnap = Track.Time + snapTimePerBeat; break; case Direction.Backward: pointToSnap = Track.Time - snapTimePerBeat; break; default: throw new ArgumentOutOfRangeException(nameof(direction), direction, null); } // Snap the value and seek to it. var seekTime = Math.Round((pointToSnap - point.StartTime) / snapTimePerBeat) * snapTimePerBeat + point.StartTime; Track.Seek(seekTime); }