skip to Main Content

I understand that Winforms is not designed for heavy graphics usage but I don’t know anything other than C# so over the years I’ve created various programs using C# and Visual Studio that push things to the edge (in my opinion) of what can be done effectively with the limitations of Visual Studio / Winforms.

Example view of the program in question

This program reads Rock Band formatted files and plays them back like a music player, with the added feature that it makes use of the additional information in the Rock Band file like the charts, album art, and lyrics to complete the experience. Everything works pretty well. The drawing is done on graphics, all of it, no images are loaded and moved around (which I found is slower).

Every instance of graphics is accompanied by the following code I found here:

graphics.SmoothingMode = SmoothingMode.HighSpeed;
graphics.CompositingQuality = CompositingQuality.HighSpeed;

Which I was led to believe would help with performance.

All the drawing happens based on a timer which runs at 60Hz (16ms) so 60 times per second it draws everything on screen. This works for everything except the lyrics. I can have the lyrics in static mode or karaoke mode no problem. But if I set them to scroll with the vocal notes (in the image above, the blue notes at the bottom, the lyrics should snap to those notes and scroll along with them) it becomes impossible to read them unless you pause, then it’s very legible. I’ve tried playing with the timer’s refresh rate from as little as 1ms to 500ms and the behavior doesn’t change. Illegible scrolling lyrics.

This is the relevant code for the scrolling lyrics:

private void DrawLyricsScrolling(IEnumerable<Lyric> lyrics, Control label, Color color, Graphics graphics)
{
        if (!openSideWindow.Checked || PlayingSong == null || btnPlayPause.Tag.ToString() == "play" || !doScrollingLyrics) 
            return;

        var time = GetCorrectedTime();
        label.Text = "";

        graphics.SmoothingMode = SmoothingMode.HighSpeed;
        graphics.CompositingQuality = CompositingQuality.HighSpeed;

        using (var pen = new SolidBrush(MediaPlayer.Visible ? picVisuals.BackColor : LabelBackgroundColor))
        {
            graphics.FillRectangle(pen, label.ClientRectangle);
        }

        foreach (var lyric in lyrics.TakeWhile(lyric => lyric.LyricStart <= time + PlaybackWindow).Where(lyric => !(lyric.LyricStart + (PlaybackWindow * 2) < time)))
        {
            var left = (int)(((lyric.LyricStart - time) / PlaybackWindow) * label.Width);
            TextRenderer.DrawText(graphics,lyric.LyricText,label.Font,new Point(left, 0),color);
        }
}

I’m hoping you can point me in the direction of what I may be doing wrong or what I may be missing altogether to improve performance, if it is at all possible, within the confines of Winforms. I have over 6,000 lines of unique code in this program so I’m not looking to change to WPF or another language or environment. Either it can be improved in C# / Winforms or it’s staying this way.

Thanks!

2

Answers


  1. You can try maybe to use EmguCV which is C# .net wrapper for OpenCV (machine vision library written in C++) which has, among other things, built in functions for drawing text/shapes on images.
    The libraries from NuGet are: Emgu.CV and Emgu.CV.runtime.windows
    If this is relevant to you, drop a comment, and I will add few more pointers on the subject.

    Login or Signup to reply.
  2. There are something that we can see here

    foreach (var lyric in lyrics.TakeWhile(lyric => lyric.LyricStart <= time + PlaybackWindow).Where(lyric => !(lyric.LyricStart + (PlaybackWindow * 2) < time)))
    {
        var left = (int)(((lyric.LyricStart - time) / PlaybackWindow) * label.Width);
        TextRenderer.DrawText(graphics,lyric.LyricText,label.Font,new Point(left, 0),color);
    }
    

    From this code and the image you are showing, what I understand is that you are:

    1/ Looping over the lyrics, word by word, taking all the items until the current time window

    2/ Then you remove the ones that are too early / should have scrolled into non-view

    3/ Then you render those words, one by one

    Neither the LinQ nor the word by word rendering was efficient, and it is especially inefficient to do so 60 times a second. My suggestion: Render the whole (or a few minutes worh of) lyric in one go and "slide" the whole rendered text sentence instead

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search