Example #1
0
    void UpdateTiming()
    {
        long numSamples;
        int  tempOut;

        if (!playback_.GetNumPlayedSamples(out numSamples, out tempOut))
        {
            numSamples = -1;
        }
        isNearChanged_ = false;
        isJustChanged_ = false;
        currentSample_ = (int)numSamples;
        if (currentSample_ >= 0)
        {
            just_.Bar  = (int)(currentSample_ / samplesPerBar_);
            just_.Beat = (int)((currentSample_ - just_.Bar * samplesPerBar_) / samplesPerBeat_);
            just_.Unit = (int)((currentSample_ - just_.Bar * samplesPerBar_ - just_.Beat * samplesPerBeat_) / samplesPerUnit_);
            just_.Fix();
            if (numBlockBar_ > 0)
            {
                while (just_.Bar >= numBlockBar_)
                {
                    just_--;
                }
            }

            timeSecFromJust_ = (double)(currentSample_ - just_.Bar * samplesPerBar_ - just_.Beat * samplesPerBeat_ - just_.Unit * samplesPerUnit_) / (double)SamplingRate;
            isFormerHalf_    = (timeSecFromJust_ * SamplingRate) < samplesPerUnit_ / 2;

            near_.Copy(just_);
            if (!isFormerHalf_)
            {
                near_++;
                near_.LoopBack(numBlockBar_);
            }

            isJustChanged_ = (just_.Equals(oldJust_) == false);
            isNearChanged_ = (near_.Equals(oldNear_) == false);

            CallEvents();

            oldNear_.Copy(near_);
            oldJust_.Copy(just_);
        }

        if (DebugText != null)
        {
            DebugText.text = "Just = " + Just.ToString() + ", MusicalTime = " + MusicalTime_;
            if (BlockInfos.Count > 0)
            {
                DebugText.text += System.Environment.NewLine + "block[" + currentBlockIndex_ + "] = " + CurrentBlock_.BlockName + "(" + numBlockBar_ + "bar)";
            }
        }
    }
Example #2
0
    /// <summary>
    /// dif from timing to Just on musical time unit.
    /// </summary>
    /// <param name="timing"></param>
    /// <returns></returns>
    public static float MusicalTimeFrom(Timing timing)
    {
        int index = 0;

        for (int i = 0; i < SectionCount; ++i)
        {
            if (i + 1 < SectionCount)
            {
                if (timing < Current_[i + 1].StartTiming)
                {
                    index = i;
                    break;
                }
            }
            else
            {
                index = i;
            }
        }
        int    startIndex    = Mathf.Min(index, Current_.sectionIndex_);
        int    endIndex      = Mathf.Max(index, Current_.sectionIndex_);
        Timing currentTiming = new Timing(timing < Just ? timing : Just);
        Timing endTiming     = (timing > Just ? timing : Just);
        int    musicalTime   = 0;

        for (int i = startIndex; i <= endIndex; ++i)
        {
            if (i < endIndex)
            {
                musicalTime += Current_[i + 1].StartTiming.GetMusicalTime(Current_[i]) - currentTiming.GetMusicalTime(Current_[i]);
                currentTiming.Copy(Current_[i + 1].StartTiming);
            }
            else
            {
                musicalTime += endTiming.GetMusicalTime(Current_[i]) - currentTiming.GetMusicalTime(Current_[i]);
            }
        }
        return((float)((timing > Just ? -1 : 1) * musicalTime + TimeSecFromJust / MusicTimeUnit));
    }
