/* quantize the start time */ private static void QuantizeStartTime( int StartTime, int QuarterNoteLen, NoteFlags QuantizedDuration, double QuantizedDurationAdjust, out FractionRec StartTimeOut, out double StartTimeAdjustOut, out double WholeNoteStartTimeAdjust) { uint Denominator; double QuantizedStartTime; double OrigDuration; FractionRec OrigDurationFractional; /* start times must be a multiple of the 64th note. see rationale */ /* in comment at top of this file. */ Denominator = 64; NoteNoteObjectRec.ConvertDurationFrac(QuantizedDuration, out OrigDurationFractional); OrigDuration = (4 * QuarterNoteLen) * QuantizedDurationAdjust * FractionRec.Fraction2Double(OrigDurationFractional); /* compute start time to nearest division they allow */ FractionRec.Double2Fraction(StartTime / (double)(QuarterNoteLen * 4), Denominator, out StartTimeOut); /* set start time adjust (relative to duration) */ QuantizedStartTime = FractionRec.Fraction2Double(StartTimeOut) * (4 * QuarterNoteLen); StartTimeAdjustOut = (QuantizedStartTime - StartTime) / OrigDuration; WholeNoteStartTimeAdjust = (QuantizedStartTime - StartTime) / (4 * QuarterNoteLen); }
/* convert fraction to a double */ public static double Fraction2Double(FractionRec Fraction) { if (Fraction.Fraction >= Fraction.Denominator) { // numerator is larger than denominator Debug.Assert(false); throw new ArgumentException(); } return(Fraction.Integer + ((double)Fraction.Fraction / Fraction.Denominator)); }
/* get comment event information. *MessageActualOut is the actual heap block. */ /* fields may be NIL if value is not needed */ public static void GetQuantizedCommentEventInfo( QuantEventRec Event, out FractionRec StartTimeOut, out double StartTimeAdjustOut, out string CommentStringOut) { Debug.Assert(Event.Type == QuantEventType.eQuantizedCommentEvent); StartTimeOut = Event.StartTime; StartTimeAdjustOut = Event.StartTimeAdjust; CommentStringOut = Event.CommentString; }
/* create new interval comment. comment string is a heap allocated */ /* non-null-terminated string; onwership of string goes to the object */ public static QuantEventRec NewQuantizedCommentEvent( FractionRec StartTime, double StartTimeAdjust, string CommentString) { QuantEventRec Event = new QuantEventRec(); Event.Type = QuantEventType.eQuantizedCommentEvent; Event.StartTime = StartTime; Event.StartTimeAdjust = StartTimeAdjust; Event.CommentString = CommentString; return(Event); }
/* determine if fraction is an integer multiple of a 64th div3 */ public static bool FractionIntMultOf64thDiv3(FractionRec Fraction) { FractionRec SixtyFourthDiv3; FractionRec SixtyFourthDiv3Reciprocal; FractionRec Product; NoteNoteObjectRec.ConvertDurationFrac(NoteFlags.e64thNote | NoteFlags.eDiv3Modifier, out SixtyFourthDiv3); Debug.Assert((SixtyFourthDiv3.Integer == 0) && (SixtyFourthDiv3.Fraction != 0)); // "cute problem" FractionRec.MakeFraction(out SixtyFourthDiv3Reciprocal, 0, (int)SixtyFourthDiv3.Denominator, (int)SixtyFourthDiv3.Fraction); /* reciprocal */ FractionRec.MultFractions(Fraction, SixtyFourthDiv3Reciprocal, out Product); /* if fraction == 0 then it is an even multiple of the 64th note */ return(Product.Fraction == 0); }
/* reduce fraction */ public static void ReduceFraction(ref FractionRec Frac) { if (Frac.Fraction >= Frac.Denominator) { // numerator is larger than denominator Debug.Assert(false); throw new ArgumentException(); } uint GCF = FindCommonFactors(Frac.Fraction, Frac.Denominator); Frac.Fraction = Frac.Fraction / GCF; Frac.Denominator = Frac.Denominator / GCF; }
/* convert a decimal number to a fraction with the specified denominator limit */ public static void Double2Fraction(double Value, uint Denominator, out FractionRec Fraction) { if (Value < 0) { Debug.Assert(false); throw new ArgumentException(); } /* add half so that truncation results in value rounded to nearest denominator */ Value = Value + (double)0.5 / Denominator; uint Integer = (uint)Value; Fraction = new FractionRec(Integer, (uint)((Value - Integer) * Denominator), Denominator); ReduceFraction(ref Fraction); }
/* test to see if the left is greater than or equal to the right */ public static bool FracGreaterEqual(FractionRec Left, FractionRec Right) { if ((Left.Fraction >= Left.Denominator) || (Right.Fraction >= Right.Denominator)) { // numerator is larger than denominator Debug.Assert(false); throw new ArgumentException(); } if (Left.Integer > Right.Integer) { /* if the integer portion is bigger, then there's no contest */ return(true); } else if (Left.Integer < Right.Integer) { /* same as above */ return(false); } else { /* if the integer portions are the same, then we have to compare the */ /* fractional portions */ if (Left.Denominator == Right.Denominator) { /* if the denominators are the same, then comparison is easy */ return(Left.Fraction >= Right.Fraction); } else { uint GCF; /* if the denominators are not the same, then they have to be */ /* made the same. as before, the GCF is the factors that are */ /* common to both sides. Left->Denominator / GCF is the portion of */ /* the left that right needs and Right->Denominator / GCF is the portion */ /* of the right that left needs. We don't care about the new */ /* denominator, but we will compare the new numerators. */ GCF = FindCommonFactors(Left.Denominator, Right.Denominator); return(Left.Fraction * (Right.Denominator / GCF) >= Right.Fraction * (Left.Denominator / GCF)); } } }
/* get note event information. fields may be NIL if value is not needed */ public static void GetQuantizedNoteEventInfo( QuantEventRec Event, out FractionRec StartTimeOut, out double StartTimeAdjustOut, out NoteFlags DurationOut, out double DurationAdjustOut, out short MIDIPitchOut, out short MIDIAttackVelocityOut, out short MIDIReleaseVelocityOut) { Debug.Assert(Event.Type == QuantEventType.eQuantizedNoteEvent); StartTimeOut = Event.StartTime; StartTimeAdjustOut = Event.StartTimeAdjust; DurationOut = Event.Duration; DurationAdjustOut = Event.DurationAdjust; MIDIPitchOut = Event.MIDIPitch; MIDIAttackVelocityOut = Event.MIDIAttackVelocity; MIDIReleaseVelocityOut = Event.MIDIReleaseVelocity; }
/* insert new event sorted into the track */ public static void QuantizedTrackInsertEventSorted( QuantizedTrackRec Track, QuantEventRec Event) { FractionRec OurEventStartTime = GetQuantizedEventTime(Event); int Scan = Track.EventList.Count - 1; while (Scan >= 0) { QuantEventRec OtherEvent = Track.EventList[Scan]; FractionRec OtherEventStartTime = GetQuantizedEventTime(OtherEvent); if (FractionRec.FracGreaterEqual(OurEventStartTime, OtherEventStartTime)) { Track.EventList.Insert(Scan + 1, Event); return; } Scan -= 1; } Track.EventList.Insert(0, Event); }
/* create new interval note */ public static QuantEventRec NewQuantizedNoteEvent( FractionRec StartTime, double StartTimeAdjust, NoteFlags Duration, double DurationAdjust, short MIDIPitch, short MIDIAttackVelocity, short MIDIReleaseVelocity) { QuantEventRec Event = new QuantEventRec(); Event.Type = QuantEventType.eQuantizedNoteEvent; Event.StartTime = StartTime; Event.StartTimeAdjust = StartTimeAdjust; Event.Duration = Duration; Event.DurationAdjust = DurationAdjust; Event.MIDIPitch = MIDIPitch; Event.MIDIAttackVelocity = MIDIAttackVelocity; Event.MIDIReleaseVelocity = MIDIReleaseVelocity; Event.TieTarget = null; return(Event); }
/* compute duration opcode for maximum atomic note that is less than or */ /* equal to the specified fractional duration */ private static NoteFlags GetMaxDurationOpcode( FractionRec TargetDuration, out FractionRec ComputedDuration, OpDurRec[] OpcodeDurationTable, int OpcodeDurationTableLength) { int Scan; for (Scan = 0; Scan < OpcodeDurationTableLength; Scan += 1) { if (!FractionRec.FracGreaterThan(OpcodeDurationTable[Scan].Duration, TargetDuration)) { ComputedDuration = OpcodeDurationTable[Scan].Duration; return(OpcodeDurationTable[Scan].Opcode); } } /* probably the start time quantizer is generating intervals that */ /* can't be represented. */ Debug.Assert(false); // couldn't find available note ComputedDuration = OpcodeDurationTable[OpcodeDurationTableLength - 1].Duration; return(OpcodeDurationTable[OpcodeDurationTableLength - 1].Opcode); }
/* multiply fractions. destination can be one of the sources */ /* this function will fail on numbers considerably smaller than the */ /* range of representable fractions. */ public static void MultFractions(FractionRec Left, FractionRec Right, out FractionRec Dest) { if ((Left.Fraction >= Left.Denominator) || (Right.Fraction >= Right.Denominator)) { // numerator is larger than denominator Debug.Assert(false); throw new ArgumentException(); } ReduceFraction(ref Left); ReduceFraction(ref Right); /* the product of two fractions: A/B * C/D == AC/BD */ /* here we multiply the denominators */ uint Denominator = Left.Denominator * Right.Denominator; /* here we multiply the numerators */ uint Numerator = (Left.Integer * Left.Denominator + Left.Fraction) * (Right.Integer * Right.Denominator + Right.Fraction); /* division gives us the integer part back and the remainder is the numerator */ Dest = new FractionRec(Numerator / Denominator, Numerator % Denominator, Denominator); /* keep denominators under control */ ReduceFraction(ref Dest); }
/* make fraction. arguments do not have to be normalized */ public static void MakeFraction(out FractionRec Fraction, int Integer, int Numerator, int Denominator) { if (Denominator == 0) { Debug.Assert(false); throw new ArgumentException(); } if (Denominator < 0) { Denominator = -Denominator; Numerator = -Numerator; } int Overflow; if (Numerator < 0) { /* round down, even for negative numbers */ Overflow = (Numerator - (Denominator - 1)) / Denominator; } else { Overflow = Numerator / Denominator; } Integer += Overflow; Numerator -= Denominator * Overflow; if (Integer < 0) { // fraction is negative Debug.Assert(false); throw new ArgumentException(); } Fraction = new FractionRec((uint)Integer, (uint)Numerator, (uint)Denominator); ReduceFraction(ref Fraction); }
/* insert rests */ private static void InsertRests( FractionRec Now, FractionRec Target, TrackObjectRec NoteTrack, OpDurRec[] OpcodeDurationTable, int OpcodeDurationTableLength) { while (FractionRec.FracGreaterThan(Target, Now)) { FractionRec Difference; NoteFlags Opcode; FractionRec OpcodesDuration; NoteNoteObjectRec Note; FrameObjectRec Frame; /* how much time left */ FractionRec.SubFractions(Target, Now, out Difference); /* search for appropriate opcode */ Opcode = GetMaxDurationOpcode(Difference, out OpcodesDuration, OpcodeDurationTable, OpcodeDurationTableLength); /* add duration to Now */ FractionRec.AddFractions(Now, OpcodesDuration, out Now); /* create the note */ Note = new NoteNoteObjectRec(NoteTrack); Note.PutNoteDuration(Opcode & NoteFlags.eDurationMask); Note.PutNoteDurationDivision(Opcode & NoteFlags.eDivisionMask); Note.PutNoteDotStatus((Opcode & NoteFlags.eDotModifier) != 0); Note.PutNotePitch(Constants.CENTERNOTE); Note.PutNoteIsItARest(true); /* create the frame */ Frame = new FrameObjectRec(); Frame.Add(Note); NoteTrack.FrameArray.Add(Frame); } }
/* subtract second fraction from first. Destination can be one of the sources */ public static void SubFractions(FractionRec Left, FractionRec Right, out FractionRec Dest) { if ((Left.Fraction >= Left.Denominator) || (Right.Fraction >= Right.Denominator)) { // numerator is larger than denominator Debug.Assert(false); throw new ArgumentException(); } /* add fractional parts */ int FractionTemp; int DenominatorTemp; if (Left.Denominator == Right.Denominator) { /* if the denominators are the same, then adding is really easy */ DenominatorTemp = (int)Left.Denominator; FractionTemp = (int)Left.Fraction - (int)Right.Fraction; } else { uint GCF; /* if the denominators are not the same, then we need to multiply each */ /* side by some number so that they will be the same. finding the greatest */ /* common factor helps us find the smallest number to multiply by. */ /* Left->Denominator / GCF = the factors that left has which right needs. */ /* Right->Denominator / GCF = the factors that right has which left needs. */ GCF = FindCommonFactors(Left.Denominator, Right.Denominator); /* by multiplying the denominators together, then dividing out the extra */ /* set of common factors, we find the smallest common denominator. The */ /* division is performed inside to prevent overflow */ DenominatorTemp = (int)((Left.Denominator / GCF) * Right.Denominator); /* the left and right sides should yield the same denominator */ if (DenominatorTemp != (Right.Denominator / GCF) * Left.Denominator) { // couldn't factor denominators Debug.Assert(false); throw new ArgumentException(); } /* since we are multiplying each fraction by N/N, we need to multiply */ /* the numerators by the same thing we multiplied the denominators by. */ FractionTemp = (int)(Left.Fraction * (Right.Denominator / GCF)) - (int)(Right.Fraction * (Left.Denominator / GCF)); } /* add the integer components */ int IntegerTemp = (int)Left.Integer - (int)Right.Integer; /* if there was an overflow in the fractional part, carry it to the integer */ if (FractionTemp >= DenominatorTemp) { // overflow occurred when it shouldn't Debug.Assert(false); throw new ArgumentException(); } if (FractionTemp < 0) { /* since we are adding, the amount of carry should never be more than 1 */ FractionTemp += DenominatorTemp; IntegerTemp -= 1; } if (FractionTemp < 0) { // numerator is way too small Debug.Assert(false); throw new ArgumentException(); } /* store result */ Dest = new FractionRec((uint)IntegerTemp, (uint)FractionTemp, (uint)DenominatorTemp); }
/* convert interval track into quantized track. */ public static bool ConvertIntervalToQuantized( IntervalTrackRec IntervalTrack, QuantizedTrackRec QuantizedTrack, int MidiQuarterNote) { int Scan; int Limit; TimeMatchRec[] MatchingTable = new TimeMatchRec[4 /*divisions*/ * 2 /*dot*/ * 9 /*notetypes*/]; int MatchingTableLength; /* build duration matching table */ MatchingTableLength = 0; for (Scan = 0; Scan < 4 /*divisions*/ * 2 /*dot*/ * 9 /*notetypes*/; Scan += 1) { double MIDIClocks; NoteFlags Descriptor; FractionRec DurationFraction; /* determine root duration and descriptor */ switch (Scan % 9) { default: Debug.Assert(false); throw new InvalidOperationException(); case 0: Descriptor = NoteFlags.e64thNote; break; case 1: Descriptor = NoteFlags.e32ndNote; break; case 2: Descriptor = NoteFlags.e16thNote; break; case 3: Descriptor = NoteFlags.e8thNote; break; case 4: Descriptor = NoteFlags.e4thNote; break; case 5: Descriptor = NoteFlags.e2ndNote; break; case 6: Descriptor = NoteFlags.eWholeNote; break; case 7: Descriptor = NoteFlags.eDoubleNote; break; case 8: Descriptor = NoteFlags.eQuadNote; break; } /* determine if dot is needed */ if (((Scan / 9) % 2) != 0) { /* dot needed */ Descriptor |= NoteFlags.eDotModifier; } /* determine what division is needed */ switch (((Scan / 9) / 2) % 4) { default: Debug.Assert(false); throw new InvalidOperationException(); case 0: break; case 1: Descriptor |= NoteFlags.eDiv3Modifier; break; case 2: Descriptor |= NoteFlags.eDiv5Modifier; break; case 3: Descriptor |= NoteFlags.eDiv7Modifier; break; } /* how int is this note */ NoteNoteObjectRec.ConvertDurationFrac(Descriptor, out DurationFraction); /* units of whole notes */ MIDIClocks = MidiQuarterNote * (4 * FractionRec.Fraction2Double(DurationFraction)); /* add to table if note can be represented in the timing scheme */ /* AND only if note is an integer multiple of the 64th div3 (see */ /* comment at the top of this file for the rationale) */ if ((MIDIClocks == Math.Floor(MIDIClocks)) && (MIDIClocks >= 1) && IntMultOf64thDiv3(Descriptor)) { MatchingTable[MatchingTableLength].Ticks = (int)MIDIClocks; MatchingTable[MatchingTableLength].Descriptor = Descriptor; MatchingTableLength += 1; } } /* quantize note events */ Limit = GetIntervalTrackLength(IntervalTrack); for (Scan = 0; Scan < Limit; Scan += 1) { IntEventRec Event; Event = GetIntervalTrackIndexedEvent(IntervalTrack, Scan); switch (IntervalEventGetType(Event)) { default: Debug.Assert(false); throw new InvalidOperationException(); case IntEventType.eIntervalNoteEvent: { /* input values */ int StartTime; int Duration; short MIDIPitch; short MIDIAttackVelocity; short MIDIReleaseVelocity; /* output values */ NoteFlags QuantizedDuration; double QuantizedDurationAdjust; int WholeNoteOverflow; FractionRec QuantizedStartTime; double QuantizedStartTimeAdjust; double WholeNoteStartTimeAdjust; /* auxiliary value */ /* stuff */ QuantEventRec QuantEvent; /* get the information */ GetIntervalNoteEventInfo(Event, out StartTime, out Duration, out MIDIPitch, out MIDIAttackVelocity, out MIDIReleaseVelocity); /* quantize duration */ QuantizeDuration(Duration, MidiQuarterNote, MatchingTable, MatchingTableLength, out QuantizedDuration, out QuantizedDurationAdjust, out WholeNoteOverflow); Debug.Assert(IntMultOf64thDiv3(QuantizedDuration)); // non-64div3 duration quantization? /* quantize start time */ QuantizeStartTime(StartTime, MidiQuarterNote, QuantizedDuration, QuantizedDurationAdjust, out QuantizedStartTime, out QuantizedStartTimeAdjust, out WholeNoteStartTimeAdjust); Debug.Assert(FractionIntMultOf64thDiv3(QuantizedStartTime)); // non-64div3 start time quantization? /* bump start time to end of whole note chain */ QuantizedStartTime.Integer += (uint)WholeNoteOverflow; /* create new event & insert into track */ QuantEvent = NewQuantizedNoteEvent(QuantizedStartTime, QuantizedStartTimeAdjust, QuantizedDuration, QuantizedDurationAdjust, MIDIPitch, MIDIAttackVelocity, MIDIReleaseVelocity); QuantizedTrackInsertEventSorted(QuantizedTrack, QuantEvent); /* insert whole notes behind the last note in reverse order */ while (WholeNoteOverflow > 0) { QuantEventRec Predecessor; /* create preceding whole note */ QuantizedStartTime.Integer -= 1; Predecessor = NewQuantizedNoteEvent(QuantizedStartTime, WholeNoteStartTimeAdjust, NoteFlags.eWholeNote, 1, MIDIPitch, MIDIAttackVelocity, MIDIReleaseVelocity); QuantizedTrackInsertEventSorted(QuantizedTrack, Predecessor); /* set tie */ PutQuantizedEventTieTarget(Predecessor, QuantEvent); QuantEvent = Predecessor; /* step */ WholeNoteOverflow -= 1; } } break; case IntEventType.eIntervalCommentEvent: { QuantEventRec QuantEvent; /* input values */ int StartTime; string OriginalString; /* output values */ FractionRec QuantizedStartTime; /* get the information */ GetIntervalCommentEventInfo(Event, out StartTime, out OriginalString); /* compute start time to nearest 64th div3 */ FractionRec.Double2Fraction(StartTime / (double)MidiQuarterNote, 64 * 3, out QuantizedStartTime); Debug.Assert(FractionIntMultOf64thDiv3(QuantizedStartTime)); //non-64div3 start time quantization? /* create new event & insert into track */ QuantEvent = NewQuantizedCommentEvent(QuantizedStartTime, 0, OriginalString); QuantizedTrackInsertEventSorted(QuantizedTrack, QuantEvent); } break; } } return(true); }
/* fill in opcode table and sort for largest-duration first */ private static void InitializeOpcodeDurationTable( OpDurRec[] Table, // [4/*divisions*/ * 2/*dot*/ * 9/*notetypes*/] out int TableLengthOut) { Debug.Assert(Table.Length == 4 /*divisions*/ * 2 /*dot*/ * 9 /*notetypes*/); /* scan all possible notes */ TableLengthOut = 0; for (int Scan = 0; Scan < 4 /*divisions*/ * 2 /*dot*/ * 9 /*notetypes*/; Scan += 1) { NoteFlags Descriptor; FractionRec DurationFraction; int InsertScan; int EndScan; /* determine root duration and descriptor */ switch (Scan % 9) { default: Debug.Assert(false); throw new InvalidOperationException(); case 0: Descriptor = NoteFlags.e64thNote; break; case 1: Descriptor = NoteFlags.e32ndNote; break; case 2: Descriptor = NoteFlags.e16thNote; break; case 3: Descriptor = NoteFlags.e8thNote; break; case 4: Descriptor = NoteFlags.e4thNote; break; case 5: Descriptor = NoteFlags.e2ndNote; break; case 6: Descriptor = NoteFlags.eWholeNote; break; case 7: Descriptor = NoteFlags.eDoubleNote; break; case 8: Descriptor = NoteFlags.eQuadNote; break; } /* determine if dot is needed */ if (((Scan / 9) % 2) != 0) { /* dot needed */ Descriptor |= NoteFlags.eDotModifier; } /* determine what division is needed */ switch (((Scan / 9) / 2) % 4) { default: Debug.Assert(false); throw new InvalidOperationException(); case 0: break; case 1: Descriptor |= NoteFlags.eDiv3Modifier; break; case 2: Descriptor |= NoteFlags.eDiv5Modifier; break; case 3: Descriptor |= NoteFlags.eDiv7Modifier; break; } /* don't use ugly rests, except we need the 64th div3 (see below) */ if (!(((Descriptor & NoteFlags.eDivisionMask) == NoteFlags.eDiv5Modifier) || ((Descriptor & NoteFlags.eDivisionMask) == NoteFlags.eDiv7Modifier) || ((Descriptor & NoteFlags.eDotModifier) != 0) || (((Descriptor & NoteFlags.eDivisionMask) == NoteFlags.eDiv3Modifier) && ((Descriptor & NoteFlags.eDurationMask) != NoteFlags.e64thNote)))) { /* don't use things that aren't multiples of a 64th div3 note. see the */ /* comment at the top of ConvertIntToQuant for the rationale. */ if (IntMultOf64thDiv3(Descriptor)) { /* get duration, units of whole notes */ NoteNoteObjectRec.ConvertDurationFrac(Descriptor, out DurationFraction); /* add duration to table */ InsertScan = 0; while (InsertScan < TableLengthOut) { if (FractionRec.FracGreaterThan(DurationFraction, Table[InsertScan].Duration)) { /* insert here */ goto InsertNowPoint; } else if (FractionRec.FractionsEqual(DurationFraction, Table[InsertScan].Duration)) { /* redundant */ goto DoneInsertingPoint; } else { /* try the next one */ InsertScan += 1; } } /* this gets executed to insert a new value before Table[InsertScan] */ InsertNowPoint: for (EndScan = TableLengthOut - 1; EndScan >= InsertScan; EndScan -= 1) { Table[EndScan + 1] = Table[EndScan]; } Table[InsertScan].Opcode = Descriptor; Table[InsertScan].Duration = DurationFraction; TableLengthOut++; DoneInsertingPoint: ; } } } /* verify sort order */ #if DEBUG for (int Scan = 0; Scan < TableLengthOut - 1; Scan += 1) { if (!FractionRec.FracGreaterThan(Table[Scan].Duration, Table[Scan + 1].Duration)) { // sort failure Debug.Assert(false); throw new InvalidOperationException(); } } #endif }
/* convert quantized track into native track */ public static void ConvertQuantToNote( QuantizedTrackRec QuantizedTrack, TrackObjectRec NoteTrack) { FractionRec CurrentTime; int Index; int Limit; List <QuantEventRec> FrameArray; TieMappingRec TieMapping; int OpcodeDurationTableLength; OpDurRec[] OpcodeDurationTable = new OpDurRec[4 /*divisions*/ * 2 /*dot*/ * 9 /*notetypes*/]; /* initialize variables */ InitializeOpcodeDurationTable(OpcodeDurationTable, out OpcodeDurationTableLength); CurrentTime.Integer = 0; CurrentTime.Fraction = 0; CurrentTime.Denominator = 1; FrameArray = new List <QuantEventRec>(); TieMapping = NewTieMapping(); Limit = GetQuantizedTrackLength(QuantizedTrack); Index = 0; /* iterate over variables */ while (Index < Limit) { FractionRec NextTime; QuantEventRec QuantEvent; bool Continue; int InspectScan; /* reset frame array */ FrameArray.Clear(); /* get the start time of the next available event */ QuantEvent = GetQuantizedTrackIndexedEvent(QuantizedTrack, Index); NextTime = GetQuantizedEventTime(QuantEvent); Debug.Assert(FractionIntMultOf64thDiv3(NextTime)); // non-64div3 start time quantization? /* sanity check */ Debug.Assert(!FractionRec.FracGreaterThan(CurrentTime, NextTime)); // next time inconsistency /* get all events starting at this time into FrameArray */ Continue = true; while (Continue && (Index < Limit)) { FractionRec EventTime; /* get the event */ QuantEvent = GetQuantizedTrackIndexedEvent(QuantizedTrack, Index); EventTime = GetQuantizedEventTime(QuantEvent); Debug.Assert(FractionIntMultOf64thDiv3(EventTime)); // non-64div3 start time quantization? if (FractionRec.FractionsEqual(EventTime, NextTime)) { /* add event to the list */ FrameArray.Add(QuantEvent); /* go past this event */ Index += 1; } else { /* end hit so stop */ Continue = false; } } /* insert rests to bring current time up to next time */ InsertRests(CurrentTime, NextTime, NoteTrack, OpcodeDurationTable, OpcodeDurationTableLength); /* remove command events from list */ InspectScan = 0; while (InspectScan < FrameArray.Count) { /* get the event */ QuantEvent = FrameArray[InspectScan]; /* determine if event is a command */ if (QuantizedEventGetType(QuantEvent) == QuantEventType.eQuantizedNoteEvent) { /* note events should be skipped */ InspectScan += 1; } else { /* command events should be handled */ switch (QuantizedEventGetType(QuantEvent)) { default: Debug.Assert(false); throw new InvalidOperationException(); case QuantEventType.eQuantizedCommentEvent: { string CommentString; CommandNoteObjectRec Note; FrameObjectRec Frame; FractionRec unusedf; double unusedd; GetQuantizedCommentEventInfo(QuantEvent, out unusedf, out unusedd, out CommentString); Note = new CommandNoteObjectRec(NoteTrack); Note.PutCommandStringArg1(CommentString); Note.PutCommandOpcode(NoteCommands.eCmdMarker); Frame = new FrameObjectRec(); Frame.Add(Note); NoteTrack.FrameArray.Add(Frame); } break; } /* delete event from array */ FrameArray.RemoveAt(InspectScan); /* don't increment InspectScan */ } } /* process remaining notes in FrameArray, computing minimum duration */ /* and updating CurrentTime with minimum duration. */ if (FrameArray.Count != 0) { NoteFlags DurationOpcode; FrameObjectRec Frame; int Scan; int FrameLimit; FractionRec MinimumDuration; FractionRec unusedf; double unusedd; short unuseds; /* initialize minimum duration */ QuantEvent = FrameArray[0]; GetQuantizedNoteEventInfo(QuantEvent, out unusedf, out unusedd, out DurationOpcode, out unusedd, out unuseds, out unuseds, out unuseds); NoteNoteObjectRec.ConvertDurationFrac(DurationOpcode, out MinimumDuration); Debug.Assert(FractionIntMultOf64thDiv3(MinimumDuration)); // non-64div3 duration quantization? /* allocate frame */ Frame = new FrameObjectRec(); /* process notes in frame */ FrameLimit = FrameArray.Count; for (Scan = 0; Scan < FrameLimit; Scan += 1) { FractionRec StartTime; double StartTimeAdjust; NoteFlags Duration; double DurationAdjust; short MIDIPitch; short MIDIAttackVelocity; short MIDIReleaseVelocity; NoteNoteObjectRec Note; FractionRec FracDuration; /* get the note */ QuantEvent = FrameArray[Scan]; Debug.Assert(QuantizedEventGetType(QuantEvent) == QuantEventType.eQuantizedNoteEvent); // non-note in frame array /* get attributes */ GetQuantizedNoteEventInfo(QuantEvent, out StartTime, out StartTimeAdjust, out Duration, out DurationAdjust, out MIDIPitch, out MIDIAttackVelocity, out MIDIReleaseVelocity); Debug.Assert(IntMultOf64thDiv3(Duration)); // non-64div3 duration quantization? Debug.Assert(FractionRec.FractionsEqual(StartTime, CurrentTime)); // start time inconsistency /* create note */ Note = new NoteNoteObjectRec(NoteTrack); Frame.Add(Note); TieMappingAddPair(TieMapping, QuantEvent, Note); /* set note attributes */ Note.PutNoteDuration(Duration & NoteFlags.eDurationMask); Note.PutNoteDurationDivision(Duration & NoteFlags.eDivisionMask); Note.PutNoteDotStatus((Duration & NoteFlags.eDotModifier) != 0); Note.EarlyLateAdjust = StartTimeAdjust; Note.DurationAdjust = DurationAdjust; Note.DurationAdjustMode = NoteFlags.eDurationAdjustMultiplicative; Note.PutNotePitch((short)(MIDIPitch - MIDIC + Constants.CENTERNOTE)); Note.Accent1 = (MIDIAttackVelocity > 0) ? -Math.Log(MIDIAttackVelocity) / Constants.LOG2 + LN127OVERLN2 : 7; Note.Accent2 = (MIDIReleaseVelocity > 0) ? -Math.Log(MIDIReleaseVelocity) / Constants.LOG2 + LN127OVERLN2 : 7; switch ((MIDIPitch - MIDIC + ((MIDIC / 12 + 1) * 12)) % 12) { default: // midi sharp/flat problem Debug.Assert(false); throw new InvalidOperationException(); case 0: /* C */ case 2: /* D */ case 4: /* E */ case 5: /* F */ case 7: /* G */ case 9: /* A */ case 11: /* B */ break; case 1: /* C# */ case 3: /* D# */ case 6: /* F# */ case 8: /* G# */ case 10: /* A# */ Note.PutNoteFlatOrSharpStatus(NoteFlags.eSharpModifier); break; } /* do the minimum duration thing */ NoteNoteObjectRec.ConvertDurationFrac(Duration, out FracDuration); Debug.Assert(FractionIntMultOf64thDiv3(FracDuration)); // non-64div3 duration quantization? if (FractionRec.FracGreaterThan(MinimumDuration, FracDuration)) { MinimumDuration = FracDuration; } } /* add frame to track */ NoteTrack.FrameArray.Add(Frame); /* if minimum duration is greater than time to next event, then */ /* add rests (one to this frame) to fill in the gap */ if (Index < Limit) { FractionRec NextEventTime; FractionRec Difference; /* get the start time of the next available event */ QuantEvent = GetQuantizedTrackIndexedEvent(QuantizedTrack, Index); NextEventTime = GetQuantizedEventTime(QuantEvent); Debug.Assert(FractionIntMultOf64thDiv3(NextEventTime)); // non-64div3 start time quantization? FractionRec.SubFractions(NextEventTime, CurrentTime, out Difference); if (FractionRec.FracGreaterThan(MinimumDuration, Difference)) { NoteNoteObjectRec Note; NoteFlags RestOpcode; FractionRec OpcodesDuration; /* insert first rest into frame */ RestOpcode = GetMaxDurationOpcode(Difference, out OpcodesDuration, OpcodeDurationTable, OpcodeDurationTableLength); Debug.Assert(IntMultOf64thDiv3(RestOpcode)); // non-64div3 duration quantization Note = new NoteNoteObjectRec(NoteTrack); Note.PutNoteDuration(RestOpcode & NoteFlags.eDurationMask); Note.PutNoteDurationDivision(RestOpcode & NoteFlags.eDivisionMask); Note.PutNoteDotStatus((RestOpcode & NoteFlags.eDotModifier) != 0); Note.PutNotePitch(Constants.CENTERNOTE); Note.PutNoteIsItARest(true); Frame.Add(Note); /* put new minimum duration in to reflect new rest we added */ NoteNoteObjectRec.ConvertDurationFrac(RestOpcode, out MinimumDuration); } } /* advance thing by minimum duration */ FractionRec.AddFractions(MinimumDuration, CurrentTime, out CurrentTime); Debug.Assert(FractionIntMultOf64thDiv3(CurrentTime)); // non-64div3 start time quantization? } } /* patch up ties */ for (Index = 0; Index < Limit; Index += 1) { QuantEventRec QuantEvent; /* get potential event */ QuantEvent = GetQuantizedTrackIndexedEvent(QuantizedTrack, Index); /* see if it ties somewhere */ if ((QuantEventType.eQuantizedNoteEvent == QuantizedEventGetType(QuantEvent)) && (GetQuantizedEventTieTarget(QuantEvent) != null)) { QuantEventRec TieTarget; NoteNoteObjectRec Source; NoteNoteObjectRec Target; /* get tie target */ TieTarget = GetQuantizedEventTieTarget(QuantEvent); /* look up source and target note events */ Source = TieMappingLookup(TieMapping, QuantEvent); Target = TieMappingLookup(TieMapping, TieTarget); /* establish tie */ Source.PutNoteTieTarget(Target); } } /* look for track name comment */ for (Index = 0; Index < Limit; Index += 1) { QuantEventRec QuantEvent; /* get potential event */ QuantEvent = GetQuantizedTrackIndexedEvent(QuantizedTrack, Index); /* see if it ties somewhere */ if (QuantEventType.eQuantizedCommentEvent == QuantizedEventGetType(QuantEvent)) { string CommentString; FractionRec unusedf; double unusedd; GetQuantizedCommentEventInfo(QuantEvent, out unusedf, out unusedd, out CommentString); /* check for track name */ if ((CommentString.Length > 11 /*Prefix*/ + Environment.NewLine.Length) && CommentString.StartsWith("Track Name" + Environment.NewLine)) { string NameString = CommentString.Substring(11, CommentString.Length - (11 + 1)); NoteTrack.Name = NameString; goto FinishedSettingTrackName; } } } /* if no track name was found, then use the first comment string */ for (Index = 0; Index < Limit; Index += 1) { QuantEventRec QuantEvent; /* get potential event */ QuantEvent = GetQuantizedTrackIndexedEvent(QuantizedTrack, Index); /* see if it ties somewhere */ if (QuantEventType.eQuantizedCommentEvent == QuantizedEventGetType(QuantEvent)) { string CommentString; FractionRec unusedf; double unusedd; GetQuantizedCommentEventInfo(QuantEvent, out unusedf, out unusedd, out CommentString); /* check for track name */ if ((CommentString.Length > 8 /*Prefix*/ + Environment.NewLine.Length) && CommentString.StartsWith("Comment" + Environment.NewLine)) { string NameString; NameString = CommentString.Substring(8, CommentString.Length - (8 + 1)); NoteTrack.Name = NameString; goto FinishedSettingTrackName; } } } FinishedSettingTrackName: ; }