static bool displayNotesPlayed = false; //Stores whether the notes that the user played should be displayed or not #region Form Triggered Methods public frmTest(midiFile _inputMidi, databaseInterface _databaseInterface) { InitializeComponent(); currentMidiFile = _inputMidi; //new midiFile(_inputMidi.devision, _inputMidi.tempo, _inputMidi.timeSig, _inputMidi.keySig, _inputMidi.listOfNotes, _inputMidi.Instrument, _databaseInterface, out bool error, out string errorString); databaseInterface = _databaseInterface; currentMidiFile.GenerateMusicPage(picDisplay.Width, picDisplay.Height, 0, ckbFingering.Checked, false, out Bitmap pageToDisplay, out nextFirstStaff, out string errorString); picDisplay.Image = pageToDisplay; }
/// <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 }
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 }
/// <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 } }
private int p_highestBin; //Stores the highest bin number that is displayed on the example bitmap of what the user should've played #endregion /// <summary> /// Instantiats the midi file class with all of the important values that are required /// </summary> /// <param name="timeSig">The time signalture of the peice</param> /// <param name="devision">The tempo of the peice</param> /// <param name="keySig">The key sig of the peice</param> /// <param name="listOfNotes">The list of notes that makes up the peice</param> public midiFile(ushort?devision, uint?tempo, uint?timeSig, ushort?keySig, List <Note> listOfNotes, string instrument, databaseInterface databaseInterface, out bool error, out string errorString) { //Assign the inputted data to the private varibles //If the input is null, then assign the default midi values to everything p_devision = devision ?? (ushort)(120); p_tempo = tempo ?? (uint)(500000); p_timeSig = timeSig ?? (uint)(0x04022408); p_keySig = keySig ?? (ushort)(0x0000); p_listOfNotes = listOfNotes; p_instrument = instrument; //Set up the Generate all the things that will be required throughout the program //This would be the bitmap lists //Generate the list of bitmaps that is the list of staves error = !GenerateStaves(databaseInterface, out errorString); if (error) { return; } //Generate the example bitmap that will show what the correct image should be error = !GenerateExampleBitmap(databaseInterface, out errorString); if (error) { return; } }
/* * --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); }