/// <summary> /// Parse an entire string of measures of rhythms. /// </summary> /// <param name="input"></param> /// <returns></returns> static public List <Measure> ParseRhythmMeasures(Workspace workspace, string input, out int upbeatOffset, out Key key) { List <Measure> measures = new List <Measure>(); int measureNumber = 0; bool incomingTie = false; bool tieToNextMeasure; int defaultOctave = 4; // First extract and parse the key from the start of the string. For no key, assume C major. // Format should be like // "C: QC QD QE QF | QD QE QF QG" for C major // "Dm: QD QE QF QD | QD QE QF QG" for D minor string[] tokens = input.Split(':'); string inputNoKey; if (tokens.Length > 1) { string keyString = tokens[0]; key = new Key(keyString); inputNoKey = input.Substring(keyString.Length + 1); } else { key = new Key(); // C major inputNoKey = input; } foreach (string measureStr in inputNoKey.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries)) { Rhythm rhythm = new Rhythm(measureStr.Trim(), incomingTie, out tieToNextMeasure, ref defaultOctave); Measure m = new Measure(workspace, rhythm); m.number = measureNumber++; measures.Add(m); // Apply any final tie to the start of the next measure. incomingTie = tieToNextMeasure; } // Now go through backwards, updating note total durations including ties. int totaldur = 0; for (int i = measures.Count - 1; i >= 0; i--) { Measure m = measures[i]; for (int j = m.rhythm.notes.Count - 1; j >= 0; j--) { Note n = m.rhythm.notes[j]; if (n.tiedAfter) { totaldur += n.duration; } else { totaldur = n.duration; } n.duartionIncludingTiesAfter = totaldur; } } // Compute upbeat offset if (measures.Count < 2) { upbeatOffset = 0; } else { upbeatOffset = measures[0].rhythm.TotalDuration; if (upbeatOffset == measures[1].rhythm.TotalDuration) { upbeatOffset = 0; } } return(measures); }
public Measure(Workspace workspace) : base(workspace) { rhythm = new Rhythm(); }
public Measure(Workspace workspace, Rhythm rhythm) : base(workspace) { this.rhythm = (Rhythm)rhythm.Clone(); }
static void Main(string[] args) { // Parse command-line args foreach (string arg in args) { if (arg == "noGUI") { GUIenabled = false; Console.WriteLine("No GUI"); } else if (arg.StartsWith("input=")) { input = arg.Substring(6, arg.Length - 6); inputFromCommandLine = true; Console.WriteLine("Input set to: " + input); } else if (arg.StartsWith("png=")) { pngFilePath = arg.Substring(4, arg.Length - 4); Console.WriteLine("Output .png: " + pngFilePath); } else if (arg.StartsWith("width=")) { pngWidth = int.Parse(arg.Substring(6, arg.Length - 6)); Console.WriteLine("Output width: " + pngWidth.ToString()); } else if (arg.StartsWith("height=")) { pngHeight = int.Parse(arg.Substring(7, arg.Length - 7)); Console.WriteLine("Output height: " + pngHeight.ToString()); } else if (arg.StartsWith("seed=")) { int seed = int.Parse(arg.Substring(5, arg.Length - 5)); Utilities.InitRandom(seed); Console.WriteLine("Random seed: " + pngHeight.ToString()); // Arguments for automatic testing and % correct reporting. } else if (arg.StartsWith("inputExampleName=")) { inputExampleName = arg.Substring(17, arg.Length - 17); Console.WriteLine("inputExampleName set to: " + inputExampleName); } else if (arg.StartsWith("testFile=")) { testFile = arg.Substring(9, arg.Length - 9); groundTruthTesting = true; Console.WriteLine("testFile set to: " + testFile); } else if (arg.StartsWith("resultFile=")) { resultFile = arg.Substring(11, arg.Length - 11); if (!File.Exists(resultFile)) { StreamWriter sw = File.CreateText(resultFile); sw.Close(); } Console.WriteLine("resultFile set to: " + resultFile); } else { throw new ArgumentException("Unknown command-line argument: " + arg); } } if (!GUIenabled && pngFilePath == null && !groundTruthTesting) { throw new ArgumentException("png argument required to set filepath"); } // Parse the file and set input if groundTruth testing if (groundTruthTesting) { Utilities.LogtoFile("GroundTruthTesting: " + DateTime.Now.ToString()); int numRepetitions; List <GroundTruthExample> groundTruthExamples; Parser.Parse(testFile, out numRepetitions, out groundTruthExamples); foreach (GroundTruthExample e in groundTruthExamples) { if (e.name == inputExampleName) { groundTruthExample = e; break; } } if (groundTruthExample.name != inputExampleName) { throw new Exception("Example name not found in ground truth file: " + inputFromCommandLine); } input = groundTruthExample.input; Utilities.LogtoFile("Input: " + input); } if (!GUIenabled) { restarting = false; } else { restarting = true; } if (GUIenabled) { Console.WindowHeight = 40; Console.BufferHeight = 3000; } try { // Verify screenshot path exists. System.IO.Directory.CreateDirectory(Constants.SCREENSHOT_PATH); while (true) //exectues once for each run of the program on an input { Init(); if (!GUIenabled) { pausing = false; } // Set the default input unless we reset with a different input. if (!GUIenabled || !restarting) { // } else { // Clear the restart flag, but leave the input alone. restarting = false; } // Parse the input measures. int upbeatOffset; Key key; List <Measure> parsedMeasures = Rhythm.ParseRhythmMeasures(workspace, input, out upbeatOffset, out key); workspace.Key = key; foreach (Measure m in parsedMeasures) { workspace.measuresInput.Add(m); } workspace.upbeatOffset = upbeatOffset; List <Measure> shiftedMeasures = Rhythm.ShiftRhythmMeasures(workspace, parsedMeasures, upbeatOffset); foreach (Measure m in shiftedMeasures) { workspace.measuresInputShifted.Add(m); } // If there is an upbeat, add the upbeat measure for drawing. Measure newDrawingMeasure = null; Measure drawingMeasureSource = null; int nextDrawingAttackPointIndex = 0; // has to be outside of measure loop because drawing meausre have a different span. int nextDrawingAttackPoint = 0; if (upbeatOffset > 0) { workspace.measuresDrawing.Add(new Measure(workspace.measuresInput[0])); newDrawingMeasure = workspace.measuresDrawing[0]; newDrawingMeasure.ClearNotes(); drawingMeasureSource = workspace.measuresInput[0]; nextDrawingAttackPoint = drawingMeasureSource.rhythm.AttackPointsIncludingRests[0] - upbeatOffset; } int numSixteenthsTotal = Constants.NUM_EXTRA_MEASURES_COMPUTATION * 16; foreach (Measure m in workspace.measuresInput) { numSixteenthsTotal += m.MeasureDuration; } workspace.MaxPossibleTime = Constants.NUM_CODELETS_PER_SIXTEENTH * numSixteenthsTotal - 1; if (groundTruthTesting) { //Utilities.LogtoFile("Ground truth testing: # measuresInput=" + workspace.measuresInput.Count.ToString()); Utilities.LogtoFile("Ground truth testing: input = " + input); } #region Main Loop! // Run N codelets for each measure int i; for (i = 0; i < workspace.measuresInput.Count + Constants.NUM_EXTRA_MEASURES_COMPUTATION; i++) { if (restarting) { break; } workspace.expectations.CleanExpectations(); if (GUIenabled) { lock (restartLock) { if (restarting) { break; } workspace.Draw(coderack); TakeScreenshot(i + 1); } } int nextAttackPointShifted = 0; int nextAttackPointIndex = 0; Measure newShiftedMeasure = null; Measure shiftedMeasureSource = null; if (i < workspace.measuresInputShifted.Count) { shiftedMeasureSource = workspace.measuresInputShifted[i]; } else { if (workspace.measuresInputShifted.Count > 0) { shiftedMeasureSource = workspace.measuresInputShifted[workspace.measuresInputShifted.Count - 1]; } } int codeletsPerMeasure = 0; if (shiftedMeasureSource != null) { codeletsPerMeasure = Constants.NUM_CODELETS_PER_SIXTEENTH * shiftedMeasureSource.MeasureDuration; } // Run N codelets (60) per sixteenth note. for (int j = 0; j < codeletsPerMeasure && !restarting; j++) { while (pausing) { //if (groundTruthTesting) // Utilities.LogtoFile("****************************** PAUSING in ground truth testing"); Thread.Sleep(100); //Application.DoEvents(); } lock (restartLock) { if (restarting) { break; } if (GUIenabled) { workspace.frmWorkspace.SetCodeletNum(workspace.CurrentTime); } coderack.RunNextCodelet(); } workspace.CurrentTime++; // Add measures and notes as time passes. // This got messey due to maintaining the drawing and internal shifted // versions for upbeat measures. Rewrite with less duplication would // be preferable, but it works for now. Note that the shifted version // has a different set of notes and attacks and durations, due to // mandatory breaking-up of long notes crossing barlines. #region Add Measures and Notes // We will add notes if we reached a new attack point. But first create empty measures as needed. // Add a shifted measure if necessary. if (j == 0 && i < workspace.measuresInputShifted.Count) { workspace.measures.Add(new Measure(shiftedMeasureSource)); newShiftedMeasure = workspace.measures[workspace.measures.Count - 1]; newShiftedMeasure.ClearNotes(); } // Add a drawing measure if necessary. //if (j == ((workspace.measuresInput[1].MeasureDuration - upbeatOffset) % workspace.measuresInput[1].MeasureDuration) * Constants.NUM_CODELETS_PER_SIXTEENTH) { if (j == upbeatOffset * Constants.NUM_CODELETS_PER_SIXTEENTH) { drawingMeasureSource = null; if (upbeatOffset > 0) { if (i + 1 < workspace.measuresInput.Count) { drawingMeasureSource = workspace.measuresInput[i + 1]; } } else if (i < workspace.measuresInput.Count) { drawingMeasureSource = workspace.measuresInput[i]; } if (drawingMeasureSource != null) { workspace.measuresDrawing.Add(new Measure(drawingMeasureSource)); newDrawingMeasure = workspace.measuresDrawing[workspace.measuresDrawing.Count - 1]; newDrawingMeasure.ClearNotes(); nextDrawingAttackPointIndex = 0; nextDrawingAttackPoint = drawingMeasureSource.rhythm.AttackPointsIncludingRests[0]; } } // Check for an attack point for the shifted measure. if (j == nextAttackPointShifted * Constants.NUM_CODELETS_PER_SIXTEENTH) { // If we aren't in the extra measures yet, add note to drawing and shifted measures. if (i < workspace.measuresInputShifted.Count) { // Add note. newShiftedMeasure.rhythm.AddNote(shiftedMeasureSource.rhythm.notes[nextAttackPointIndex]); // Increment attack point. nextAttackPointIndex++; if (nextAttackPointIndex < shiftedMeasureSource.rhythm.AttackPointsIncludingRests.Count) { nextAttackPointShifted = shiftedMeasureSource.rhythm.AttackPointsIncludingRests[nextAttackPointIndex]; } } } // Check for an attack point for the drawing measure. if (j == (((nextDrawingAttackPoint + upbeatOffset) * Constants.NUM_CODELETS_PER_SIXTEENTH) % codeletsPerMeasure)) { // If we aren't in the extra measures yet, add note to drawing and shifted measures. if (i < workspace.measuresInputShifted.Count) { // Add note. newDrawingMeasure.rhythm.AddNote(drawingMeasureSource.rhythm.notes[nextDrawingAttackPointIndex]); // Increment attack point. nextDrawingAttackPointIndex++; if (nextDrawingAttackPointIndex < drawingMeasureSource.rhythm.AttackPointsIncludingRests.Count) { nextDrawingAttackPoint = drawingMeasureSource.rhythm.AttackPointsIncludingRests[nextDrawingAttackPointIndex]; if (workspace.measuresDrawing.Count == 1) { nextDrawingAttackPoint -= upbeatOffset; // correct the upbeat measure's attack points } } } } #endregion if (GUIenabled) { if (workspace.CurrentTime % 15 == 0) { Console.WriteLine("Time = " + workspace.CurrentTime.ToString()); } lock (restartLock) { if (restarting) { break; } if (workspace.CurrentTime % NUM_CODELETS_UNTIL_DRAW == 0) { workspace.Draw(coderack); } } } // Update the slipnet every 15 codelets. if (workspace.CurrentTime % Constants.NUM_CODELETS_TILL_SLIPNET_UPDATE == 0) { // Generate list of concept instances from the workspace. List <ConceptInstance> conceptInstances = new List <ConceptInstance>(); /* * foreach (GroupElement ge in workspace.Layers[0].GroupElements) { * // Grab all concept nodes activated above threshold in any perspect lists. * foreach (Perspect p in ge.Perspects) { * conceptInstances.Add(new ConceptInstance(p.ConceptNode, p.Strength)); * } * }*/ slipnet.UpdateActivations(conceptInstances); } // Update the Temperature every 15 codelets. if (workspace.CurrentTime % Constants.NUM_CODELETS_TILL_TEMPERATURE_UPDATE == 0) { // Use the average happiness of structures in the recent window. int max = workspace.MaxLocation; int min = Math.Max(0, max - Constants.NUM_MEASURES_FOR_WORKSPACE_HAPPINESS); workspace.Temperature = workspace.HappinessForRange(min, max); } // Update the coderack every 15 codelets. if (workspace.CurrentTime % Constants.NUM_CODELETS_TILL_CODERACK_UPDATE == 0) { // Add new codelets. coderack.Populate(); coderack.Populate(); } // Clean out old codelets every 15 codelets. if (workspace.CurrentTime % Constants.NUM_CODELETS_TILL_CODERACK_CLEANUP == 0) { // Remove oldest codelets until the coderack is the maximum size. coderack.RemoveOld(); } // Fade old links, relationships every 15 codelets. if (workspace.CurrentTime % Constants.NUM_CODELETS_TILL_LINK_FADE == 0) { // Remove oldest codelets until the coderack is the maximum size. workspace.WeakenOldLinks(); } // Dump in more high-level codelets every 30 codelets. if (workspace.CurrentTime % Constants.NUM_CODELETS_TILL_NEW_HIGH_LEVEL == 0) { AddHighLevelCodelets(i); } if (GUIenabled) { Thread.Sleep(TIME_TO_SLEEP_BETWEEN_CODELETS); } } // Done with a measure. if (restarting) { //if (groundTruthTesting) // Utilities.LogtoFile("****************************** RESTART in ground truth testing"); break; } if (GUIenabled) { // Show coderack. //Console.WriteLine("Coderack:"); //Console.WriteLine(codeRack.ToString()); Console.WriteLine("Workspace:"); Console.WriteLine(workspace.ToString()); Console.WriteLine(slipnet.ToString()); } } // Done with all measures! #endregion // DONE with run! // Clear future expectations. workspace.expectations.RemoveAllExpectations(); // Final draw of everything. workspace.Draw(coderack); // Final screenshot w/o expectations. TakeScreenshot(i + 1); // Finally generate the graphical output if we're in noGUI mode and not doing groundTruthTesting. if (!GUIenabled & !groundTruthTesting) { int width = DEFAULT_OUTPUT_WIDTH, height = DEFAULT_OUTPUT_HEIGHT; Bitmap bmp = new Bitmap(width, height); Graphics graphics = Graphics.FromImage(bmp); WorkspaceForm.DrawWorkspaceToContext(graphics, width, height, workspace, 0, false, false, bmp); // Now scale to desired size. Bitmap bmpScaled = new Bitmap(bmp, pngWidth, pngHeight); bmpScaled.Save(pngFilePath, System.Drawing.Imaging.ImageFormat.Png); return; } // Compute results and store to file if we're going GT testing. if (groundTruthTesting) { // Check groups. int totalNumGroupsFound; if (!ESSEN_TESTING) { totalNumGroupsFound = workspace.groups.Count; } else { // Only count strong normal (non-meta) groups in Essen testing. totalNumGroupsFound = 0; foreach (Group g in workspace.groups) { if (g.Level == 1 && g.ComputeStrength() > 20) { totalNumGroupsFound++; } } } int numMissingGroups = 0; foreach (GroundTruth.ExpectedGroup eg in groundTruthExample.groups) { // Try to find the expected group. bool found = false; foreach (Group g in workspace.groups) { if (g.MinLocation == eg.startMeasure && g.MaxLocation == eg.endMeasure) { found = true; break; } } if (!found) { numMissingGroups++; } } // Check analogies. int totalNumAnalogiesFound = workspace.analogies.Count; int numMissingAnalogies = 0; foreach (GroundTruth.ExpectedAnalogy ea in groundTruthExample.analogies) { // Try to find the expected group. bool found = false; foreach (Analogy a in workspace.analogies) { if (a.LHS.MinLocation == ea.LHS.startMeasure && a.LHS.MaxLocation == ea.LHS.endMeasure && a.RHS.MinLocation == ea.RHS.startMeasure && a.RHS.MaxLocation == ea.RHS.endMeasure) { found = true; break; } } if (!found) { numMissingAnalogies++; } } // Write to disk. Format: // # expected groups, # missing, #expected analogies, #missing, timestamp // Lock results file in case we run multiple musicats at once. bool retry = true; int retries = 10; while (retry) { retry = false; Utilities.LogtoFile("Avg ms: " + workspace.Coderack.AvgCodeletRunTimeMs.ToString() + "\tavg age at run: " + workspace.Coderack.AverageCodeletAgeAtRun.ToString() + "\t% Killed: " + workspace.Coderack.PercentCodeletsKilled.ToString() + "\ttimestep at end: " + workspace.CurrentTime.ToString() + "\t#measures in workspaceShifted: " + workspace.measuresInputShifted.Count.ToString()); try { FileStream fileStream = new FileStream(resultFile, FileMode.Append, FileAccess.Write, FileShare.None); using (TextWriter tw = new StreamWriter(fileStream)) { tw.WriteLine("{0},{1},{2},{3},{4},{5},{6}", groundTruthExample.groups.Count, numMissingGroups, groundTruthExample.analogies.Count, numMissingAnalogies, DateTime.Now.ToString(), totalNumGroupsFound, totalNumAnalogiesFound); } } catch (FileNotFoundException Ex) { // The file didn't exist MessageBox.Show("Didn't exist:" + Ex.ToString()); } catch (AccessViolationException Ex) { // You don't have the permission to open this MessageBox.Show("Access violation: " + Ex.ToString()); } catch (IOException Ex) { // IO exception -- try a few more times. if (retries > 0) { retry = true; retries--; } else { MessageBox.Show("Max retries reached: IO exception: " + Ex.ToString()); } Thread.Sleep(20); } catch (Exception Ex) { // Something happened! MessageBox.Show("Unexpected exception: " + Ex.ToString()); } } return; // done with ground truth testing! } // Wait until reset event happens while (!restarting) { Thread.Sleep(100); //Application.DoEvents(); } curMelodyIndex = workspace.frmWorkspace.MelodyIndex; workspace.CloseForms(); //Console.Read(); } } catch (Exception e) { Utilities.LogtoFile(e.ToString()); #if DEBUG throw e; #else MessageBox.Show(e.ToString()); #endif } }