Example #1
0
        /// <summary>
        /// Run when the selected instrument in the comboBox is changed
        /// Updates the dgvExercises with exercises that are for the instruemnt that the user selected
        /// </summary>
        private void cmbInstuments_SelectedIndexChanged(object sender, EventArgs e)
        {
            databaseInterface databaseInterface = new databaseInterface(dbConnectionString);    //The database class that will allow me to interact with the database
            SqlCommand        sqlCommandToRun   = new SqlCommand();                             //The Sql Statement that will be used to populate dgvExercises


            //If the "All" option is selected, then set the statement to one that will select all of the exercises, else, just select the ones with the instrument there
            if (cmbInstuments.SelectedItem.ToString() == "All")
            {
                sqlCommandToRun.CommandText = "SELECT * FROM Exercises WHERE FileExists = 1 AND Exercises.Instrument IN(SELECT Instrument FROM Notes)";         //Sql Command that will select all the exercises that use instruments for which there are not definitions
            }
            else
            {
                sqlCommandToRun.CommandText = "SELECT * FROM Exercises WHERE FileExists = 1 AND Exercises.Instrument IN(SELECT Instrument FROM Notes) AND Instrument = @instrument"; //Sql Commmand that will select all the exercises that use the right instrument
                sqlCommandToRun.Parameters.AddWithValue("@instrument", cmbInstuments.SelectedItem.ToString());                                                                       //If the selected item is "All", then set the parameter to *, else set it to the inputted string
            }

            dgvExercises.DataSource = databaseInterface.executeQuery(sqlCommandToRun);      //Assign the results of the SQL query to dgvExercises
        }
Example #2
0
        static midiFile loadedFile;        //The object that will store the loaded MIDI file


        #region Form Event Triggered Subs

        /// <summary>
        /// Run when form loads.
        /// Checks all the files are there
        /// Gets a list of instruments and adds it to the comboBox
        /// Populates dgvExercises with data from the database
        /// </summary>
        private void frmMainMenu_Load(object sender, EventArgs e)
        {
            #region Varibles
            databaseInterface databaseInterface = new databaseInterface(dbConnectionString);        //The database class that will allow me to interact with the database easily
            SqlCommand        currentCommand;
            #endregion

            #region @@ TEMP LOADING OF IMAGIES INTO DATABSE TESTING



//This bit of code just allows me to throw the image data for the fingering into the database nice and easy
#if false
            /*fileOpener = File.Open(@"C:\Users\Thomas\source\repos\A-level Coursework\Desktop Programs\Coursework Project\Coursework Project\Resources\E5.png", FileMode.Open);
             * fileOpener.CopyTo(imageStream);
             */

            MemoryStream imageStream = new MemoryStream();
            string[]     fileNames   = { "D5", "E5", "FS5", "G5", "A5", "B5", "CS5", "D6", "E6", "FS6", "G6", "A6", "B6", "CS6" };
            byte[]       noteNumbers = { 74, 76, 78, 79, 81, 83, 85, 86, 88, 90, 91, 93, 95, 97 };
            FileStream   fileOpener;


            for (int i = 0; i < noteNumbers.Length; i++)
            {
                imageStream = new MemoryStream();
                fileOpener  = File.Open($@"C:\Users\Thomas\source\repos\A-level Coursework\Desktop Programs\Coursework Project\Coursework Project\Resources\{fileNames[i]}.png", FileMode.Open);
                fileOpener.CopyTo(imageStream);


                //currentCommand = new SqlCommand("INSERT INTO Notes(FingeringDrawing) VALUES @inputImage ");
                currentCommand = new SqlCommand("UPDATE Notes SET FingeringDrawing = @inputImage WHERE Note = @noteNumber");
                currentCommand.Parameters.AddWithValue("@inputImage", imageStream.ToArray());
                currentCommand.Parameters.AddWithValue("@noteNumber", noteNumbers[i]);

                databaseInterface.executeNonQuery(currentCommand);
            }
#endif

#if false
            MemoryStream memStream       = new MemoryStream();
            DataTable    outputDataTable = new DataTable();
            currentCommand = new SqlCommand("SELECT FingeringDrawing FROM Notes");

            outputDataTable = databaseInterface.executeQuery(currentCommand);

            for (int i = 0; i < 14; i++)
            {
                memStream.Write(outputDataTable.Rows[i].Field <byte[]>("FingeringDrawing"), 0, outputDataTable.Rows[i].Field <byte[]>("FingeringDrawing").Length);

                picTesting.Image = new Bitmap(memStream);
            }
#endif

            #endregion



            //Check all the files in the database to see if they are there
            CheckDatabaseFiles(databaseInterface);

            //Get a list of all of the instruments of the availble exercises and adds them to the Combo box view.
            currentCommand = new SqlCommand("SELECT DISTINCT Instrument FROM Exercises WHERE FileExists = 1 INTERSECT SELECT DISTINCT Instrument FROM Notes");      //This command gets a list of all of the instruments that can be found in the Exercises table AND in the notes table. This ensures that there will be no exercises selected for which there aren't any defininitions for that instrument
            cmbInstuments.Items.AddRange(databaseInterface.executeQuery(currentCommand).AsEnumerable().Select(row => row[0].ToString()).ToArray());                 //Cycle through the datatable line by line and add each result to an array that can then be assigned to the combo box

            //Query database to select all available songs and display them in the datagrid view
            currentCommand          = new SqlCommand("SELECT * FROM Exercises WHERE FileExists = 1 AND Exercises.Instrument IN(SELECT Instrument FROM Notes)"); //This command gets all of the exercises where the file can be found and the instrument used in the exercises also has note definitions for it
            dgvExercises.DataSource = databaseInterface.executeQuery(currentCommand);                                                                           //Assign the result from the sql query to dgvExercises
        }
