protected override void OnRenderFrame(FrameEventArgs e) { Thread.CurrentThread.Priority = ThreadPriority.Highest; settings.renderedFrames = 0; Task.Factory.StartNew(() => PlaybackLoop(), TaskCreationOptions.LongRunning); SpinWait.SpinUntil(() => playbackLoopStarted); Stopwatch watch = new Stopwatch(); watch.Start(); if (!settings.timeBasedNotes) { tempoFrameStep = (midi.division / lastTempo) * (1000000.0 / settings.fps); } lock (render.renderer) { lastDeltaTimeOnScreen = render.renderer.NoteScreenTime; render.renderer.CurrentMidi = midi.info; } int noNoteFrames = 0; long lastNC = 0; bool firstRenderer = true; frameStartTime = DateTime.Now.Ticks; if (settings.timeBasedNotes) { microsecondsPerTick = 10000; } else { microsecondsPerTick = (long)(lastTempo / midi.division * 10); } // variables var downscaleWidth = settings.width / settings.downscale; var downscaleHeight = settings.height / settings.downscale; var Vector = new Vector2(downscaleWidth, downscaleHeight); int pixelsLength = 0; Stream ffStream = null; IntPtr unmanagedPointer; if (settings.ffRender) { pixelsLength = pixels.Length; ffStream = ffmpegvideo.StandardInput.BaseStream; } lock (globalDisplayNotes) { foreach (Note n in globalDisplayNotes) { n.meta = null; } } while (settings.running && (noNoteFrames < settings.fps * 5 || midi.unendedTracks != 0)) { if (!settings.Paused || settings.forceReRender) { if (settings.lastBGChangeTime != lastBGChangeTime) { if (settings.BGImage == null) { if (bgTexID != -1) { GL.DeleteTexture(bgTexID); } bgTexID = -1; } else { if (bgTexID == -1) { bgTexID = GL.GenTexture(); } try { loadImage(new Bitmap(settings.BGImage), bgTexID, false, true); } catch { MessageBox.Show("Couldn't load image"); if (bgTexID != -1) { GL.DeleteTexture(bgTexID); } bgTexID = -1; } } lastBGChangeTime = settings.lastBGChangeTime; } lock (render.disposeQueue) { try { if (render.disposeQueue.Count != 0) { try { while (render.disposeQueue.Count != 0) { var r = render.disposeQueue.Dequeue(); if (r.Initialized) { try { r.Dispose(); } catch { } //GC.Collect(); } } } catch (InvalidOperationException) { } } } catch { MessageBox.Show("There's a problem while trying to dispose other renderers", "Error"); } } lock (render.renderer) { try { if (!render.renderer.Initialized) { render.renderer.Init(); render.renderer.NoteColors = midi.tracks.Select(t => t.trkColors).ToArray(); render.renderer.ReloadTrackColors(); if (firstRenderer) { firstRenderer = false; midi.SetZeroColors(); } render.renderer.CurrentMidi = midi.info; } render.renderer.Tempo = 60000000.0 / lastTempo; lastDeltaTimeOnScreen = render.renderer.NoteScreenTime; //if (settings.timeBasedNotes) // SpinWait.SpinUntil(() => (midi.currentFlexSyncTime > midiTime + lastDeltaTimeOnScreen + tempoFrameStep * settings.tempoMultiplier || midi.unendedTracks == 0) || !settings.running); //else // SpinWait.SpinUntil(() => (midi.currentSyncTime > midiTime + lastDeltaTimeOnScreen + tempoFrameStep * settings.tempoMultiplier || midi.unendedTracks == 0) || !settings.running); //if (!settings.running) break; render.renderer.RenderFrame(globalDisplayNotes, midiTime, finalCompositeBuff.BufferID); settings.renderedFrames++; lastNC = render.renderer.LastNoteCount; if (lastNC == 0 && midi.unendedTracks == 0) { noNoteFrames++; } else { noNoteFrames = 0; } } catch (Exception ex) { MessageBox.Show("The renderer has crashed\n" + ex.Message + "\n" + ex.StackTrace); break; } } } double mv = 1; if (settings.realtimePlayback) { mv = (DateTime.Now.Ticks - frameStartTime) / microsecondsPerTick / tempoFrameStep; if (mv > settings.fps / 4) { mv = settings.fps / 4; } } lastMV = mv; if (!settings.Paused) { lock (globalTempoEvents) { while (globalTempoEvents.GetFirst() != null && midiTime + (tempoFrameStep * mv * settings.tempoMultiplier) > globalTempoEvents.GetFirst().pos) { var t = globalTempoEvents.Pop(); if (t.tempo == 0) { Console.WriteLine("Zero tempo event encountered, ignoring"); continue; } // var _t = ((t.pos) - midiTime) / (tempoFrameStep * mv * settings.tempoMultiplier); // mv *= 1 - _t; mv *= 1 - ((t.pos) - midiTime) / (tempoFrameStep * mv * settings.tempoMultiplier); if (!settings.timeBasedNotes) { tempoFrameStep = ((double)midi.division / t.tempo) * (1000000.0 / settings.fps); } lastTempo = t.tempo; midiTime = t.pos; } } midiTime += mv * tempoFrameStep * settings.tempoMultiplier; } frameStartTime = DateTime.Now.Ticks; if (settings.timeBasedNotes) { microsecondsPerTick = 10000; } else { microsecondsPerTick = (long)(lastTempo / midi.division * 10); } while (globalColorEvents.First != null && globalColorEvents.First.pos < midiTime) { var c = globalColorEvents.Pop(); var track = c.track; if (!settings.ignoreColorEvents) { if (c.channel == 0x7F) { #region Loop int i = 0; c.track.trkColors[i].left = c.col1; c.track.trkColors[i].right = c.col2; c.track.trkColors[i].isDefault = false; ++i; c.track.trkColors[i].left = c.col1; c.track.trkColors[i].right = c.col2; c.track.trkColors[i].isDefault = false; ++i; c.track.trkColors[i].left = c.col1; c.track.trkColors[i].right = c.col2; c.track.trkColors[i].isDefault = false; ++i; c.track.trkColors[i].left = c.col1; c.track.trkColors[i].right = c.col2; c.track.trkColors[i].isDefault = false; ++i; c.track.trkColors[i].left = c.col1; c.track.trkColors[i].right = c.col2; c.track.trkColors[i].isDefault = false; ++i; c.track.trkColors[i].left = c.col1; c.track.trkColors[i].right = c.col2; c.track.trkColors[i].isDefault = false; ++i; c.track.trkColors[i].left = c.col1; c.track.trkColors[i].right = c.col2; c.track.trkColors[i].isDefault = false; ++i; c.track.trkColors[i].left = c.col1; c.track.trkColors[i].right = c.col2; c.track.trkColors[i].isDefault = false; ++i; c.track.trkColors[i].left = c.col1; c.track.trkColors[i].right = c.col2; c.track.trkColors[i].isDefault = false; ++i; c.track.trkColors[i].left = c.col1; c.track.trkColors[i].right = c.col2; c.track.trkColors[i].isDefault = false; ++i; c.track.trkColors[i].left = c.col1; c.track.trkColors[i].right = c.col2; c.track.trkColors[i].isDefault = false; ++i; c.track.trkColors[i].left = c.col1; c.track.trkColors[i].right = c.col2; c.track.trkColors[i].isDefault = false; ++i; c.track.trkColors[i].left = c.col1; c.track.trkColors[i].right = c.col2; c.track.trkColors[i].isDefault = false; ++i; c.track.trkColors[i].left = c.col1; c.track.trkColors[i].right = c.col2; c.track.trkColors[i].isDefault = false; ++i; c.track.trkColors[i].left = c.col1; c.track.trkColors[i].right = c.col2; c.track.trkColors[i].isDefault = false; ++i; c.track.trkColors[i].left = c.col1; c.track.trkColors[i].right = c.col2; c.track.trkColors[i].isDefault = false; #endregion } } else { c.track.trkColors[c.channel].left = c.col1; c.track.trkColors[c.channel].right = c.col2; c.track.trkColors[c.channel].isDefault = false; } } downscaleBuff.BindBuffer(); GL.Clear(ClearBufferMask.ColorBufferBit); GL.Viewport(0, 0, downscaleWidth, downscaleHeight); if (bgTexID != -1) { GL.UseProgram(postShaderFlip); GL.BindTexture(TextureTarget.Texture2D, bgTexID); DrawScreenQuad(); } if (settings.downscale > 1) { GL.UseProgram(postShaderDownscale); GL.Uniform1(uDownscaleFac, settings.downscale); GL.Uniform2(uDownscaleRes, Vector); } else { GL.UseProgram(postShader); } finalCompositeBuff.BindTexture(); DrawScreenQuad(); if (settings.ffRender) { if (ffmpegvideo.HasExited || (settings.ffRenderMask && ffmpegmask.HasExited)) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("FFMPEG process closed unexpectedly!"); Console.WriteLine("Use 'ffmpeg debug' for more advanced info."); Console.ResetColor(); settings.running = false; } //if (!settings.ffRenderMask) // GL.UseProgram(postShader); //else // GL.UseProgram(postShaderMaskColor); // finalCompositeBuff.BindTexture(); // ffmpegOutputBuff.BindBuffer(); // GL.Clear(ClearBufferMask.ColorBufferBit); // GL.Viewport(0, 0, downscaleWidth, downscaleHeight); // downscaleBuff.BindTexture(); // DrawScreenQuad(); unmanagedPointer = Marshal.AllocHGlobal(pixelsLength); GL.ReadPixels(0, 0, downscaleWidth, downscaleHeight, PixelFormat.Bgra, PixelType.UnsignedByte, unmanagedPointer); Marshal.Copy(unmanagedPointer, pixels, 0, pixelsLength); if (lastRenderPush != null) { lastRenderPush.GetAwaiter().GetResult(); } lastRenderPush = Task.Run(() => { ffStream.Write(pixels, 0, pixelsLength); }); Marshal.FreeHGlobal(unmanagedPointer); if (settings.ffRenderMask) { if (lastRenderPushMask != null) { lastRenderPushMask.GetAwaiter().GetResult(); } GL.UseProgram(postShaderMask); ffmpegOutputBuff.BindBuffer(); GL.Clear(ClearBufferMask.ColorBufferBit); GL.Viewport(0, 0, downscaleWidth, downscaleHeight); downscaleBuff.BindTexture(); DrawScreenQuad(); unmanagedPointer = Marshal.AllocHGlobal(pixelsmask.Length); GL.ReadPixels(0, 0, downscaleWidth, downscaleHeight, PixelFormat.Bgra, PixelType.UnsignedByte, unmanagedPointer); Marshal.Copy(unmanagedPointer, pixelsmask, 0, pixelsmask.Length); if (lastRenderPush != null) { lastRenderPush.GetAwaiter().GetResult(); } lastRenderPush = Task.Run(() => { ffmpegmask.StandardInput.BaseStream.Write(pixelsmask, 0, pixelsmask.Length); }); Marshal.FreeHGlobal(unmanagedPointer); } } //GLPostbuffer.UnbindBuffers(); //GL.Clear(ClearBufferMask.ColorBufferBit); //GL.UseProgram(postShader); //DrawScreenQuad(); //GL.Viewport(0, 0, Width, Height); //downscaleBuff.BindTexture(); //DrawScreenQuad(); //GLPostbuffer.UnbindTextures(); GL.UseProgram(postShader); GLPostbuffer.UnbindBuffers(); GL.Clear(ClearBufferMask.ColorBufferBit); GL.Viewport(0, 0, Width, Height); downscaleBuff.BindTexture(); DrawScreenQuad(); GLPostbuffer.UnbindTextures(); if (settings.ffRender) { VSync = VSyncMode.Off; } else if (settings.vsync) { VSync = VSyncMode.On; } else { VSync = VSyncMode.Off; } try { SwapBuffers(); } catch { break; } ProcessEvents(); double fr = 10000000.0 / watch.ElapsedTicks; settings.liveFps = (settings.liveFps * 2 + fr) / 3; watch.Reset(); watch.Start(); } Console.WriteLine("Left render loop"); settings.running = false; if (settings.ffRender) { if (lastRenderPush != null) { lastRenderPush.GetAwaiter().GetResult(); } ffmpegvideo.StandardInput.Close(); ffmpegvideo.Close(); if (settings.ffRenderMask) { if (lastRenderPushMask != null) { lastRenderPushMask.GetAwaiter().GetResult(); } ffmpegmask.StandardInput.Close(); ffmpegmask.Close(); } } Console.WriteLine("Disposing current renderer"); try { render.renderer.Dispose(); } catch { } try { Console.WriteLine("Disposing of other renderers"); while (render.disposeQueue.Count != 0) { var r = render.disposeQueue.Dequeue(); try { if (r.Initialized) { r.Dispose(); } } catch { } } } catch (InvalidOperationException) { } Console.WriteLine("Disposed of renderers"); globalDisplayNotes = null; globalTempoEvents = null; globalColorEvents = null; pixels = null; pixelsmask = null; if (settings.ffRender) { ffmpegvideo.Dispose(); if (settings.ffRenderMask) { ffmpegmask.Dispose(); } } ffmpegvideo = null; ffmpegmask = null; finalCompositeBuff.Dispose(); ffmpegOutputBuff.Dispose(); downscaleBuff.Dispose(); finalCompositeBuff = null; ffmpegOutputBuff = null; downscaleBuff = null; GL.DeleteBuffers(2, new int[] { screenQuadBuffer, screenQuadIndexBuffer }); GL.DeleteProgram(postShader); GL.DeleteProgram(postShaderMask); GL.DeleteProgram(postShaderMaskColor); GL.DeleteProgram(postShaderDownscale); midi = null; render.renderer = null; render.disposeQueue = null; Console.WriteLine("Closing window"); this.Close(); }