/// <summary> /// The value argument is the MNXC measure location string /// ("0.25", "3/8", "4:0.25", "4:1/4", "#event235" etc.) /// I assume that the argument value can be "incoming" and "outgoing" /// in addition to the values described at /// https://w3c.github.io/mnx/specification/common/#measure-location /// See https://w3c.github.io/mnx/specification/common/#the-tied-element /// I'd prefer not to allow the decimal representations of position. /// These just duplicate the other options, and create unnecessary work /// for parsers, so I'm currently ignoring them. /// </summary> public PositionInMeasure(string value) { if (value[0] == '#') { ID = value.Substring(1); // no '#' (okay?) } else if (value.IndexOf(':') < 0) { MeasureNumber = null; // means "current measure" switch (value) { case "incoming": Short = ShortTieOrSlur.incoming; break; case "outgoing": Short = ShortTieOrSlur.outgoing; break; default: Position = new MNXDurationSymbol(value); break; } } else { char[] separator = { ':' }; string[] mStrs = value.Split(separator, System.StringSplitOptions.None); int.TryParse(mStrs[0], out int measureNumber); MeasureNumber = measureNumber; Position = new MNXDurationSymbol(mStrs[1]); } }
public GlobalMeasure(XmlReader r, int measureIndex, TimeSignature currentTimeSig) { M.Assert(r.Name == "measure-global"); // https://w3c.github.io/mnx/specification/common/#the-measure-element Index = measureIndex; // ji: 23.06.2020 GlobalDirections = new GlobalDirections(currentTimeSig); // default if (!r.IsEmptyElement) { int count = r.AttributeCount; for (int i = 0; i < count; i++) { r.MoveToAttribute(i); switch (r.Name) { // The optional MNX-Common "index" attribute is always ignored. //case "index": // Index = Int32.Parse(r.Value); // M.Assert(Index > 0); // break; case "number": Number = Int32.Parse(r.Value); M.Assert(Number > 0); break; case "barline": Barline = GetBarlineType(r.Value); break; default: throw new ApplicationException("Unknown attribute"); } } M.ReadToXmlElementTag(r, "directions-global"); while (r.Name == "directions-global") { if (r.NodeType != XmlNodeType.EndElement) { switch (r.Name) { case "directions-global": GlobalDirections = new GlobalDirections(r, currentTimeSig); currentTimeSig = GlobalDirections.CurrentTimeSignature; break; } } M.ReadToXmlElementTag(r, "directions-global", "measure-global"); } } TicksDuration = new MNXDurationSymbol(currentTimeSig.Signature).GetDefaultTicks(); M.Assert(r.Name == "measure-global"); // end of measure-global }
/// <summary> /// This function is called recursively. /// TicksPosInScore is not set. Grace groups are ignored. /// TicksPosInScore and TicksDurations are updated for grace notes /// when the whole score has been read (in MNX.AdjustForGraceNotes()). /// </summary> /// <param name="outerTicks"></param> private void SetTicksDurationsInContentIgnoringGraces(int outerTicks) { int GetBasicTicks(IHasTicksDuration component) { int rval = 0; if (component is Event e) { MNXDurationSymbol defaultDuration = e.MNXDurationSymbol; MNXDurationSymbol ticksOverride = e.TicksOverride; rval = (ticksOverride == null) ? defaultDuration.GetDefaultTicks() : ticksOverride.GetDefaultTicks(); } else if (component is Forward f) { rval = f.TicksDuration; } else if (component is TupletDef t) { rval = t.OuterDuration.GetDefaultTicks(); } return(rval); } List <int> eventForwardTupletBasicTicks = new List <int>(); List <IHasTicksDuration> eventForwardTuplets = new List <IHasTicksDuration>(); // Get the default outer ticks of each contained Event, Forward and TupletDef component. foreach (IHasTicksDuration component in Components) { if (component is Event || component is Forward || component is TupletDef) { eventForwardTupletBasicTicks.Add(GetBasicTicks(component)); eventForwardTuplets.Add(component); } } List <int> innerTicks = M.IntDivisionSizes(outerTicks, eventForwardTupletBasicTicks); // Set the default outer ticks of each contained Event, Forward and TupletDef component. for (int i = 0; i < eventForwardTuplets.Count; i++) { IHasTicksDuration eventForwardTuplet = eventForwardTuplets[i]; int ticks = innerTicks[i]; if (eventForwardTuplet is Event e) { e.TicksDuration = innerTicks[i]; } if (eventForwardTuplet is Forward f) { f.TicksDuration = innerTicks[i]; } if (eventForwardTuplet is TupletDef t) { t.SetTicksDurationsInContentIgnoringGraces(innerTicks[i]); // recursive call } } }
public Forward(XmlReader r) { // TicksDuration is initally be related to the default ticksDuration of the MNXDurationSymbol, // but it changes if this event is part of a tuplet, and again when the file has been completely // read, to accomodate Grace notes. // TicksPosInScore is set correctly when the complete file has been parsed. TicksPosInScore = 0; M.Assert(r.Name == "forward"); r.MoveToAttribute("duration"); MNXDurationSymbol = new MNXDurationSymbol(r.Value); M.ReadToXmlElementTag(r, "forward"); M.Assert(r.Name == "forward"); // end of forward }
public TupletDef(XmlReader r, bool isTopLevel) { M.Assert(r.Name == "tuplet"); _isTopLevel = isTopLevel; int count = r.AttributeCount; for (int i = 0; i < count; i++) { r.MoveToAttribute(i); switch (r.Name) { case "outer": OuterDuration = new MNXDurationSymbol(r.Value); break; case "inner": InnerDuration = new MNXDurationSymbol(r.Value); break; case "orient": Orient = GetMNXOrientation(r.Value); break; case "staff": int staff; int.TryParse(r.Value, out staff); if (staff > 0) { Staff = staff; } break; case "show-number": ShowNumber = GetTupletNumberDisplay(r.Value); break; case "show-value": ShowValue = GetTupletNumberDisplay(r.Value); break; case "bracket": Bracket = GetTupletBracketDisplay(r.Value); break; default: throw new ApplicationException("Unknown attribute"); } } M.ReadToXmlElementTag(r, "event", "grace", "forward", "tuplet"); while (r.Name == "event" || r.Name == "grace" || r.Name == "forward" || r.Name == "tuplet") { if (r.Name == "tuplet" && r.NodeType == XmlNodeType.EndElement) { break; // pop 1 level } if (r.NodeType != XmlNodeType.EndElement) { switch (r.Name) { case "event": Event e = new Event(r); Components.Add(e); break; case "grace": Grace grace = new Grace(r); Components.Add(grace); break; case "forward": Forward forward = new Forward(r); Components.Add(forward); break; case "tuplet": TupletDef tuplet = new TupletDef(r, false); Components.Add(tuplet); break; } } M.ReadToXmlElementTag(r, "event", "grace", "forward", "tuplet"); } M.Assert(IEventsAndGraces.Count > 0); M.Assert(r.Name == "tuplet"); // end of (nested) tuplet content if (_isTopLevel) { int outerTicks = this.OuterDuration.GetDefaultTicks(); this.OuterDuration.TicksDuration = outerTicks; SetTicksDurationsInContentIgnoringGraces(outerTicks); } }
public Event(XmlReader r) { M.Assert(r.Name == "event"); // TicksDuration is initally be related to the default ticksDuration of the MNXDurationSymbol, // but it changes if this event is part of a tuplet, and again when the file has been completely // read, to accomodate Grace notes. // TicksPosInScore is set correctly when the complete file has been parsed. TicksPosInScore = 0; int count = r.AttributeCount; for (int i = 0; i < count; i++) { r.MoveToAttribute(i); switch (r.Name) { case "value": MNXDurationSymbol = new MNXDurationSymbol(r.Value); break; case "measure": M.ThrowError("Not Implemented"); break; case "orient": M.ThrowError("Not Implemented"); break; case "staff": M.ThrowError("Not Implemented"); break; case "duration": TicksOverride = new MNXDurationSymbol(r.Value); break; case "id": ID = r.Value; break; } } // extend the contained elements as necessary.. M.ReadToXmlElementTag(r, "note", "rest", "slur"); while (r.Name == "note" || r.Name == "rest" || r.Name == "slur") { if (r.NodeType != XmlNodeType.EndElement) { switch (r.Name) { case "note": if (Notes == null && Rest == null) { Notes = new List <Note>(); } Notes.Add(new Note(r)); break; case "rest": if (Notes == null && Rest == null) { Rest = new Rest(r); } break; case "slur": if (SlurDefs == null) { SlurDefs = new List <SlurDef>(); } SlurDefs.Add(new SlurDef(r)); break; } } M.ReadToXmlElementTag(r, "note", "rest", "slur", "event"); } M.Assert(r.Name == "event"); // end of event }