private void DrawIssue(SongIssue issue)
    {
        if (dynamicTexture == null)
        {
            return;
        }

        Color color = SongIssueUtils.GetColorForIssue(issue);

        int songDurationInMillis = (int)Math.Ceiling(songAudioPlayer.AudioClip.length * 1000);

        int startMillis = (int)BpmUtils.BeatToMillisecondsInSong(songMeta, issue.StartBeat);
        int endMillis   = (int)BpmUtils.BeatToMillisecondsInSong(songMeta, issue.EndBeat);

        // Use a minimum width of 0.5% such that the issue is not overlooked
        int    lengthInMillis  = endMillis - startMillis;
        double lengthInPercent = (double)lengthInMillis / songDurationInMillis;

        if (lengthInPercent < 0.005)
        {
            endMillis = (int)(startMillis + songDurationInMillis * 0.005);
        }

        int xStart = (int)(dynamicTexture.TextureWidth * startMillis / songDurationInMillis);
        int xEnd   = (int)(dynamicTexture.TextureWidth * endMillis / songDurationInMillis);

        if (xEnd < xStart)
        {
            ObjectUtils.Swap(ref xStart, ref xEnd);
        }

        dynamicTexture.DrawRectByCorners(xStart, 0, xEnd, (int)(dynamicTexture.TextureHeight * 0.5f), color);
    }
    private void DrawNotes(Voice voice, Color color)
    {
        if (dynamicTexture == null)
        {
            return;
        }

        List <Note> notes = voice.Sentences.SelectMany(sentence => sentence.Notes).ToList();

        if (notes.IsNullOrEmpty())
        {
            return;
        }

        int songDurationInMillis = (int)Math.Ceiling(songAudioPlayer.AudioClip.length * 1000);

        // constant offset to
        // (a) ensure that midiNoteRange > 0,
        // (b) have some space to the border of the texture.
        int minMaxOffset  = 1;
        int midiNoteMin   = notes.Select(note => note.MidiNote).Min() - minMaxOffset;
        int midiNoteMax   = notes.Select(note => note.MidiNote).Max() + minMaxOffset;
        int midiNoteRange = midiNoteMax - midiNoteMin;

        foreach (Note note in notes)
        {
            double startMillis = BpmUtils.BeatToMillisecondsInSong(songMeta, note.StartBeat);
            double endMillis   = BpmUtils.BeatToMillisecondsInSong(songMeta, note.EndBeat);

            int yStart  = dynamicTexture.TextureHeight * (note.MidiNote - midiNoteMin) / midiNoteRange;
            int yLength = dynamicTexture.TextureHeight / midiNoteRange * 2;
            int yEnd    = yStart + yLength;
            int xStart  = (int)(dynamicTexture.TextureWidth * startMillis / songDurationInMillis);
            int xEnd    = (int)(dynamicTexture.TextureWidth * endMillis / songDurationInMillis);
            if (xEnd < xStart)
            {
                ObjectUtils.Swap(ref xStart, ref xEnd);
            }

            xEnd = xEnd < dynamicTexture.TextureWidth
                ? xEnd
                : dynamicTexture.TextureWidth - 1;
            yEnd = yEnd < dynamicTexture.TextureHeight
                ? yEnd
                : dynamicTexture.TextureHeight - 1;
            dynamicTexture.DrawRectByCorners(xStart, yStart, xEnd, yEnd, color);
        }
    }