protected override void OnRenderFrame(FrameEventArgs e) { Task.Factory.StartNew(() => PlaybackLoop(), TaskCreationOptions.LongRunning); SpinWait.SpinUntil(() => playbackLoopStarted); Stopwatch watch = new Stopwatch(); watch.Start(); if (!settings.timeBasedNotes) { tempoFrameStep = ((double)midi.division / lastTempo) * (1000000.0 / settings.fps); } lock (render) { 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)((double)lastTempo / midi.division * 10); } 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) { try { if (render.disposeQueue.Count != 0) { try { while (true) { var r = render.disposeQueue.Dequeue(); if (r.Initialized) { try { r.Dispose(); } catch { } GC.Collect(); } } } catch (InvalidOperationException) { } } 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; lock (globalDisplayNotes) { foreach (Note n in globalDisplayNotes) { n.meta = null; } } } 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); 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.First != null && midiTime + (tempoFrameStep * mv * settings.tempoMultiplier) > globalTempoEvents.First.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; 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) { for (int i = 0; i < 16; i++) { c.track.trkColors[i].left = c.col1; c.track.trkColors[i].right = c.col2; c.track.trkColors[i].isDefault = false; } } 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, settings.width / settings.downscale, settings.height / settings.downscale); if (bgTexID != -1) { GL.UseProgram(postShaderFlip); GL.BindTexture(TextureTarget.Texture2D, bgTexID); DrawScreenQuad(); } if (settings.downscale > 1) { GL.UseProgram(postShaderDownscale); GL.Uniform1(uDownscaleFac, (int)settings.downscale); GL.Uniform2(uDownscaleRes, new Vector2(settings.width / settings.downscale, settings.height / settings.downscale)); } 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, settings.width / settings.downscale, settings.height / settings.downscale); downscaleBuff.BindTexture(); DrawScreenQuad(); IntPtr unmanagedPointer = Marshal.AllocHGlobal(pixels.Length); GL.ReadPixels(0, 0, settings.width / settings.downscale, settings.height / settings.downscale, PixelFormat.Bgra, PixelType.UnsignedByte, unmanagedPointer); Marshal.Copy(unmanagedPointer, pixels, 0, pixels.Length); if (lastRenderPush != null) { lastRenderPush.GetAwaiter().GetResult(); } lastRenderPush = Task.Run(() => { ffmpegvideo.StandardInput.BaseStream.Write(pixels, 0, pixels.Length); }); 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, settings.width / settings.downscale, settings.height / settings.downscale); downscaleBuff.BindTexture(); DrawScreenQuad(); unmanagedPointer = Marshal.AllocHGlobal(pixelsmask.Length); GL.ReadPixels(0, 0, settings.width / settings.downscale, settings.height / settings.downscale, 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(postShaderBlackFill); DrawScreenQuad(); GL.UseProgram(postShader); 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 = null; Console.WriteLine("Closing window"); this.Close(); }
public RenderWindow(CurrentRendererPointer renderer, MidiFile midi, RenderSettings settings) : base(16, 9, new GraphicsMode(new ColorFormat(8, 8, 8, 8)), "Render", GameWindowFlags.Default, DisplayDevice.Default) { Width = (int)(DisplayDevice.Default.Width / 1.5); Height = (int)((double)Width / settings.width * settings.height); Location = new Point((DisplayDevice.Default.Width - Width) / 2, (DisplayDevice.Default.Height - Height) / 2); //textEngine = new GLTextEngine(); render = renderer; this.settings = settings; lastTempo = midi.zerothTempo; lock (render) { render.renderer.Tempo = 60000000.0 / midi.zerothTempo; midiTime = -render.renderer.NoteScreenTime; if (settings.timeBasedNotes) { tempoFrameStep = 1000.0 / settings.fps; } else { tempoFrameStep = ((double)midi.division / lastTempo) * (1000000 / settings.fps); } midiTime -= tempoFrameStep * settings.renderSecondsDelay * settings.fps; } globalDisplayNotes = midi.globalDisplayNotes; globalTempoEvents = midi.globalTempoEvents; globalColorEvents = midi.globalColorEvents; globalPlaybackEvents = midi.globalPlaybackEvents; this.midi = midi; if (settings.ffRender) { pixels = new byte[settings.width * settings.height * 4 / settings.downscale / settings.downscale]; ffmpegvideo = startNewFF(settings.ffPath); if (settings.ffRenderMask) { pixelsmask = new byte[settings.width * settings.height * 4 / settings.downscale / settings.downscale]; ffmpegmask = startNewFF(settings.ffMaskPath); } } finalCompositeBuff = new GLPostbuffer(settings.width, settings.height); ffmpegOutputBuff = new GLPostbuffer(settings.width / settings.downscale, settings.height / settings.downscale); downscaleBuff = new GLPostbuffer(settings.width / settings.downscale, settings.height / settings.downscale); GL.GenBuffers(1, out screenQuadBuffer); GL.GenBuffers(1, out screenQuadIndexBuffer); GL.BindBuffer(BufferTarget.ArrayBuffer, screenQuadBuffer); GL.BufferData( BufferTarget.ArrayBuffer, (IntPtr)(screenQuadArray.Length * 8), screenQuadArray, BufferUsageHint.StaticDraw); GL.BindBuffer(BufferTarget.ElementArrayBuffer, screenQuadIndexBuffer); GL.BufferData( BufferTarget.ElementArrayBuffer, (IntPtr)(screenQuadArrayIndex.Length * 4), screenQuadArrayIndex, BufferUsageHint.StaticDraw); postShader = MakeShader(postShaderVert, postShaderFrag); postShaderFlip = MakeShader(postShaderFlipVert, postShaderFrag); postShaderMask = MakeShader(postShaderVert, postShaderFragAlphaMask); postShaderMaskColor = MakeShader(postShaderVert, postShaderFragAlphaMaskColor); postShaderDownscale = MakeShader(postShaderVert, postShaderFragDownscale); postShaderBlackFill = MakeShader(postShaderVert, postShaderFragBlackFill); uDownscaleRes = GL.GetUniformLocation(postShaderDownscale, "res"); uDownscaleFac = GL.GetUniformLocation(postShaderDownscale, "factor"); }