Example #3
0
        /// <summary>
        /// Checks the database to see if the files can be found. If a file cannot be found, it's "FileExists" row will be set to 0, else it will be set to 1 to show it does exist.
        /// </summary>
        /// <param name="databaseToCheck">The database interface that will be searched</param>
        private void CheckDatabaseFiles(databaseInterface databaseToCheck)
        {
            SqlCommand    currentCommand;
            List <string> filesNotFound = new List <string>();      //Stores the list of file paths that do not have files at the other end
            string        currentFilePath;                          //Stores the file path that is currently being checked to see if it exsists
            string        generatedSqlString;                       //Stores the generated SQL statement that will be used to update the database with which files are there or not

            //Checks each file in the Database to see if it can be found. If it can't be, then it's filepath is output to a list
            using (currentCommand = new SqlCommand("SELECT FilePath FROM Exercises")) {
                //Loop through all of the paths returned from the database, converts them string, checks they exist. If they do, the ids are saved to one list, else, the ids are saved to another list
                foreach (DataRow currentRow in databaseToCheck.executeQuery(currentCommand).Rows)
                {
                    currentFilePath = Convert.ToString(currentRow.ItemArray[0]);    //Get the file path from the row

                    //If the file doesn't exsist, then save the filePath to the fileNotFound list
                    if (!File.Exists(currentFilePath))
                    {
                        filesNotFound.Add(currentFilePath);
                    }
                }
            }

            //Update the datbase so the files are shown not to exist. Else, update athe database so all files are shown to exist
            using (currentCommand = new SqlCommand()) {
                /* In order to update the database to store whether the file can be found or not, an UPDATE statement is used with parametisation.
                 *
                 *  The parametised statement should look like this for example:
                 *  UPDATE Exercises SET FileExists = CASE
                 *                                        WHEN FilePath IN( @0, @1, @2, @3, @4) THEN 0
                 *                                        ELSE 1
                 *                                    END;
                 *
                 * In this statement, the parametised values will be the file paths of files that could not be found
                 */

                generatedSqlString = "UPDATE Exercises SET FileExists = CASE WHEN FilePath IN( ";      //The first part of the SQL Statement that will update the database depedning if the files are availible (A default null value is added, so if all files are found, the database is still updated)


                if (filesNotFound.Count != 0)
                {
                    //Loop through the list of files that have not been found
                    for (int i = 0; i < filesNotFound.Count; i++)
                    {
                        generatedSqlString += $" @{i},";                                        //Add a paramenter to the end of the statement string. eg: " @5,"
                        currentCommand.Parameters.AddWithValue($"@{i}", filesNotFound[i]);      //Add the parameter and the value that the parameter will assume to the command object which will be passed to the database interface
                    }
                }
                else
                {
                    generatedSqlString += "NULL";   //If there are not files to update, then set it to null
                }


                generatedSqlString  = generatedSqlString.TrimEnd(new char[] { ',' }); //Remove the last comma from the string, since it will be the unecerssary comma from the for loop
                generatedSqlString += ") THEN 0 ELSE 1 END;";                         //Finish off the SQL Statement

                currentCommand.CommandText = generatedSqlString;                      //Assign the generated string to the command object to get executed

                databaseToCheck.executeNonQuery(currentCommand);                      //Execute the command
            }
        }
