/// <summary> /// Used by Clone. /// </summary> public Grp(Gamut gamut, int midiChannel, int msPositionReContainer, List<IUniqueDef> clonedIUDs) : base(midiChannel, msPositionReContainer, clonedIUDs) { Debug.Assert(gamut != null && gamut.ContainsAllPitches(clonedIUDs)); _gamut = gamut; SetBeamEnd(); }
//rootOctave = 4; //pitchesPerChord = 6; //msDurationPerChord = 200; // dummy, durations are set from pitches below in the ctor //velocityFactor = 0.5; // dummy, velocities are set from absolute pitches below in the ctor /// <summary> /// An exception will be thrown if the gamut argument is null. /// </summary> /// <param name="gamut"></param> public TenorPaletteGrp(Gamut gamut) : base(gamut, 4, 6, 200, gamut.NPitchesPerOctave, 0.5) { _minimumVelocity = 20; _maximumVelocity = 127; _velocityPerAbsolutePitch = gamut.GetVelocityPerAbsolutePitch(_minimumVelocity, _maximumVelocity); base.SetVelocityPerAbsolutePitch(_velocityPerAbsolutePitch, (byte)_minimumVelocity); int minMsDuration = 200; int maxMsDuration = 300; SetDurationsFromPitches(maxMsDuration, minMsDuration, true); }
/// <summary> /// A MidiChordDef having msDuration, and containing an ornament having BasicMidiChordDefs with nPitchesPerChord. /// The notated pitch and the pitch of BasicMidiChordDefs[0] are set to rootNotatedPitch. /// The notated velocity of all pitches is set to 127. /// The root pitches of the BasicMidiChordDefs begin with rootNotatedPitch, and follow the ornamentEnvelope, using /// the ornamentEnvelope's values as indices in the gamut. Their durations are as equal as possible, to give the /// overall msDuration. If ornamentEnvelope is null, a single, one-note BasicMidiChordDef will be created. /// This constructor uses Gamut.GetChord(rootNotatedPitch, nPitchesPerChord) which returns pitches that are /// vertically spaced differently according to the absolute height of the rootNotatedPitch. The number of pitches /// in a chord may also be less than nPitchesPerChord (see gamut.GetChord(...) ). /// An exception is thrown if rootNotatedPitch is not in the gamut. /// </summary> /// <param name="msDuration">The duration of this MidiChordDef</param> /// <param name="gamut">The gamut containing all the pitches.</param> /// <param name="rootNotatedPitch">The lowest notated pitch. Also the lowest pitch of BasicMidiChordDefs[0].</param> /// <param name="nPitchesPerChord">The chord density (some chords may have less pitches).</param> /// <param name="ornamentEnvelope">The ornament definition.</param> public MidiChordDef(int msDuration, Gamut gamut, int rootNotatedPitch, int nPitchesPerChord, Envelope ornamentEnvelope = null) : base(msDuration) { NotatedMidiPitches = gamut.GetChord(rootNotatedPitch, nPitchesPerChord); var nmVelocities = new List<byte>(); foreach(byte pitch in NotatedMidiPitches) // can be less than nPitchesPerChord { nmVelocities.Add(127); } NotatedMidiVelocities = nmVelocities; // Sets BasicMidiChords. If ornamentEnvelope == null, BasicMidiChords[0] is set to the NotatedMidiChord. SetOrnament(gamut, ornamentEnvelope); }
/// <summary> /// Grp objects own unique IUniqueDefs, but can share Gamuts. The Gamut may not be null. /// </summary> /// <param name="gamut">can not be null</param> /// <param name="rootOctave">must be greater than or equal to 0</param> /// <param name="nPitchesPerChord">must be greater than 0</param> /// <param name="msDurationPerChord">must be greater than 0</param> /// <param name="nChords">must be greater than 0</param> /// <param name="velocityFactor">must be greater than 0.0</param> public Grp(Gamut gamut, int rootOctave, int nPitchesPerChord, int msDurationPerChord, int nChords, double velocityFactor) : base(0, 0, new List<IUniqueDef>()) { Debug.Assert(gamut != null); Debug.Assert(rootOctave >= 0); Debug.Assert(nPitchesPerChord > 0); Debug.Assert(msDurationPerChord > 0); Debug.Assert(nChords > 0); Debug.Assert(velocityFactor > 0.0); _gamut = gamut; for(int i = 0; i < nChords; ++i) { int rootNotatedPitch; if(i == 0) { rootNotatedPitch = gamut.AbsolutePitchHierarchy[i] + (12 * rootOctave); rootNotatedPitch = (rootNotatedPitch <= gamut.MaxPitch) ? rootNotatedPitch : gamut.MaxPitch; } else { List<byte> previousPitches = ((MidiChordDef)_uniqueDefs[i - 1]).BasicMidiChordDefs[0].Pitches; if(previousPitches.Count > 1) { rootNotatedPitch = previousPitches[1]; } else { rootNotatedPitch = gamut.AbsolutePitchHierarchy[i]; while(rootNotatedPitch < previousPitches[0]) { rootNotatedPitch += 12; if(rootNotatedPitch > gamut.MaxPitch) { rootNotatedPitch = gamut.MaxPitch; break; } } } } MidiChordDef mcd = new MidiChordDef(msDurationPerChord, gamut, rootNotatedPitch, nPitchesPerChord, null); mcd.AdjustVelocities(velocityFactor); _uniqueDefs.Add(mcd); } SetBeamEnd(); }
/// <summary> /// A BasicMidiChordDef having density notes. Absent fields are set to 0 or null. /// Note that the number of pitches returned can be less than nPitches. Pitches that would be higher than 127 are /// simply not added to the returned list. /// All pitches are given velocity = 127. /// The pitches are found using the function gamut.GetChord(rootPitch, density). See that function for further documentation. /// </summary> /// <param name="msDuration">The duration</param> /// <param name="gamut"></param> /// <param name="rootPitch">The lowest pitch</param> /// <param name="density">The number of pitches. The actual number created can be smaller.</param> public BasicMidiChordDef(int msDuration, Gamut gamut, int rootPitch, int density) { #region conditions Debug.Assert(density > 0 && density <= 12); Debug.Assert(rootPitch >= 0 && rootPitch <= 127); Debug.Assert(msDuration > 0); #endregion conditions _msDuration = msDuration; // read-only! Pitches = gamut.GetChord(rootPitch, density); var newVelocities = new List<byte>(); foreach(byte pitch in Pitches) // can be less than nPitchesPerChord { newVelocities.Add(127); } Velocities = newVelocities; }
/// <summary> /// Returns a new, related TenorPaletteGrp whose Gamut has the new pitchHierarchyIndex % 22. /// Throws an exception if this.Gamut == null. /// </summary> /// <param name="pitchHierarchyIndex">the pitchHierarchyIndex of the returned TenorPaletteGrp's Gamut (will be treated % 22)</param> internal TenorPaletteGrp RelatedPitchHierarchyGrp(int pitchHierarchyIndex) { pitchHierarchyIndex %= 22; Debug.Assert(Gamut != null); Gamut gamut = new Gamut(pitchHierarchyIndex, Gamut.BasePitch, Gamut.NPitchesPerOctave); TenorPaletteGrp newTenorPaletteGrp = new TenorPaletteGrp(gamut); return newTenorPaletteGrp; }
/// <summary> /// Returns a new, related TenorPaletteGrp having the new domain % 12. /// </summary> /// <param name="domain">the the number of chords in the returned TenorPaletteGrp, and the nPitchesPerOctave of its Gamut (will be treated % 12)</param> internal TenorPaletteGrp RelatedDomainGrp(int domain) { domain %= 12; Gamut gamut = new Gamut(Gamut.RelativePitchHierarchyIndex, Gamut.BasePitch, domain); TenorPaletteGrp newTenorPaletteGrp = new TenorPaletteGrp(gamut); return newTenorPaletteGrp; }
/// <summary> /// Returns a new, related TenorPaletteGrp whose Gamut has the new basePitch % 12. /// Throws an exception if this.Gamut == null. /// </summary> /// <param name="basePitch">the basePitch of the returned TenorPaletteGrp's Gamut (will be treated % 12)</param> internal TenorPaletteGrp RelatedBasePitchGrp(int basePitch) { basePitch %= 12; Debug.Assert(Gamut != null); Gamut gamut = new Gamut(Gamut.RelativePitchHierarchyIndex, basePitch, Gamut.NPitchesPerOctave); TenorPaletteGrp newTenorPaletteGrp = new TenorPaletteGrp(gamut); return newTenorPaletteGrp; }
private byte DoTranspose(byte initialValue, Gamut gamut, int steps) { int index = gamut.IndexOf(initialValue); int newIndex = index + steps; newIndex = (newIndex >= 0) ? newIndex : 0; newIndex = (newIndex < gamut.Count) ? newIndex : gamut.Count - 1; return (byte)gamut[newIndex]; }
/// <summary> /// Returns the index of a pitch in the same octave as the pitch at pitchIndexInOldGamut. /// </summary> /// <param name="oldGamut">may not be null</param> /// <param name="newGamut">may not be null</param> /// <param name="pitchIndexInOldGamut"></param> /// <returns></returns> private int GetPitchIndexInNewGamut(Gamut oldGamut, Gamut newGamut, int pitchIndexInOldGamut) { Debug.Assert(oldGamut != null && newGamut != null); int oldNPitchesPerOctave = oldGamut.NPitchesPerOctave; int octave = pitchIndexInOldGamut / oldNPitchesPerOctave; int oldPitchIndexInOctave = pitchIndexInOldGamut - (octave * oldNPitchesPerOctave); int newNPitchesPerOctave = newGamut.NPitchesPerOctave; // find the minimum angular distance between the oldPitchIndexInOctave and any newPitchIndexInOctave double oldRadians = oldPitchIndexInOctave * ((2 * Math.PI) / oldNPitchesPerOctave); int newPitchIndexInOctave = 0; double currentMinDelta = Double.MaxValue; for(int i = 0; i < newNPitchesPerOctave; ++i) { double newRadians = i * ((2 * Math.PI) / newNPitchesPerOctave); double delta1Abs = (newRadians > oldRadians) ? newRadians - oldRadians : oldRadians - newRadians; double oldRadiansPlus2pi = oldRadians + (2 * Math.PI); double delta2Abs = (newRadians > oldRadiansPlus2pi) ? newRadians - oldRadiansPlus2pi : oldRadiansPlus2pi - newRadians; double minDelta = (delta1Abs < delta2Abs) ? delta1Abs : delta2Abs; if(minDelta < currentMinDelta) { currentMinDelta = minDelta; newPitchIndexInOctave = i; } } int newPitchIndex = newPitchIndexInOctave + (octave * newNPitchesPerOctave); newPitchIndex = (newPitchIndex < newGamut.Count) ? newPitchIndex : newGamut.Count - 1; return newPitchIndex; }
/// <summary> /// All the pitches in the MidiChordDef must be contained in the gamut. /// Transposes the pitches in NotatedMidiPitches, and all BasicMidiChordDef.Pitches by /// the number of steps in the gamut. Negative values transpose down. /// The vertical velocity sequence remains unchanged except when notes are removed. /// It is not an error if Midi values would exceed the range of the gamut. /// In this case, they are silently coerced to the bottom or top notes of the gamut respectively. /// Duplicate top and bottom gamut pitches are removed. /// </summary> public void TransposeStepsInGamut(Gamut gamut, int steps) { #region conditions Debug.Assert(gamut != null); foreach(BasicMidiChordDef bmcd in BasicMidiChordDefs) { foreach(int pitch in bmcd.Pitches) { Debug.Assert(gamut.Contains(pitch)); } } #endregion conditions int bottomMostPitch = gamut[0]; int topMostPitch = gamut[gamut.Count - 1]; foreach(BasicMidiChordDef bmcd in BasicMidiChordDefs) { List<byte> pitches = bmcd.Pitches; List<byte> velocities = bmcd.Velocities; for(int i = 0; i < pitches.Count; ++i) { pitches[i] = DoTranspose(pitches[i], gamut, steps); } RemoveDuplicateNotes(pitches, velocities); } SetNotatedValuesFromFirstBMCD(); }
/// <summary> /// Sets an ornament having the shape and number of elements in the ornamentEnvelope. /// If ornamentEnvelope == null, BasicMidiChords[0] is set to the NotatedMidiChord. /// using the NotatedMidiPitches as the first chord. /// Uses the current Gamut. /// Replaces any existing ornament. /// Sets the OrnamentNumberSymbol to the number of BasicMidiChordDefs. /// </summary> /// <param name="ornamentEnvelope"></param> public void SetOrnament(Gamut gamut, Envelope ornamentEnvelope) { Debug.Assert(gamut != null); List<int> basicMidiChordRootPitches = gamut.PitchSequence(_notatedMidiPitches[0], ornamentEnvelope); // If ornamentEnvelope is null, basicMidiChordRootPitches will only contain rootNotatedpitch. BasicMidiChordDefs = new List<BasicMidiChordDef>(); foreach(int rootPitch in basicMidiChordRootPitches) { BasicMidiChordDef bmcd = new BasicMidiChordDef(1000, gamut, rootPitch, _notatedMidiPitches.Count); BasicMidiChordDefs.Add(bmcd); } this.MsDuration = _msDuration; // resets the BasicMidiChordDef msDurations. if(basicMidiChordRootPitches.Count > 1) { _ornamentNumberSymbol = basicMidiChordRootPitches.Count; } }
/// <summary> /// Calls the other SetOrnament function /// </summary> /// <param name="ornamentShape"></param> /// <param name="nOrnamentChords"></param> public void SetOrnament(Gamut gamut, IReadOnlyList<byte> ornamentShape, int nOrnamentChords) { int nPitchesPerOctave = gamut.NPitchesPerOctave; Envelope ornamentEnvelope = new Envelope(ornamentShape, 127, nPitchesPerOctave, nOrnamentChords); SetOrnament(gamut, ornamentEnvelope); }
private void OppositePitches(Gamut gamut, Gamut oppositeGamut, List<byte> pitches) { for(int i = 0; i < pitches.Count; ++i) { int pitchIndex = gamut.IndexOf(pitches[i]); // N.B. it is not necessarily true that gamut.Count == oppositeGamut.Count. pitchIndex = (pitchIndex < oppositeGamut.Count) ? pitchIndex : oppositeGamut.Count - 1; pitches[i] = (byte)oppositeGamut[pitchIndex]; } }
/// <summary> /// 1. Creates a new, opposite gamut from the argument Gamut (see Gamut.Opposite()). /// 2. Clones this MidiChordDef, and replaces the clone's pitches by the equivalent pitches in the opposite Gamut. /// 3. Returns the clone. /// </summary> public MidiChordDef Opposite(Gamut gamut) { #region conditions Debug.Assert(gamut != null); #endregion conditions int relativePitchHierarchyIndex = (gamut.RelativePitchHierarchyIndex + 11) % 22; Gamut oppositeGamut = new Gamut(relativePitchHierarchyIndex, gamut.BasePitch, gamut.NPitchesPerOctave); MidiChordDef oppositeMCD = (MidiChordDef)Clone(); #region conditions Debug.Assert(gamut[0] == oppositeGamut[0]); Debug.Assert(gamut.NPitchesPerOctave == oppositeGamut.NPitchesPerOctave); // N.B. it is not necessarily true that gamut.Count == oppositeGamut.Count. #endregion conditions // Substitute the oppositeMCD's pitches by the equivalent pitches in the oppositeGamut. OppositePitches(gamut, oppositeGamut, oppositeMCD.NotatedMidiPitches); foreach(BasicMidiChordDef bmcd in oppositeMCD.BasicMidiChordDefs) { OppositePitches(gamut, oppositeGamut, bmcd.Pitches); } return oppositeMCD; }
/// <summary> /// Creates a list of TenorPaletteGrps, each of which has the same relativePitchHierarchyIndex. /// </summary> private List<Grp> GetTenorPaletteGrpList(int relativePitchHierarchyIndex) { const int gamutBasePitch = 0; List<Grp> grps = new List<Grp>(); for(int i = 0, domain = 12; domain >= 1; --domain, ++i) // domain is both Gamut.PitchesPerOctave and nChords per Grp { Gamut gamut = new Gamut(relativePitchHierarchyIndex, gamutBasePitch, domain); TenorPaletteGrp tpg = new TenorPaletteGrp(gamut); #region begin test code 1 Shear and permute tpg.Shear(0, -1 * (gamut.NPitchesPerOctave)); tpg.SetVelocitiesForGamut(); if(domain % 2 != 0) { tpg.Permute(1, 7); } #endregion end test code 1 #region begin test code 2 transpose chords to the same absolute root pitch //for(int iudIndex = 0; iudIndex < tpg.Count; ++iudIndex) //{ // tpg.TransposeChordDownToAbsolutePitch(iudIndex, 0); //} #endregion end test code 2 #region begin test code 3, adjust velocities //if(domain % 2 != 0) //{ // tpg.AdjustVelocities(0.5); //} #endregion #region begin test code 4, adjust velocities //if(domain % 2 != 0 && tpg.Count > 1) //{ // tpg.AdjustVelocitiesHairpin(0, tpg.Count - 1, 0.5, 1.0); //} #endregion #region begin test code 5, related Grps //if(domain % 2 != 0 && tpg.Count > 1) //{ // TenorPaletteGrp previousTpg = (TenorPaletteGrp)grps[i - 1]; // //tpg = previousTpg.RelatedPitchHierarchyGrp(previousTpg.Gamut.RelativePitchHierarchyIndex + 11); // //tpg = previousTpg.RelatedBasePitchGrp(11); // tpg = previousTpg.RelatedDomainGrp(6); //} #endregion #region begin test code 6, timeWarp //if(domain % 2 != 0 && tpg.Count > 1) //{ // tpg.TimeWarp(new Envelope(new List<int>() { 4, 7, 2 }, 7, 7, tpg.Count), 20); //} #endregion #region begin test code 7, SetPitchWheelSliders //Envelope env = new Envelope(new List<int>() { 0,8 }, 8, 127, tpg.Count); //tpg.SetPitchWheelSliders(env); #endregion #region begin test code 8, SetPanGliss //if(tpg.Count > 1) //{ // if(domain % 2 != 0) // { // tpg.SetPanGliss(0, tpg.Count - 1, 127, 0); // } // else // { // tpg.SetPanGliss(0, tpg.Count - 1, 0, 127); // } //} #endregion #region begin test code 8, set inverse velocities //if(domain % 2 != 0 && tpg.Count > 1) //{ // TenorPaletteGrp prevTpg = (TenorPaletteGrp)grps[i - 1]; // Gamut prevGamut = prevTpg.Gamut; // tpg = new TenorPaletteGrp(prevGamut); // identical to prevTpg // // inverse velocityPerAbsolutePitch // List<byte> velocityPerAbsolutePitch = prevGamut.GetVelocityPerAbsolutePitch(20, 127, prevGamut.NPitchesPerOctave - 1); // tpg.SetVelocityPerAbsolutePitch(velocityPerAbsolutePitch, 20); //} #endregion #region begin test code 8, set Gamut (pitches //if(domain % 2 != 0 && tpg.Count > 1) //{ // TenorPaletteGrp prevTpg = (TenorPaletteGrp)grps[i - 1]; // Gamut prevGamut = prevTpg.Gamut; // tpg = new TenorPaletteGrp(prevGamut); // identical to prevTpg // int newRelativePitchHierarchyIndex = prevGamut.RelativePitchHierarchyIndex + 11; // int newBasePitch = prevGamut.BasePitch; // int newNPitchesPerOctave = 8; // Gamut gamut1 = new Gamut(newRelativePitchHierarchyIndex, newBasePitch, newNPitchesPerOctave); // tpg.Gamut = gamut1; // sets the pitches, velocities are still those of the original pitches. // // reverse the velocityperAbsolutePitch hierarchy re the prevGamut. // List<byte> velocityPerAbsolutePitch = prevGamut.GetVelocityPerAbsolutePitch(20, 127, prevGamut.NPitchesPerOctave - 1); // tpg.SetVelocityPerAbsolutePitch(velocityPerAbsolutePitch, 20); //} #endregion grps.Add(tpg); } return (grps); }
/// <summary> /// The rootPitch and all the pitches in the MidiChordDef must be contained in the gamut. /// The vertical velocity sequence remains unchanged except when notes are removed because they are duplicates. /// Calculates the number of steps to transpose, and then calls TransposeStepsInGamut. /// When this function returns, rootPitch is the lowest pitch in both BasicMidiChordDefs[0] and NotatedMidiPitches. /// </summary> public void TransposeToRootInGamut(Gamut gamut, int rootPitch) { #region conditions Debug.Assert(gamut != null); Debug.Assert(gamut.Contains(rootPitch)); Debug.Assert(gamut.Contains(BasicMidiChordDefs[0].Pitches[0])); #endregion conditions int stepsToTranspose = gamut.IndexOf(rootPitch) - gamut.IndexOf(BasicMidiChordDefs[0].Pitches[0]); // checks that all the pitches are in the gamut. TransposeStepsInGamut(gamut, stepsToTranspose); }