Example #3
0
    void UpdateTiming()
    {
        // find section index
        int newIndex = sectionIndex_;

        if (CreateSectionClips)
        {
            newIndex       = sectionSources_.IndexOf(musicSource_);
            currentSample_ = musicSource_.timeSamples;
        }
        else
        {
            int oldSample = currentSample_;
            currentSample_ = musicSource_.timeSamples;
            if (sectionIndex_ + 1 >= Sections.Count)
            {
                if (currentSample_ < oldSample)
                {
                    newIndex = 0;
                }
            }
            else
            {
                if (Sections[sectionIndex_ + 1].StartTimeSamples <= currentSample_)
                {
                    newIndex = sectionIndex_ + 1;
                }
            }
        }

        if (newIndex != sectionIndex_)
        {
            sectionIndex_ = newIndex;
            OnSectionChanged();
        }

        // calc current timing
        isNearChanged_ = false;
        isJustChanged_ = false;
        int sectionSample = currentSample_ - (CreateSectionClips ? 0 : CurrentSection_.StartTimeSamples);

        if (sectionSample >= 0)
        {
            just_.Bar  = (int)(sectionSample / samplesPerBar_) + CurrentSection_.StartBar;
            just_.Beat = (int)((sectionSample % samplesPerBar_) / samplesPerBeat_);
            just_.Unit = (int)(((sectionSample % samplesPerBar_) % samplesPerBeat_) / samplesPerUnit_);
            just_.Fix(CurrentSection_);
            if (CreateSectionClips)
            {
                if (CurrentSection_.LoopType == Section.ClipType.Loop && numLoopBar_ > 0)
                {
                    just_.Bar -= CurrentSection_.StartBar;
                    while (just_.Bar >= numLoopBar_)
                    {
                        just_.Decrement(CurrentSection_);
                    }
                    just_.Bar += CurrentSection_.StartBar;
                }

                if (isTransitioning_ && just_.Equals(transitionTiming_))
                {
                    if (CurrentSection_.LoopType == Section.ClipType.Loop && just_.Bar == CurrentSection_.StartBar)
                    {
                        just_.Bar = CurrentSection_.StartBar + numLoopBar_;
                    }
                    just_.Decrement(CurrentSection_);
                }
            }
            else
            {
                if (sectionIndex_ + 1 >= Sections.Count)
                {
                    if (numLoopBar_ > 0)
                    {
                        while (just_.Bar >= numLoopBar_)
                        {
                            just_.Decrement(CurrentSection_);
                        }
                    }
                }
                else
                {
                    while (just_.Bar >= Sections[sectionIndex_ + 1].StartBar)
                    {
                        just_.Decrement(CurrentSection_);
                    }
                }
            }

            just_.Bar       -= CurrentSection_.StartBar;
            timeSecFromJust_ = (double)(sectionSample - just_.Bar * samplesPerBar_ - just_.Beat * samplesPerBeat_ - just_.Unit * samplesPerUnit_) / (double)samplingRate_;
            isFormerHalf_    = (timeSecFromJust_ * samplingRate_) < samplesPerUnit_ / 2;
            just_.Bar       += CurrentSection_.StartBar;

            near_.Copy(just_);
            if (!isFormerHalf_)
            {
                near_.Increment(CurrentSection_);
            }
            if (samplesInLoop_ != 0 && currentSample_ + samplesPerUnit_ / 2 >= samplesInLoop_)
            {
                near_.Init();
            }

            isNearChanged_ = (near_.Equals(oldNear_) == false);
            isJustChanged_ = (just_.Equals(oldJust_) == false);

            CallEvents();

            oldNear_.Copy(near_);
            oldJust_.Copy(just_);
        }

        if (DebugText != null)
        {
            DebugText.text = "Just = " + Just.ToString() + ", MusicalTime = " + MusicalTime_;
            if (Sections.Count > 0)
            {
                DebugText.text += System.Environment.NewLine + "section[" + sectionIndex_ + "] = " + CurrentSection_.ToString();
            }
        }
        else if (DebugPrint)
        {
            string text = "Just = " + Just.ToString() + ", MusicalTime = " + MusicalTime_;
            if (Sections.Count > 0)
            {
                text += System.Environment.NewLine + "section[" + sectionIndex_ + "] = " + CurrentSection_.ToString();
            }
            Debug.Log(text);
        }
    }
Example #4
0
    void SetNextSection_(int sectionIndex, SyncType syncType = SyncType.NextBar)
    {
        if (CreateSectionClips == false || isTransitioning_)
        {
            return;
        }

        if (sectionIndex < 0 || SectionCount <= sectionIndex || sectionIndex == sectionIndex_)
        {
            return;
        }

        int syncUnit = 0;

        transitionTiming_.Copy(just_);
        switch (syncType)
        {
        case SyncType.NextBeat:
            syncUnit = samplesPerBeat_;
            transitionTiming_.Beat += 1;
            transitionTiming_.Unit  = 0;
            break;

        case SyncType.Next2Beat:
            syncUnit = samplesPerBeat_ * 2;
            transitionTiming_.Beat += 2;
            transitionTiming_.Unit  = 0;
            break;

        case SyncType.NextBar:
            syncUnit = samplesPerBar_;
            transitionTiming_.Bar += 1;
            transitionTiming_.Beat = transitionTiming_.Unit = 0;
            break;

        case SyncType.Next2Bar:
            syncUnit = samplesPerBar_ * 2;
            transitionTiming_.Bar += 2;
            transitionTiming_.Beat = transitionTiming_.Unit = 0;
            break;

        case SyncType.Next4Bar:
            syncUnit = samplesPerBar_ * 4;
            transitionTiming_.Bar += 4;
            transitionTiming_.Beat = transitionTiming_.Unit = 0;
            break;

        case SyncType.Next8Bar:
            syncUnit = samplesPerBar_ * 8;
            transitionTiming_.Bar += 8;
            transitionTiming_.Beat = transitionTiming_.Unit = 0;
            break;

        case SyncType.SectionEnd:
            syncUnit = samplesInLoop_;
            transitionTiming_.Bar  = CurrentSection_.StartBar + numLoopBar_;
            transitionTiming_.Beat = transitionTiming_.Unit = 0;
            break;
        }
        transitionTiming_.Fix(CurrentSection_);
        if (CurrentSection_.LoopType == Section.ClipType.Loop && transitionTiming_.Bar >= CurrentSection_.StartBar + numLoopBar_)
        {
            transitionTiming_.Bar -= numLoopBar_;
        }

        if (syncUnit <= 0)
        {
            return;
        }

        double transitionTime = AudioSettings.dspTime + (syncUnit - musicSource_.timeSamples % syncUnit) / (double)samplingRate_ / musicSource_.pitch;

        sectionSources_[sectionIndex].PlayScheduled(transitionTime);
        sectionSources_[sectionIndex_].SetScheduledEndTime(transitionTime);
        isTransitioning_ = true;
    }