Example #4
0
        /// <summary>
        /// Analyses how well the user has played
        /// </summary>
        /// <param name="listOfNotes">The list of MIDI notes that should have been played</param>
        /// <param name="timingList">The list that contains the timing information about what the user played</param>
        /// <param name="pitchList">The list of pitches that have been returned from the FFT</param>
        /// <param name="secondsPerTick">The seconds per MIDI tick</param>
        /// <param name="audioBufferLengthSec">The length of each timing Sub Buffer in seconds</param>
        /// <param name="pitchBufferLengthSec">The length of each pitch buffer in seconds</param>
        /// <param name="timingScore">The proportion of times where the user was playing at the right time</param>
        /// <param name="noteScore">The proportion of times where the user was playing the right notes at the right time</param>
        /// <param name="errorString">Stores the error message incase an error is hit</param>
        /// <returns>Whether the function was sucessfull or not</returns>
        private bool AnalysePerformance(List <Note> listOfNotes, List <bool> timingList, List <int> pitchList, float secondsPerTick, float audioBufferLengthSec, float pitchBufferLengthSec, out float timingScore, out float noteScore, out string errorString)
        {
            float noteStartSec;                                                                                                                            //Stores the start time of the note currently being processed in seconds
            float noteLengthSec;                                                                                                                           //Stores the length of the note currently being processed in seconds
            int   audioIndex;                                                                                                                              //Stores the index of the current note in the timing list
            int   pitchIndex;                                                                                                                              //Stores the index of the current note in the pitch list
            int   audioIndexLength;                                                                                                                        //Stores the number of indexs that the current note takes up in the timing list
            int   pitchIndexLength;                                                                                                                        //Stores the number of indexs that the current note takes up in the pitch list

            System.Data.SqlClient.SqlCommand getNoteCommand = new System.Data.SqlClient.SqlCommand("SELECT Note,BinNum FROM Notes WHERE Instrument = @0"); //The command that will select the Corosponging bin number from the database
            int       audioSamplesCorrect = 0;                                                                                                             //Stores the number of audio samples that were correct (Eg, there was audio playing/not playing at the right time)
            int       pitchSamplesCorrect = 0;                                                                                                             //Stores the number of pitches that were correct (Eg, the right note was being played at that time)
            DataTable noteDataTable       = new DataTable();

            Dictionary <byte, int> noteBinPairs = new Dictionary <byte, int>();


            timingScore = 0;
            noteScore   = 0;
            errorString = "";

            //Query database about what bin ids that each note makes
            getNoteCommand.Parameters.AddWithValue("@0", currentMidiFile.Instrument);

            noteDataTable = databaseInterface.executeQuery(getNoteCommand);


            //Create dictionary
            noteBinPairs = noteDataTable.AsEnumerable().ToDictionary(r => r.Field <byte>(0), r => r.Field <int>(1));        //Convert the datatable result to the dictionary by using linq expressions to cycle through all of the keys in column 0, and all the values that will be in column 1

            for (int i = 0; i < listOfNotes.Count; i++)
            {
                noteStartSec  = listOfNotes[i].absoluteTime * secondsPerTick;             //Get the start time of the note in seconds
                noteLengthSec = listOfNotes[i].length * secondsPerTick;                   //Get the length of the note in seconds

                audioIndex       = Convert.ToInt32(noteStartSec / audioBufferLengthSec);  //Get the index of the note in the audio list
                audioIndexLength = Convert.ToInt32(noteLengthSec / audioBufferLengthSec); //Get the length of indexs that the current note takes up in the audio list

                pitchIndex       = Convert.ToInt32(noteStartSec * pitchBufferLengthSec);  //Get the index of the note in the pitch list
                pitchIndexLength = Convert.ToInt32(noteLengthSec * pitchBufferLengthSec); //Get the length of indexs that the current note takes up in the pitch list


                //If the Note length would go on longer than the audio file was, then there is an error
                if (audioIndex + audioIndexLength > timingList.Count || pitchIndex + pitchIndexLength > pitchList.Count)
                {
                    errorString = "Midi File longer than the recorded audio file";
                    return(false);
                }


                //If the note number is not 0, then it is an actual note and so should be treated as such, else, it is a rest and so should be analysed as such
                if (listOfNotes[i].noteNum != 0)
                {
                    audioSamplesCorrect += timingList.GetRange(audioIndex, audioIndexLength).Count(x => x == true);     //Count all the times that audio was playing in the specified range
                }
                else
                {
                    audioSamplesCorrect += timingList.GetRange(audioIndex, audioIndexLength).Count(x => x == false);     //Count all the times that audio was NOT playing in the specified range
                }
            }

            //Calculate the overall scores the user got
            timingScore = (float)audioSamplesCorrect / (float)timingList.Count;
            noteScore   = (float)pitchSamplesCorrect / (float)pitchList.Count;

            return(true);
        }
        /*
         * --Initilization--
         * Calculate how many seconds are displayed in one line of music (Devision * Beats per bar * num of bars)
         * Calculate the number of pixels per second (width of one staff / seconds in one staff)
         * Calculate the width of the bitmap that will be required (Pixels Per Second * number of seconds)
         *
         * --Drawing the Notes that should've been played on--
         * Calculate the width of the box to draw (Note length * devision * Pixels per second)
         * Calucalte hieght to draw the box at (The line is split up into the range of highest bin number to lowest bin number using a scale factor = (height / Number of bins) and the lowest bin number box drawn at the botton)
         *
         *
         * --Drawing the notes that were actually played on--
         *
         * *******************************************************
         * How to do the drawing bit:
         *
         */

        private bool GenerateExampleBitmap(databaseInterface databaseConnection, out string errorString)
        {
            //Set up all of the varibles that will be used
            int ticksPerLine = p_devision * (int)(p_timeSig >> 24) * 4;                 //Number of MIDI ticks that one line of music represents (Ticks per quarter note * Quarter notes per bar * number of bar in line)
            //float secondsPerLine = (int)(p_timeSig >> 24) * (p_tempo/1000000) * 4;        //Gets the number of seconds that 1 line of music repreents     (Qauter note per bar * (Seconds per quater note) * number of bars in each line)
            //float pixelsPerSecond = secondsPerLine / p_listOfStaves[0].Width;             //Gets the number of pixels that 1 seconds takes up (Seconds per line / pixels per line)
            float pixelsPerTick = (float)p_listOfStaves[0].Width / ticksPerLine;                                            //Gets the number of pixels represented by 1 tick
            int   widthOfBitmap = (int)(pixelsPerTick * (p_listOfNotes.Last().absoluteTime + p_listOfNotes.Last().length)); //Gets the width the generated bitmap would need to be store the entire length of the midiFile
            Dictionary <byte, int> noteFftLocations = new Dictionary <byte, int>();
            int           lowestBinNum;                                                                                     //Stores the lowest frequncy bin that will be displayed as correct
            int           largestBinNum;                                                                                    //Stores the highest frequency bin that will be displayed as correct
            int           binHeight;                                                                                        //Stores the number of vertile pixels that are dedicated to the specific fft bins/pitchest
            int           currentNoteFFTPos;                                                                                //Stores the FFT pos of a note collected from the dictionary
            List <Bitmap> outListOfExamples = new List <Bitmap>();

            SqlCommand sqlCommandToRun = new SqlCommand();

            Bitmap   currentLine         = new Bitmap(p_listOfStaves[0].Width, p_listOfStaves[0].Height);
            Graphics currentLineGraphics = Graphics.FromImage(currentLine);

            Bitmap   generatedExample;         //Bitmap to store the generated image
            Graphics generatedExampleGraphics; //Graphics that will handle all the drawing

            errorString = "";

            //Get the dictionary of fft locations
            sqlCommandToRun.CommandText = "SELECT Note, BinNum FROM Notes WHERE Instrument = @instrument";           //Sql Commmand that will select all the exercises that use the right instrument
            sqlCommandToRun.Parameters.AddWithValue("@instrument", p_instrument);                                    //If the selected item is "All", then set the parameter to *, else set it to the inputted string


            //Populate the noteStave location dictionary
            try {
                //Creates a distionary from the datatable returned from the SQL query
                //                                                                                               ↓Loops throug the table, transmutes the type to a byte which can then be converted to a dicatonary
                noteFftLocations = databaseConnection.executeQuery(sqlCommandToRun).AsEnumerable().ToDictionary(currentRow => currentRow.Field <byte>(0), currentRow => currentRow.Field <int>(1));
            } catch (Exception) {
                errorString = "Could not retreive note FFT positions from database";
                return(false);
            }



            p_lowestBin  = lowestBinNum = noteFftLocations.First().Value - 2;      //The lowest bin number that will be displayed. The +-2 is to give clearance above and below the image to allow for the bitmaps that the user played to be displayed
            p_highestBin = largestBinNum = noteFftLocations.Last().Value + 2;      //The highest bin number that will be displayed

            binHeight = p_listOfStaves[0].Height / (largestBinNum - lowestBinNum); //Gets the height in pixels that will be dedicated to each bin



            generatedExample = new Bitmap(widthOfBitmap, p_listOfStaves[0].Height);

            using (generatedExampleGraphics = Graphics.FromImage(generatedExample)) {
                //Loop through all of the notes that will be drawn
                foreach (Note currentNote in p_listOfNotes)
                {
                    //only draw anything If the note is not a rest and the note is longer than 0.25 beats
                    if ((float)currentNote.length / p_devision > 0.25f)
                    {
                        //Make sure that the
                        if (noteFftLocations.TryGetValue(currentNote.noteNum, out currentNoteFFTPos))
                        {
                            //calculate the x position of the box that needs to be drawn (pixels Per tick * absolute time value)
                            //Get the bin location of the note from the dictionary and convert to a height(
                            //Calculate the width of the box that needs to be drawn (pixelsPerTick * Number of ticks)
                            //Draw the box

                            generatedExampleGraphics.FillRectangle(Brushes.Green, pixelsPerTick * currentNote.absoluteTime, generatedExample.Height - binHeight * (currentNoteFFTPos - lowestBinNum), pixelsPerTick * currentNote.length, binHeight);
                        }
                        else
                        {
                            //Could not find the FFt location so draw an X to show somthing went wrong
                            generatedExampleGraphics.DrawImage(Properties.Resources.NotFound, pixelsPerTick * currentNote.absoluteTime, 0);
                        }
                    }
                }
            }



            //Split long bitmap into sections
            for (int currentXPos = 0; currentXPos <= generatedExample.Width; currentXPos += p_listOfStaves[0].Width)
            {
                currentLine         = new Bitmap(currentLine.Width, currentLine.Height);    //Clear the bitmap after each use
                currentLineGraphics = Graphics.FromImage(currentLine);

                currentLineGraphics.DrawImage(generatedExample, -currentXPos, 0);           //get the section of the l o n g image that will be the current line
                outListOfExamples.Add(new Bitmap(currentLine));                             //Add the current line to the array
            }


            p_listOfRightNotes = outListOfExamples;


            return(true);
        }
        /// <summary>
        /// Generates the list of staves that have the images for the display and assignes it to p_listOfStaves
        /// </summary>
        /// <param name="databaseConnection">The database connection to use to get the note data out of the database to allow the notes to be drawn in the right place</param>
        /// <param name="errorString">Outs any errors that may have occured</param>
        /// <returns>whether there was an error or not</returns>
        public bool GenerateStaves(databaseInterface databaseConnection, out string errorString)
        {
            errorString = null;                                  //Stores any errors that can be passed back to the enclosing method
            List <Bitmap> listOfStaves    = new List <Bitmap>(); //this will store the list of generated bitmaps that will be use
            List <Bitmap> listOfFingering = new List <Bitmap>(); //This will store the lines of generated bitmaps that will display the fingering to the user
            const int     staveValue      = 45;
            const int     bottomYPos      = 365;                 //Stores the y pos of the bottom line of the stave from which all notes will be drawn above
            const int     cleffOffset     = 300;

            int[]     barOffsets  = { 515, 1863, 3211, 4551 };                  //Stores the x position of the start of the bars
            int       ticksPerBar = (int)(p_devision) * (int)(p_timeSig >> 24); //Ticks per note times the number of notes in a bar
            int       currentBarTickCount;                                      //Stores the current number of ticks that have been found in the list of notes
            byte      currentNotesInBar  = 0;                                   //Stores the number of notes that will need to be displayed in the current bar. Each bar is 1348px wide and starts 515px after the start of the last bar
            int       notesToDrawInBar   = 0;                                   //Stores the number of actually visable notes that will be drawn
            int       currentNoteIndex   = 0;                                   //Sores the index of the current note being processed
            int       currentDrawingNote = 0;
            byte      currentBar         = 0;                                   //Stores the number bar that is currently being drawn
            const int barWidth           = 1348;                                //The width of one bar
            byte      staveLocation      = 0;

            DataTable inputDatatable;       //Data table that stores the stuff returned for the database cuz devising a LINQ expression for dealing with converting byte streams to bitmaps would be wayyy too annoying and uneccersary.
            //MemoryStream bitmapMemStream;   //Memory stream that allows me to convert from the byte array stored in the database to the bitmap images (Don't use "Using" with this on cuz as long as the bitmap needs to exsists, the stream it cam from will need to exist to. Probably not neccersary since this is a memore stream not a binary stream)

            Dictionary <byte, byte>   noteStaveLocation;                                   //Stores the note value as the key and the location on the stave (0 - middle c)
            Dictionary <byte, Bitmap> noteFingeringDict = new Dictionary <byte, Bitmap>(); //Stores the note value as the key and the image of the fingering of that note as the value

            SqlCommand sqlCommandToRun = new SqlCommand();

            Bitmap bitmapToDraw    = new Bitmap(Properties.Resources.NotFound.Width, Properties.Resources.NotFound.Height); //= Properties.Resources.NotFound;        //Saves the image of the right length note that will be drawn
            Bitmap fingeringToDraw = new Bitmap(Properties.Resources.NotFound.Width, Properties.Resources.NotFound.Height); //Used to save the image of the fngering for that particular note

            int firstNoteIndex = 0;                                                                                         //Stores the index of the first note in the bar
            //int staveStartOffsetX = 300;

            Bitmap   currentStaff         = Properties.Resources.StaffWithBars;
            Graphics currentStaffGraphics = Graphics.FromImage(currentStaff);

            Bitmap   currentGeneratedFingering         = new Bitmap(currentStaff.Width, currentStaff.Height);
            Graphics currentGeneratedFingeringGraphics = Graphics.FromImage(currentGeneratedFingering);

            sqlCommandToRun.CommandText = "SELECT Note, StaveLocation FROM Notes WHERE Instrument = @instrument"; //Sql Commmand that will select all the exercises that use the right instrument
            sqlCommandToRun.Parameters.AddWithValue("@instrument", p_instrument);                                 //If the selected item is "All", then set the parameter to *, else set it to the inputted string

            //Populate the noteStave location dictionary
            try {
                //Creates a distionary from the datatable returned from the SQL query
                //                                                                                               ↓Loops throug the table, transmutes the type to a byte which can then be converted to a dicatonary
                noteStaveLocation = databaseConnection.executeQuery(sqlCommandToRun).AsEnumerable().ToDictionary(currentRow => currentRow.Field <byte>("Note"), currentRow => currentRow.Field <byte>("StaveLocation"));



                //Creates a dictionary that stores the note values and the fingering images that will be used to drawing the fingering bitmaps that will be displayed beneath the note images
                sqlCommandToRun = new SqlCommand("SELECT Note, FingeringDrawing FROM Notes WHERE Instrument = @instrument");
                sqlCommandToRun.Parameters.AddWithValue("@instrument", p_instrument);

                inputDatatable = databaseConnection.executeQuery(sqlCommandToRun);


                //Iterate through the returned datatable from the database and get the image and add that to the dictionary
                foreach (DataRow row in inputDatatable.Rows)
                {
                    noteFingeringDict.Add(row.Field <byte>("Note"), new Bitmap(new MemoryStream(row.Field <byte[]>("FingeringDrawing"))));      //For the image bit: Get the byte array from the datatable, then create a memory stream from this byte array, then create a bitmap from this memorey stream and then throw that into the dictionary
                }
            } catch (Exception) {
                errorString = "Problem acsessing the database";
                return(false);
            }



            //Draw time signature
            //nn dd cc dd     nn / 2 ^ dd     nn - Numerator  dd - denomiator     cc - Clock ticks per metronome tick     bb - Number of 1 / 32 notes per 24 MIDI clocks(8 normally)
            //Numerator = (timeSig >> 24)
            //Denominat = (2^(midiFile.timeSig >> 16) & 0xFF)
            currentStaffGraphics.DrawString((p_timeSig >> 24).ToString(), new Font("Arial", 175), Brushes.Black, cleffOffset, 100);
            currentStaffGraphics.DrawString(Math.Pow(2, (p_timeSig >> 16) & 0xFF).ToString(), new Font("Arial", 175), Brushes.Black, cleffOffset, 300);



            //Loop through all of the staves
            while (p_listOfNotes.Count > currentNoteIndex + 1)
            {
                currentBar = 0;

                //Loop through all of the bars
                while (currentBar < 4 && (p_listOfNotes.Count > currentNoteIndex + 1))
                {
                    //Get a list of all of the notes that will be displayed in the current bar
                    firstNoteIndex      = firstNoteIndex + currentNotesInBar + 1 < p_listOfNotes.Count ? firstNoteIndex + currentNotesInBar : p_listOfNotes.Count - 1;
                    currentBarTickCount = 0;
                    currentNotesInBar   = 0;
                    notesToDrawInBar    = 0;
                    currentDrawingNote  = 0;
                    bitmapToDraw        = Properties.Resources.NotFound;

                    //Loop untill there are the right number of beats in the bar
                    do
                    {
                        //Only count the note if it is long enough to be counted
                        if ((float)p_listOfNotes[currentNoteIndex].length / (float)p_devision >= 0.20f)
                        {
                            currentBarTickCount += p_listOfNotes[currentNoteIndex].length;
                            notesToDrawInBar++;
                        }
                        currentNotesInBar++;
                        currentNoteIndex++;
                    } while ((p_listOfNotes.Count > currentNoteIndex) && (currentBarTickCount <= ticksPerBar - p_listOfNotes[currentNoteIndex].length));



                    //Display those notes on the stave
                    for (int i = firstNoteIndex; i < firstNoteIndex + currentNotesInBar; i++)
                    {
                        //Only draw the note if it is not too short
                        if ((float)p_listOfNotes[i].length / (float)p_devision >= 0.20)
                        {
                            //Find out if the note is a rest of not
                            if (p_listOfNotes[i].noteNum != 0)
                            {
                                //Find out what image to draw
                                switch ((float)p_listOfNotes[i].length / p_devision)
                                {
                                //Draw a Quaver
                                case float len when(len > 0.20 && len <= 0.75):
                                    bitmapToDraw = Properties.Resources.EighthNote;

                                    break;

                                //Draw a Crotchet
                                case float len when(len > 0.75 && len <= 1.25):
                                    bitmapToDraw = Properties.Resources.QuarterNote;

                                    break;

                                //Draw a Dotted crochet
                                case float len when(len > 0.25 && len <= 1.75):
                                    bitmapToDraw = Properties.Resources.DottedQuarterNote;

                                    break;

                                //Draw a Minum
                                case float len when(len > 1.75 && len <= 2.5):
                                    bitmapToDraw = Properties.Resources.HalfNote;

                                    break;

                                //Draw a Dotted minum
                                case float len when(len > 2.5 && len <= 3.5):
                                    bitmapToDraw = Properties.Resources.DottedHalfNote;

                                    break;

                                //Draw a Whole note
                                case float len when(len > 3.5 && len <= 5.0):
                                    bitmapToDraw = Properties.Resources.WholeNote;

                                    break;

                                //Draw a dotted whole note
                                case float len when(len > 5.0 && len <= 7.0):
                                    bitmapToDraw = Properties.Resources.DottedWholeNote;

                                    break;


                                default:
                                    //Note length that cannot be drawn
                                    bitmapToDraw = Properties.Resources.NotFound;
                                    break;
                                }


                                //Gets the stave location from the dictionary
                                if (!noteStaveLocation.TryGetValue(p_listOfNotes[i].noteNum, out staveLocation))
                                {
                                    staveLocation = 5;                             //Draw all the X's in the same location
                                    bitmapToDraw  = Properties.Resources.NotFound; //show that the image cannot be found
                                }

                                fingeringToDraw = new Bitmap(10, 10);

                                //If the fingering diagram cannot be found in the dictionary, then display a note found in it's place
                                if (!noteFingeringDict.TryGetValue(p_listOfNotes[i].noteNum, out fingeringToDraw))
                                {
                                    fingeringToDraw = Properties.Resources.NotFound;
                                }
                            }
                            else            //Else, note is a rest and a rest should be drawn

                            {
                            }

                            //Draw the right note in the right place
                            currentStaffGraphics.DrawImage(bitmapToDraw, barWidth / (notesToDrawInBar != 0 ? notesToDrawInBar : 1) * (currentDrawingNote) + barOffsets[currentBar], bottomYPos - staveLocation * staveValue);

                            //Draw the fingering for the current note in the same place on another bitmap for use later
                            currentGeneratedFingeringGraphics.DrawImage(fingeringToDraw, barWidth / (notesToDrawInBar != 0 ? notesToDrawInBar : 1) * (currentDrawingNote) + barOffsets[currentBar], 0);

                            currentDrawingNote++;
                        }
                    }
                    currentBar++;
                }



                listOfStaves.Add(currentStaff);
                listOfFingering.Add(currentGeneratedFingering);


                currentStaff         = Properties.Resources.StaffWithBars;
                currentStaffGraphics = Graphics.FromImage(currentStaff);

                currentGeneratedFingering         = new Bitmap(currentStaff.Width, currentStaff.Height);
                currentGeneratedFingeringGraphics = Graphics.FromImage(currentGeneratedFingering);
            }



            p_listOfStaves           = listOfStaves;
            p_listOfFingeringBitmaps = listOfFingering;
            return(true);
        }