/// <summary> /// Update <see cref="ChartRenderer"/> instance to process pending rendering queue. /// </summary> /// <param name="delta">The number of elapsed milliseconds between current and last <see cref="Update(double)"/> call.</param> public void Update(double delta) { SoundSystem.Instance.Update(); if (!IsRendering || IsPaused) { return; } // TODO: CONFIRM // In case rendering start off-sync when chart has so many bpm changes // Try to split the events between Time and Sound events and process them separately foreach (var ev in Chart.Events) { double offset = Offset; Measure = (int)(offset / 192f); Beat = (int)((offset % 192f) / (192f / 4f)); Cell = (int)(offset % 192f); Elapsed = TimeSpan.FromSeconds(timeOffset + (((offset - bpmOffset) / (192f / 4f)) / BPM) * 60); Elapsed = Elapsed > Duration ? Duration : Elapsed; double latency = ev.Offset - offset; // Skip event if (ev.Judged || latency > 192 / 4) { continue; } // Preload sample if (ev is Event.Sound sample && sample.Payload != null) { Task.Run(() => sample.Preload()); continue; } if (ev is Event.Time time) { if (latency <= 0) { if (time.Channel == Event.ChannelType.BPM) { // Update bpm offset footprint startTick += (time.Offset - bpmOffset) / BPM * TICK_SIGNATURE; timeOffset += (((offset - bpmOffset) / (192f / 4f)) / BPM) * 60; // Update bpm information BPM = time.Value; bpmOffset = time.Offset; // Apply current offset with current bpm offset = Offset; Elapsed = TimeSpan.FromSeconds(timeOffset + (((offset - bpmOffset) / (192f / 4f)) / BPM) * 60); // Judge the event ev.Judge(); } } } else if (ev is Event.Sound sound) { if (latency <= 0 && !ev.Judged) { if (sound.Signature != Event.SignatureType.Release) { sound.Play(); } ev.Judge(); } } } // Invoke callback Rendering?.Invoke(this, EventArgs.Empty); // TODO: Do we need measure count to determine end of song to simulate o2jam gameplay? if (IsRendering && Elapsed >= Chart.Duration && Beat == 0 && SoundSystem.Instance.GetPlayingSources().Length == 0) { IsRendering = false; RenderComplete?.Invoke(this, EventArgs.Empty); } }
private void OnRenderComplete(ApplicationContext applicationContext, EventArgs e) { RenderComplete?.Invoke(this, e); }