Example #5
0
    // Update is called once per frame
    void Update()
    {
        long numSamples;

        isNowChanged_  = false;
        isJustChanged_ = false;
#if ADX
        if (playback.GetStatus() != CriAtomExPlayback.Status.Playing)
        {
            return;
        }
        int tempOut;
        if (!playback.GetNumPlayedSamples(out numSamples, out tempOut))
        {
            numSamples = -1;
        }
#else
        if (!MusicSource.IsPlaying())
        {
            return;
        }
        numSamples = MusicSource.source.timeSamples;
#endif
        if (numSamples >= 0)
        {
            Just_.bar = (int)(numSamples / SamplesPerBar);
#if ADX
            UpdateNumBlockBar(numSamples);
            if (NumBlockBar != 0)
            {
                Just_.bar %= NumBlockBar;
            }
#else
            if (NumBar != 0)
            {
                Just_.bar %= NumBar;
            }
#endif
            Just_.beat    = (int)((numSamples % SamplesPerBar) / SamplesPerBeat);
            Just_.unit    = (int)((numSamples % SamplesPerBeat) / SamplesPerUnit);
            isFormerHalf_ = (numSamples % SamplesPerUnit) < SamplesPerUnit / 2;
            dtFromJust_   = (double)(numSamples % SamplesPerUnit) / (double)SamplingRate;

            Now_.Copy(Just_);
            if (!isFormerHalf_)
            {
                Now_.Increment();
            }
            if (SamplesInLoop != 0 && numSamples + SamplesPerUnit / 2 >= SamplesInLoop)
            {
                Now_.Init();
            }

            isNowChanged_  = Now_.totalUnit != OldNow.totalUnit;
            isJustChanged_ = Just_.totalUnit != OldJust.totalUnit;

            CallEvents();

            OldNow.Copy(Now_);
            OldJust.Copy(Just_);
        }
        else
        {
            //Debug.LogWarning( "Warning!! Failed to GetNumPlayedSamples" );
        }
    }
Example #6
0
        /// <summary>
        /// Does a procedure similar to <see cref="MapCleaner"/> which adjusts the pattern so it fits in the beatmap.
        /// It does so according to the options selected in this.
        /// </summary>
        /// <param name="patternBeatmap"></param>
        /// <param name="beatmap"></param>
        /// <param name="parts"></param>
        /// <param name="timingPointsChanges"></param>
        private void PreparePattern(Beatmap patternBeatmap, Beatmap beatmap, out List <Part> parts, out List <TimingPointsChange> timingPointsChanges)
        {
            double patternStartTime = patternBeatmap.GetHitObjectStartTime();

            Timing originalTiming = beatmap.BeatmapTiming;
            Timing patternTiming  = patternBeatmap.BeatmapTiming;

            GameMode targetMode = (GameMode)beatmap.General["Mode"].IntValue;

            double originalCircleSize = beatmap.Difficulty["CircleSize"].DoubleValue;
            double patternCircleSize  = patternBeatmap.Difficulty["CircleSize"].DoubleValue;

            double originalTickRate = beatmap.Difficulty["SliderTickRate"].DoubleValue;
            double patternTickRate  = patternBeatmap.Difficulty["SliderTickRate"].DoubleValue;

            // Don't include SV changes if it is based on nothing
            bool includePatternSliderVelocity = patternTiming.Count > 0;

            // Avoid including hitsounds if there are no timingpoints to get hitsounds from
            bool includeTimingPointHitsounds = IncludeHitsounds && patternTiming.Count > 0;

            // Don't scale to new timing if the pattern has no timing to speak of
            bool scaleToNewTiming = ScaleToNewTiming && patternTiming.Redlines.Count > 0;

            // Avoid overwriting timing if the pattern has no redlines
            TimingOverwriteMode timingOverwriteMode = patternTiming.Redlines.Count > 0
                ? TimingOverwriteMode
                : TimingOverwriteMode.OriginalTimingOnly;

            // Get the scale for custom scale x CS scale
            double csScale = Beatmap.GetHitObjectRadius(originalCircleSize) /
                             Beatmap.GetHitObjectRadius(patternCircleSize);
            double spatialScale = ScaleToNewCircleSize && !double.IsNaN(csScale) ? CustomScale * csScale : CustomScale;

            // Get a BPM multiplier to fix the tick rate
            // This multiplier is not meant to change SV so this is subtracted from the greenline SV later
            double bpmMultiplier = FixTickRate ? patternTickRate / originalTickRate : 1;

            // Dont give new combo to all hit objects which were actually new combo in the pattern,
            // because it leads to unexpected NC's at the start of patterns.

            // Collect Kiai toggles
            List <TimingPoint> kiaiToggles = new List <TimingPoint>();
            bool lastKiai = false;

            // If not including the kiai of the pattern, add the kiai of the original map.
            // This has to be done because this part of the original map might get deleted.
            foreach (TimingPoint tp in IncludeKiai ? patternTiming.TimingPoints : originalTiming.TimingPoints)
            {
                if (tp.Kiai != lastKiai || kiaiToggles.Count == 0)
                {
                    kiaiToggles.Add(tp.Copy());
                    lastKiai = tp.Kiai;
                }
            }

            // Collect SliderVelocity changes for mania/taiko
            List <TimingPoint> svChanges = new List <TimingPoint>();
            double             lastSV    = -100;

            // If not including the SV of the pattern, add the SV of the original map.
            // This has to be done because this part of the original map might get deleted.
            foreach (TimingPoint tp in includePatternSliderVelocity ? patternTiming.TimingPoints : originalTiming.TimingPoints)
            {
                if (tp.Uninherited)
                {
                    lastSV = -100;
                }
                else
                {
                    if (Math.Abs(tp.MpB - lastSV) > Precision.DOUBLE_EPSILON)
                    {
                        svChanges.Add(tp.Copy());
                        lastSV = tp.MpB;
                    }
                }
            }

            // If not including the SV of the pattern, set the SV of sliders to that of the original beatmap,
            // so the pattern will take over the SV of the original beatmap.
            if (!includePatternSliderVelocity)
            {
                foreach (var ho in patternBeatmap.HitObjects.Where(ho => ho.IsSlider))
                {
                    ho.SliderVelocity = originalTiming.GetSvAtTime(ho.Time);
                }
            }

            // Get the timeline before moving all objects so it has the correct hitsounds
            // Make sure that moving the objects in the pattern moves the timeline objects aswell
            // This method is NOT safe to use in beat time
            Timeline patternTimeline         = patternBeatmap.GetTimeline();
            Timing   transformOriginalTiming = originalTiming;
            Timing   transformPatternTiming  = patternTiming;

            if (scaleToNewTiming)
            {
                // Transform everything to beat time relative to pattern start time
                foreach (var ho in patternBeatmap.HitObjects)
                {
                    double oldEndTime = ho.GetEndTime(false);

                    ho.Time    = patternTiming.GetBeatLength(patternStartTime, ho.Time);
                    ho.EndTime = patternTiming.GetBeatLength(patternStartTime, oldEndTime);

                    // The body hitsounds are not copies of timingpoints in patternTiming so they should be copied before changing offset
                    for (int i = 0; i < ho.BodyHitsounds.Count; i++)
                    {
                        TimingPoint tp = ho.BodyHitsounds[i].Copy();
                        tp.Offset           = patternTiming.GetBeatLength(patternStartTime, tp.Offset);
                        ho.BodyHitsounds[i] = tp;
                    }
                }

                foreach (var tp in kiaiToggles.Concat(svChanges))
                {
                    tp.Offset = patternTiming.GetBeatLength(patternStartTime, tp.Offset);
                }

                // Transform the pattern redlines to beat time
                // This will not change the order of redlines (unless negative BPM exists)
                transformPatternTiming = patternTiming.Copy();
                foreach (var tp in transformPatternTiming.Redlines)
                {
                    tp.Offset = patternTiming.GetBeatLength(patternStartTime, tp.Offset);
                }

                // Transform the original timingpoints to beat time
                // This will not change the order of timingpoints (unless negative BPM exists)
                transformOriginalTiming = originalTiming.Copy();
                foreach (var tp in transformOriginalTiming.TimingPoints)
                {
                    tp.Offset = originalTiming.GetBeatLength(patternStartTime, tp.Offset);
                }
            }

            // Fix SV for the new global SV
            var globalSvFactor = transformOriginalTiming.SliderMultiplier / transformPatternTiming.SliderMultiplier;

            if (FixGlobalSv)
            {
                foreach (HitObject ho in patternBeatmap.HitObjects.Where(o => o.IsSlider))
                {
                    ho.SliderVelocity *= globalSvFactor;
                }
                foreach (TimingPoint tp in svChanges)
                {
                    tp.MpB *= globalSvFactor;
                }
            }
            else
            {
                foreach (HitObject ho in patternBeatmap.HitObjects.Where(o => o.IsSlider))
                {
                    ho.TemporalLength /= globalSvFactor;
                }
            }

            // Partition the pattern based on the timing in the pattern
            if (PatternOverwriteMode == PatternOverwriteMode.PartitionedOverwrite)
            {
                parts = PartitionBeatmap(patternBeatmap, scaleToNewTiming);
            }
            else
            {
                parts = new List <Part> {
                    new Part(patternBeatmap.HitObjects[0].Time,
                             patternBeatmap.HitObjects[patternBeatmap.HitObjects.Count - 1].Time,
                             patternBeatmap.HitObjects)
                };
            }

            // Construct a new timing which is a mix of the beatmap and the pattern.
            // If scaleToNewTiming then use beat relative values to determine the duration of timing sections in the pattern.
            // scaleToNewTiming must scale all the partitions, timingpoints, hitobjects, and events (if applicable).
            Timing newTiming = new Timing(transformOriginalTiming.SliderMultiplier);

            var lastEndTime = double.NegativeInfinity;

            foreach (var part in parts)
            {
                var startTime = part.StartTime;
                var endTime   = part.EndTime; // Subtract one to omit BPM changes right on the end of the part.

                // Add the redlines in between patterns
                newTiming.AddRange(transformOriginalTiming.GetRedlinesInRange(lastEndTime, startTime, false));

                var startOriginalRedline = transformOriginalTiming.GetRedlineAtTime(startTime);

                // Minus 1 the offset so its possible to have a custom BPM redline right on the start time if you have
                // the default BPM redline before it.
                var patternDefaultMpb = transformPatternTiming.GetMpBAtTime(startTime - 2 * Precision.DOUBLE_EPSILON);

                TimingPoint[] inPartRedlines;
                TimingPoint   startPartRedline;
                switch (timingOverwriteMode)
                {
                case TimingOverwriteMode.PatternTimingOnly:
                    // Subtract one from the end time to omit BPM changes right on the end of the part.
                    inPartRedlines = transformPatternTiming.GetRedlinesInRange(startTime,
                                                                               Math.Max(startTime, endTime - 2 * Precision.DOUBLE_EPSILON)).ToArray();
                    startPartRedline = transformPatternTiming.GetRedlineAtTime(startTime);
                    break;

                case TimingOverwriteMode.InPatternAbsoluteTiming:
                    var tempInPartRedlines = transformPatternTiming.GetRedlinesInRange(startTime, endTime - 2 * Precision.DOUBLE_EPSILON);

                    // Replace all parts in the pattern which have the default BPM to timing from the target beatmap.
                    inPartRedlines = tempInPartRedlines.Select(tp => {
                        if (Precision.AlmostEquals(tp.MpB, patternDefaultMpb))
                        {
                            var tp2    = transformOriginalTiming.GetRedlineAtTime(tp.Offset).Copy();
                            tp2.Offset = tp2.Offset;
                            return(tp2);
                        }

                        return(tp);
                    }).ToArray();

                    startPartRedline = startOriginalRedline;
                    break;

                case TimingOverwriteMode.InPatternRelativeTiming:
                    // Multiply mix the pattern timing and the original timing together.
                    // The pattern timing divided by the default BPM will be used as a scalar for the original timing.
                    var tempInPartRedlines2    = transformPatternTiming.GetRedlinesInRange(startTime, endTime - 2 * Precision.DOUBLE_EPSILON);
                    var tempInOriginalRedlines = transformOriginalTiming.GetRedlinesInRange(startTime, endTime - 2 * Precision.DOUBLE_EPSILON);

                    // Replace all parts in the pattern which have the default BPM to timing from the target beatmap.
                    inPartRedlines = tempInPartRedlines2.Select(tp => {
                        var tp2  = tp.Copy();
                        tp2.MpB *= transformOriginalTiming.GetMpBAtTime(tp.Offset) / patternDefaultMpb;
                        return(tp2);
                    }).Concat(tempInOriginalRedlines.Select(tp => {
                        var tp2  = tp.Copy();
                        tp2.MpB *= transformPatternTiming.GetMpBAtTime(tp.Offset) / patternDefaultMpb;
                        return(tp2);
                    })).ToArray();

                    startPartRedline      = transformPatternTiming.GetRedlineAtTime(startTime).Copy();
                    startPartRedline.MpB *= transformOriginalTiming.GetMpBAtTime(startTime) / patternDefaultMpb;
                    break;

                default:      // Original timing only
                    // Subtract one from the end time to omit BPM changes right on the end of the part.
                    inPartRedlines = transformOriginalTiming.GetRedlinesInRange(startTime,
                                                                                Math.Max(startTime, endTime - 2 * Precision.DOUBLE_EPSILON)).ToArray();
                    startPartRedline = transformOriginalTiming.GetRedlineAtTime(startTime);
                    break;
                }

                // Add the redlines for inside the part
                newTiming.AddRange(inPartRedlines);

                // If the pattern starts with different BPM than the map add an extra redline at the start of the pattern
                // to make sure it the pattern starts out at the right BPM as we only copy the timingpoints during the pattern itself
                // and the redline may be way before that.
                // This will probably only do something on the PatternTimingOnly mode as the other modes make sure
                // the BPM at the start of the pattern will be the same as the original beatmap anyways.
                if (Math.Abs(startPartRedline.MpB * bpmMultiplier - startOriginalRedline.MpB) > Precision.DOUBLE_EPSILON)
                {
                    // We dont have to add the redline again if its already during the pattern.
                    if (Math.Abs(startPartRedline.Offset - startTime) > Precision.DOUBLE_EPSILON)
                    {
                        var copy = startPartRedline.Copy();
                        copy.Offset = startTime;
                        newTiming.Add(copy);
                    }
                }

                // Fix SV for the new BPM, so the SV effect of the new BPM is cancelled
                if (FixBpmSv)
                {
                    if (scaleToNewTiming)
                    {
                        foreach (HitObject ho in patternBeatmap.HitObjects.Where(o => o.IsSlider))
                        {
                            var bpmSvFactor = SnapToNewTiming ?
                                              transformPatternTiming.GetMpBAtTime(ho.Time) /
                                              newTiming.GetMpBAtTime(newTiming.ResnapBeatTime(ho.Time, BeatDivisors)) :
                                              transformPatternTiming.GetMpBAtTime(ho.Time) /
                                              newTiming.GetMpBAtTime(ho.Time);
                            ho.SliderVelocity *= bpmSvFactor;
                        }
                    }
                    else
                    {
                        foreach (HitObject ho in patternBeatmap.HitObjects.Where(o => o.IsSlider))
                        {
                            var bpmSvFactor = SnapToNewTiming ?
                                              transformPatternTiming.GetMpBAtTime(ho.Time) / newTiming.GetMpBAtTime(newTiming.Resnap(ho.Time, BeatDivisors)) :
                                              transformPatternTiming.GetMpBAtTime(ho.Time) / newTiming.GetMpBAtTime(ho.Time);
                            ho.SliderVelocity *= bpmSvFactor;
                        }
                    }
                }

                // Recalculate temporal length and re-assign redline for the sliderend resnapping later
                foreach (var ho in part.HitObjects)
                {
                    ho.UnInheritedTimingPoint = newTiming.GetRedlineAtTime(ho.Time);
                    if (ho.IsSlider)
                    {
                        // If scaleToNewTiming then the end time is already at the correct beat time
                        // The SV has to be adjusted so the sliderend is really on the end time
                        if (scaleToNewTiming)
                        {
                            var wantedMsDuration = (newTiming.GetMilliseconds(ho.GetEndTime(false), patternStartTime) -
                                                    newTiming.GetMilliseconds(ho.Time, patternStartTime)) / ho.Repeat;
                            var trueMsDuration = newTiming.CalculateSliderTemporalLength(SnapToNewTiming ? newTiming.ResnapBeatTime(ho.Time, BeatDivisors) : ho.Time, ho.PixelLength, ho.SliderVelocity);
                            ho.SliderVelocity /= trueMsDuration / wantedMsDuration;
                        }
                        else
                        {
                            ho.TemporalLength = newTiming.CalculateSliderTemporalLength(SnapToNewTiming ? newTiming.Resnap(ho.Time, BeatDivisors) : ho.Time, ho.PixelLength, ho.SliderVelocity);
                        }
                    }
                }

                // Update the end time because the lengths of sliders changed
                endTime      = part.HitObjects.Max(o => o.GetEndTime(!scaleToNewTiming));
                part.EndTime = endTime;

                // Add a redline at the end of the pattern to make sure the BPM goes back to normal after the pattern.
                var endOriginalRedline = transformOriginalTiming.GetRedlineAtTime(endTime);
                var endPartRedline     = inPartRedlines.LastOrDefault() ?? startPartRedline;
                if (Math.Abs(endPartRedline.MpB * bpmMultiplier - endOriginalRedline.MpB) > Precision.DOUBLE_EPSILON)
                {
                    // We dont have to add the redline again if its already during the parts in between parts.
                    if (Math.Abs(endOriginalRedline.Offset - endTime) > Precision.DOUBLE_EPSILON)
                    {
                        var copy = endOriginalRedline.Copy();
                        copy.Offset = endTime;
                        newTiming.Add(copy);
                    }
                }

                lastEndTime = endTime;
            }

            // Transform the beat time back to millisecond time
            Timing transformNewTiming = newTiming;

            if (scaleToNewTiming)
            {
                // Transform back the timing
                transformNewTiming = newTiming.Copy();
                foreach (var tp in transformNewTiming.TimingPoints)
                {
                    tp.Offset = Math.Floor(newTiming.GetMilliseconds(tp.Offset, patternStartTime) + Precision.DOUBLE_EPSILON);
                }

                // Transform back the parts
                foreach (Part part in parts)
                {
                    part.StartTime = Math.Floor(newTiming.GetMilliseconds(part.StartTime, patternStartTime));
                    part.EndTime   = Math.Floor(newTiming.GetMilliseconds(part.EndTime, patternStartTime));
                }

                // Transform everything to millisecond time relative to pattern start time
                foreach (var ho in patternBeatmap.HitObjects)
                {
                    // Calculate the millisecond end time before changing the start time because the end time getter uses the beat time start time
                    var msEndTime = newTiming.GetMilliseconds(ho.GetEndTime(false), patternStartTime);

                    ho.Time = newTiming.GetMilliseconds(ho.Time, patternStartTime);

                    // End time has to be set after the time because the end time setter uses the millisecond start time
                    ho.EndTime = msEndTime;

                    foreach (var tp in ho.BodyHitsounds)
                    {
                        tp.Offset = newTiming.GetMilliseconds(tp.Offset, patternStartTime);
                    }

                    // It is necessary to resnap early so it can recalculate the duration using the right offset
                    if (SnapToNewTiming)
                    {
                        ho.ResnapSelf(transformNewTiming, BeatDivisors);
                    }

                    if (ho.IsSlider)
                    {
                        ho.CalculateSliderTemporalLength(transformNewTiming, true);
                    }

                    ho.UnInheritedTimingPoint = transformNewTiming.GetRedlineAtTime(ho.Time);
                    ho.UpdateTimelineObjectTimes();
                }

                foreach (var tp in kiaiToggles.Concat(svChanges))
                {
                    tp.Offset = Math.Floor(newTiming.GetMilliseconds(tp.Offset, patternStartTime));
                }
            }

            // Apply custom scale and rotate
            if (Math.Abs(spatialScale - 1) > Precision.DOUBLE_EPSILON ||
                Math.Abs(CustomRotate) > Precision.DOUBLE_EPSILON)
            {
                // Create a transformation matrix for the custom scale and rotate
                // The rotation is inverted because the default osu! rotation goes clockwise
                Matrix2 transform = Matrix2.Mult(Matrix2.CreateScale(spatialScale), Matrix2.CreateRotation(-CustomRotate));
                Vector2 centre    = new Vector2(256, 192);
                foreach (var ho in patternBeatmap.HitObjects)
                {
                    ho.Move(-centre);
                    ho.Transform(transform);
                    ho.Move(centre);

                    // Scale pixel length and SV for sliders aswell
                    if (ho.IsSlider)
                    {
                        ho.PixelLength    *= spatialScale;
                        ho.SliderVelocity /= spatialScale;
                    }
                }

                // osu! clips coordinates to the bounds (0,512), so there is some space downwards to still place the pattern
                // Calculate the new bounds of the pattern and try to place it in the playfield
                var     minX   = patternBeatmap.HitObjects.Min(o => o.Pos.X);
                var     minY   = patternBeatmap.HitObjects.Min(o => o.Pos.Y);
                Vector2 offset = new Vector2(Math.Max(-minX, 0), Math.Max(-minY, 0));
                if (offset.LengthSquared > 0)
                {
                    foreach (var ho in patternBeatmap.HitObjects)
                    {
                        ho.Move(offset);
                    }
                }
            }

            // Manualify stacks
            if (FixStackLeniency)
            {
                // If scale to new timing was used update the circle size of the pattern,
                // so it calculates stacks at the new size of the pattern.
                if (ScaleToNewCircleSize)
                {
                    patternBeatmap.Difficulty["CircleSize"].DoubleValue = originalCircleSize;
                }

                patternBeatmap.CalculateEndPositions();
                patternBeatmap.UpdateStacking(rounded: true);

                // Manualify by setting the base position to the stacked position
                foreach (var ho in patternBeatmap.HitObjects)
                {
                    var offset = ho.StackedPos - ho.Pos;
                    ho.Move(offset);
                }
            }

            // Resnap everything to the new timing.
            if (SnapToNewTiming)
            {
                // Resnap all objects
                foreach (HitObject ho in patternBeatmap.HitObjects)
                {
                    ho.ResnapSelf(transformNewTiming, BeatDivisors);
                    ho.ResnapEnd(transformNewTiming, BeatDivisors);
                    ho.ResnapPosition(targetMode, patternCircleSize);  // Resnap to column X positions for mania only
                }
                // Resnap Kiai toggles
                foreach (TimingPoint tp in kiaiToggles)
                {
                    tp.ResnapSelf(transformNewTiming, BeatDivisors);
                }

                // Resnap SliderVelocity changes
                foreach (TimingPoint tp in svChanges)
                {
                    tp.ResnapSelf(transformNewTiming, BeatDivisors);
                }
            }

            // Multiply BPM and divide SV
            foreach (var part in parts)
            {
                foreach (var tp in transformNewTiming.GetRedlinesInRange(part.StartTime - 2 * Precision.DOUBLE_EPSILON, part.EndTime, false))
                {
                    tp.MpB /= bpmMultiplier;  // MpB is the inverse of the BPM
                }

                foreach (var ho in part.HitObjects)
                {
                    ho.SliderVelocity *= bpmMultiplier;  // SliderVelocity is the inverse of the multiplier
                }
            }

            // Make new timingpoints changes for the hitsounds and other stuff

            // Add redlines
            timingPointsChanges = transformNewTiming.Redlines.Select(tp =>
                                                                     new TimingPointsChange(tp, mpb: true, meter: true, unInherited: true, omitFirstBarLine: true, fuzzyness: Precision.DOUBLE_EPSILON)).ToList();

            // Add SliderVelocity changes for taiko and mania
            if (includePatternSliderVelocity && (targetMode == GameMode.Taiko || targetMode == GameMode.Mania))
            {
                timingPointsChanges.AddRange(svChanges.Select(tp => new TimingPointsChange(tp, mpb: true, fuzzyness: 0.4)));
            }

            // Add Kiai toggles
            timingPointsChanges.AddRange(kiaiToggles.Select(tp => new TimingPointsChange(tp, kiai: true)));

            // Add Hitobject stuff
            foreach (HitObject ho in patternBeatmap.HitObjects)
            {
                if (ho.IsSlider) // SliderVelocity changes
                {
                    TimingPoint tp = ho.TimingPoint.Copy();
                    tp.Offset = ho.Time;
                    tp.MpB    = ho.SliderVelocity;
                    timingPointsChanges.Add(new TimingPointsChange(tp, mpb: true, fuzzyness: 0.4));
                }

                if (!IncludeHitsounds)
                {
                    // Remove hitsounds and skip adding body hitsounds
                    ho.ResetHitsounds();
                    continue;
                }

                if (includeTimingPointHitsounds)
                {
                    // Body hitsounds
                    bool vol = ho.IsSlider || ho.IsSpinner;
                    bool sam = ho.IsSlider && ho.SampleSet == 0;
                    bool ind = ho.IsSlider;
                    timingPointsChanges.AddRange(ho.BodyHitsounds.Select(tp =>
                                                                         new TimingPointsChange(tp, volume: vol, index: ind, sampleset: sam)));
                }
            }

            // Add timeline hitsounds
            if (includeTimingPointHitsounds)
            {
                foreach (TimelineObject tlo in patternTimeline.TimelineObjects)
                {
                    if (tlo.HasHitsound)
                    {
                        // Add greenlines for hitsounds
                        TimingPoint tp = tlo.HitsoundTimingPoint.Copy();
                        tp.Offset = tlo.Time;
                        timingPointsChanges.Add(new TimingPointsChange(tp, sampleset: true, volume: true, index: true));
                    }
                }
            }

            // Replace the old timingpoints
            patternTiming.Clear();
            TimingPointsChange.ApplyChanges(patternTiming, timingPointsChanges);

            patternBeatmap.GiveObjectsGreenlines();
            patternBeatmap.CalculateSliderEndTimes();
        }
Example #7
0
    void UpdateTiming()
    {
        // find section index
        int newIndex  = sectionIndex_;
        int oldSample = currentSample_;

        currentSample_ = musicSource_.timeSamples;
        if (sectionIndex_ + 1 >= Sections.Count)
        {
            if (currentSample_ < oldSample)
            {
                newIndex = 0;
            }
        }
        else
        {
            if (Sections[sectionIndex_ + 1].StartTimeSamples <= currentSample_)
            {
                newIndex = sectionIndex_ + 1;
            }
        }

        if (newIndex != sectionIndex_)
        {
            sectionIndex_ = newIndex;
            OnSectionChanged();
        }

        // calc current timing
        isNearChanged_ = false;
        isJustChanged_ = false;
        int sectionSample = currentSample_ - CurrentSection_.StartTimeSamples;

        if (sectionSample >= 0)
        {
            just_.Bar  = (int)(sectionSample / samplesPerBar_) + CurrentSection_.StartTiming.Bar;
            just_.Beat = (int)((sectionSample % samplesPerBar_) / samplesPerBeat_) + CurrentSection_.StartTiming.Beat;
            just_.Unit = (int)(((sectionSample % samplesPerBar_) % samplesPerBeat_) / samplesPerUnit_) + CurrentSection_.StartTiming.Unit;
            just_.Fix(CurrentSection_);
            if (sectionIndex_ + 1 >= Sections.Count)
            {
                if (numLoopBar_ > 0)
                {
                    while (just_.Bar >= numLoopBar_)
                    {
                        just_.Decrement(CurrentSection_);
                    }
                }
            }
            else
            {
                while (just_ >= Sections[sectionIndex_ + 1].StartTiming)
                {
                    just_.Decrement(CurrentSection_);
                }
            }

            just_.Subtract(CurrentSection_.StartTiming, CurrentSection_);
            timeSecFromJust_ = (double)(sectionSample - just_.Bar * samplesPerBar_ - just_.Beat * samplesPerBeat_ - just_.Unit * samplesPerUnit_) / (double)samplingRate_;
            isFormerHalf_    = (timeSecFromJust_ * samplingRate_) < samplesPerUnit_ / 2;
            just_.Add(CurrentSection_.StartTiming, CurrentSection_);

            near_.Copy(just_);
            if (!isFormerHalf_)
            {
                near_.Increment(CurrentSection_);
            }
            if (samplesInLoop_ != 0 && currentSample_ + samplesPerUnit_ / 2 >= samplesInLoop_)
            {
                near_.Init();
            }

            isNearChanged_ = (near_.Equals(oldNear_) == false);
            isJustChanged_ = (just_.Equals(oldJust_) == false);

            CallEvents();

            oldNear_.Copy(near_);
            oldJust_.Copy(just_);
        }

        if (DebugText != null)
        {
            DebugText.text = "Just = " + Just.ToString() + ", MusicalTime = " + MusicalTime_;
            if (Sections.Count > 0)
            {
                DebugText.text += System.Environment.NewLine + "section[" + sectionIndex_ + "] = " + CurrentSection_.ToString();
            }
        }
    }
Example #8
0
    void UpdateTiming()
    {
        isNowChanged_  = false;
        isJustChanged_ = false;

        if (SectionIndex < 0 || sections.Count <= SectionIndex)
        {
            Debug.LogWarning("Music:" + name + " has invalid SectionIndex = " + SectionIndex + ", sections.Count = " + sections.Count);
            return;
        }
        long numSamples = MusicSource.GetTimeSamples();


        int NewIndex = -1;

        for (int i = SectionIndex; i < sections.Count; i++)
        {
            if (sections[i].StartTimeSamples_ <= numSamples && (sections.Count <= i + 1 || numSamples < sections[i + 1].StartTimeSamples_))
            {
                NewIndex = i;
                break;
            }
        }
        if (NewIndex < 0)
        {
            if (0 <= numSamples && numSamples < delayTimeSamples)
            {
                NewIndex = 0;
                Initialize();
                OnSectionChanged();
            }
            else
            {
                for (int i = 0; i < SectionIndex; i++)
                {
                    if (sections[i].StartTimeSamples_ <= numSamples && numSamples < sections[i + 1].StartTimeSamples_)
                    {
                        NewIndex = i;
                    }
                }
            }
        }

        if (NewIndex != SectionIndex)
        {
            SectionIndex = NewIndex;
            OnSectionChanged();
        }

        numSamples -= CurrentSection_.StartTimeSamples_;
        if (numSamples >= 0)
        {
            Just_.bar  = (int)(numSamples / SamplesPerBar) + CurrentSection_.StartTiming_.bar;
            Just_.beat = (int)((numSamples % SamplesPerBar) / SamplesPerBeat) + CurrentSection_.StartTiming_.beat;
            Just_.unit = (int)(((numSamples % SamplesPerBar) % SamplesPerBeat) / SamplesPerUnit) + CurrentSection_.StartTiming_.unit;
            if (Just_.unit >= CurrentSection_.mtBeat_)
            {
                Just_.beat += (int)(Just_.unit / CurrentSection_.mtBeat_);
                Just_.unit %= CurrentSection_.mtBeat_;
            }
            int barUnit = Just_.beat * CurrentSection_.mtBeat_ + Just_.unit;
            if (barUnit >= CurrentSection_.mtBar_)
            {
                Just_.bar += (int)(barUnit / CurrentSection_.mtBar_);
                Just_.beat = 0;
                Just_.unit = (barUnit % CurrentSection_.mtBar_);
                if (Just_.unit >= CurrentSection_.mtBeat_)
                {
                    Just_.beat += (int)(Just_.unit / CurrentSection_.mtBeat_);
                    Just_.unit %= CurrentSection_.mtBeat_;
                }
            }
            if (NumLoopBar > 0)
            {
                Just_.bar %= NumLoopBar;
            }

            isFormerHalf_ = (numSamples % SamplesPerUnit) < SamplesPerUnit / 2;
            dtFromJust_   = (double)(numSamples % SamplesPerUnit) / (double)SamplingRate;

            Now_.Copy(Just_);
            if (!isFormerHalf_)
            {
                Now_.Increment();
            }
            if (SamplesInLoop != 0 && numSamples + SamplesPerUnit / 2 >= SamplesInLoop)
            {
                Now_.Init();
            }

            isNowChanged_  = Now_.totalUnit != OldNow.totalUnit;
            isJustChanged_ = Just_.totalUnit != OldJust.totalUnit;

            CallEvents();

            OldNow.Copy(Now_);
            OldJust.Copy(Just_);
        }
        else
        {
            //Debug.LogWarning( "Warning!! Failed to GetNumPlayedSamples" );
        }

        DebugUpdateText();
    }