Questo sito utilizza cookies solo per scopi di autenticazione sul sito e nient'altro. Nessuna informazione personale viene tracciata. Leggi l'informativa sui cookies.
Username: Password: oppure
Fret - Fret.cs

Fret.cs

Caricato da: Totem
Scarica il programma completo

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Text.RegularExpressions;
  6.  
  7. namespace ExtensionMethods
  8. {
  9.     public static class IEnumerableExtensions
  10.     {
  11.         public static Int32 IndexOf<T>(this IEnumerable<T> instance, T seed) where T : IEquatable<T>
  12.         {
  13.             Int32 i = 0;
  14.             foreach (T element in instance)
  15.             {
  16.                 if (element.Equals(seed))
  17.                     return i;
  18.                 i++;
  19.             }
  20.             return -1;
  21.         }
  22.     }
  23. }
  24.  
  25. namespace Fret
  26. {
  27.     using ExtensionMethods;
  28.  
  29.     /// <summary>
  30.     /// Indica il nome della nota, secondo lo standard inglese.
  31.     /// </summary>
  32.     public enum NoteName
  33.     {
  34.         C = 0,
  35.         D = 2,
  36.         E = 4,
  37.         F = 5,
  38.         G = 7,
  39.         A = 9,
  40.         B = 11
  41.     }
  42.  
  43.     /// <summary>
  44.     /// Rappresenta una frazione. Non espone operatori.
  45.     /// </summary>
  46.     public struct Fraction
  47.     {
  48.         public Int32 Numerator;
  49.         public Int32 Denumerator;
  50.  
  51.         public Fraction(Int32 num, Int32 denum)
  52.         {
  53.             this.Numerator = num;
  54.             this.Denumerator = denum;
  55.         }
  56.     }
  57.  
  58.     /// <summary>
  59.     /// Classi che implementano questa interfaccia rappresentano elementi di un file di notazione in formato GUIDO standard
  60.     /// e sono perciò rappresentabili con una stringa opportunamente formattata.
  61.     /// </summary>
  62.     public interface IGuidoFormattable
  63.     {
  64.         /// <summary>
  65.         /// Restituisce la rappresentazione di questa entità nel formato di notazione GUIDO.
  66.         /// </summary>
  67.         String ToGuidoNotation();
  68.     }
  69.  
  70.     public interface ITypedCloneable<T>
  71.     {
  72.         T Clone();
  73.     }
  74.  
  75.     public interface IGuidoElement: IGuidoFormattable, ITypedCloneable<IGuidoElement>
  76.     { }
  77.  
  78.     /// <summary>
  79.     /// Indica una nota sulla tastiera. Istanze di quela classe sono oggetti immutabili.
  80.     /// </summary>
  81.     public class NotePitch
  82.     {
  83.         private Int32 noteIndex = 0;
  84.  
  85.         private static Int32[] accidentalsIndex = { 1, 3, 6, 8, 10 }; // indici delle note alterate
  86.         private static Int32 minNoteIndex = 0; // inizia dal do della terza ottava sotto la centrale (e non dal la come nel pianoforte)
  87.         private static Int32 maxNoteIndex = 84;
  88.         // Segue il pool-pattern. Dato che ogni istanza è immutabile, è inutile creare più istanze uguali
  89.         private static Dictionary<Int32, NotePitch> notesPool = new Dictionary<Int32, NotePitch>();
  90.  
  91.         /// <summary>
  92.         /// Indica il nome della nota, scondo lo standard inglese.
  93.         /// </summary>
  94.         public NoteName Name { get; private set; }
  95.         /// <summary>
  96.         /// Indica l'ottava in cui si trova la nota. L'ottava centrale ha valore 1.
  97.         /// </summary>
  98.         public SByte Octave { get; private set; }
  99.         /// <summary>
  100.         /// Determina le alterazioni applicate alla nota. +1 indica un diesis, -1 un bemolle (analogamente +2 e -2).
  101.         /// 0 Indica che non è presente nessuna alterazione.
  102.         /// </summary>
  103.         public SByte Accidentals { get; private set; }
  104.  
  105.         private NotePitch() { }
  106.  
  107.         public static NotePitch CreateFromName(NoteName name, SByte accidentals = 0, SByte octave = 1)
  108.         {
  109.             if ((octave < -3) || (octave > 3))
  110.                 throw new ArgumentOutOfRangeException("octave deve essere compreso tra -3 e 3.");
  111.  
  112.             Int32 noteIndex = (octave + 3) * 12 + (Int32)name + accidentals;
  113.             if (notesPool.ContainsKey(noteIndex))
  114.                 return notesPool[noteIndex];
  115.  
  116.             NotePitch result = new NotePitch();
  117.             result.Name = name;
  118.             result.Accidentals = accidentals;
  119.             result.Octave = octave;
  120.             result.noteIndex = noteIndex;
  121.  
  122.             return result;
  123.         }
  124.  
  125.         public static NotePitch CreateFromKeyboardIndex(Int32 noteIndex, SByte defaultAccidentals = +1)
  126.         {
  127.             if ((noteIndex < minNoteIndex) || (noteIndex > maxNoteIndex))
  128.                 throw new IndexOutOfRangeException(String.Format("noteIndex deve essere compreso tra {0} e {1}.", minNoteIndex, maxNoteIndex));
  129.  
  130.             if (notesPool.ContainsKey(noteIndex))
  131.                 return notesPool[noteIndex];
  132.  
  133.             NotePitch result = new NotePitch();
  134.             Int32 noteIndexWithinOctave = noteIndex % 12;
  135.             result.noteIndex = noteIndex;
  136.             result.Octave = (SByte)((noteIndex / 12) - 3);
  137.             if (accidentalsIndex.IndexOf(noteIndexWithinOctave) > -1)
  138.             {
  139.                 if (defaultAccidentals > 0)
  140.                 {
  141.                     result.Name = (NoteName)(noteIndexWithinOctave - 1);
  142.                     result.Accidentals = +1;
  143.                 }
  144.                 else if (defaultAccidentals < 0)
  145.                 {
  146.                     result.Name = (NoteName)(noteIndexWithinOctave + 1);
  147.                     result.Accidentals = -1;
  148.                 }
  149.             }
  150.             else
  151.                 result.Name = (NoteName)noteIndexWithinOctave;
  152.  
  153.             return result;
  154.         }
  155.  
  156.         public NotePitch Transpose(Int32 offset, SByte defaultAccidentals)
  157.         {
  158.             if (defaultAccidentals == 0)
  159.                 defaultAccidentals = (SByte)Math.Sign(offset);
  160.             return NotePitch.CreateFromKeyboardIndex(this.noteIndex + offset, defaultAccidentals);
  161.         }
  162.  
  163.         public NotePitch TransposeByOctave(SByte offset)
  164.         {
  165.             return this.Transpose(12 * offset, this.Accidentals);
  166.         }
  167.  
  168.         public NotePitch TransposeByInterval(Key key, Int32 interval)
  169.         {
  170.             return key.GetDiatonicHarmonic(this, interval);
  171.         }
  172.  
  173.         public Int32 GetDistance(NotePitch otherPitch)
  174.         {
  175.             return this.noteIndex - otherPitch.noteIndex;
  176.         }
  177.  
  178.         public override String ToString()
  179.         {
  180.             return String.Format("{0}{1}{2}",
  181.                 Enum.GetName(typeof(NoteName), this.Name).ToLower(),
  182.                 (this.Accidentals != 0 ?
  183.                     (this.Accidentals > 0 ? new String('#', this.Accidentals)
  184.                                           : new String('&', -this.Accidentals))
  185.                     : ""),
  186.                 this.Octave);
  187.         }
  188.     }
  189.  
  190.     /// <summary>
  191.     /// Rappresenta una nota musicale all'interno di un pezzo.
  192.     /// </summary>
  193.     public class Note : IGuidoElement
  194.     {
  195.         public static Type Type = typeof(Note);
  196.  
  197.         private static Type noteNameType = typeof(NoteName);
  198.         private static Regex guidoNoteRegex = new Regex("(?<Name>[a-g])(?<Acc>(#|&)+)?(?<Octave>(\\-)?\\d)\\*(?<Num>\\d+)/(?<Den>\\d+)(?<Dots>\\.+)?", RegexOptions.IgnoreCase);
  199.  
  200.         public NotePitch Pitch { get; set; }
  201.         /// <summary>
  202.         /// Contiene la durata della nota, sottoforma di frazione.
  203.         /// </summary>
  204.         public Fraction Duration { get; set; }
  205.         /// <summary>
  206.         /// Specifica il numero di punti usati per modificare la durata della nota.
  207.         /// </summary>
  208.         public Byte NumberOfDots { get; set; }
  209.  
  210.         private Note()
  211.         {
  212.             this.Duration = new Fraction(1, 1);
  213.             this.NumberOfDots = 0;
  214.         }
  215.  
  216.         public Note(NotePitch pitch, Fraction duration, Byte numberOfDots)
  217.         {
  218.             this.Pitch = pitch;
  219.             this.Duration = duration;
  220.             this.NumberOfDots = numberOfDots;
  221.         }
  222.  
  223.         public String ToGuidoNotation()
  224.         {
  225.             return String.Format("{0}*{1}/{2}{3}",
  226.                 this.Pitch.ToString(),
  227.                 this.Duration.Numerator,
  228.                 this.Duration.Denumerator,
  229.                 (this.NumberOfDots > 0 ? new String('.', this.NumberOfDots) : ""));
  230.         }
  231.  
  232.         /// <summary>
  233.         /// Crea un oggetto Note a partire dalla sua rappresentazione in notazione GUIDO.
  234.         /// </summary>
  235.         /// <param name="noteString">Un'opportuna stringa contenente la rappresentazione della nota.</param>
  236.         public static Note FromGuidoNotation(String noteString)
  237.         {
  238.             Note result = null;
  239.             Match m = guidoNoteRegex.Match(noteString);
  240.  
  241.             if (m.Success)
  242.             {
  243.                 NoteName name;
  244.                 SByte accidentals = 0, octave;
  245.  
  246.                 result = new Note();
  247.                
  248.                 name = (NoteName)Enum.Parse(noteNameType, m.Groups["Name"].Value.ToUpper());
  249.                 if (m.Groups["Acc"] != null)
  250.                     if (m.Groups["Acc"].Value.Contains("#"))
  251.                         accidentals = (SByte)m.Groups["Acc"].Length;
  252.                     else
  253.                         accidentals = (SByte)(-m.Groups["Acc"].Length);
  254.                 octave = Convert.ToSByte(m.Groups["Octave"].Value);
  255.                 result.Pitch = NotePitch.CreateFromName(name, accidentals, octave);
  256.                 result.Duration = new Fraction(Convert.ToInt32(m.Groups["Num"].Value), Convert.ToInt32(m.Groups["Den"].Value));
  257.                 if (m.Groups["Dots"] != null)
  258.                     result.NumberOfDots = (Byte)m.Groups["Dots"].Length;
  259.             }
  260.             else
  261.                 throw new FormatException("La stringa '" + noteString + "' non è conforme alla notazione GUIDO standard.");
  262.  
  263.             return result;
  264.         }
  265.  
  266.         public Note TypedClone()
  267.         {
  268.             return (this.Clone() as Note);
  269.         }
  270.  
  271.         public IGuidoElement Clone()
  272.         {
  273.             return this.MemberwiseClone() as IGuidoElement;
  274.         }
  275.  
  276.         /// <summary>
  277.         /// Restituisce una nota che si trova a offset semitoni di distanza da quella corrente.
  278.         /// </summary>
  279.         /// <param name="offset">Il numero di semitoni di cui transporre la nota.</param>
  280.         /// <param name="defaultAccidental">Se la nota a cui si arriva dopo la trasposizione non è una nota "naturale", ossia senza alterazioni, le sarà associato di default un diesis se
  281.         /// la trasposizione era a salire, o un bemolle se era a scendere. Questo parametro modifica il comportamento predefinito, in modo che la nota risultante venga alterata solo con diesis
  282.         /// nel caso defaultAccidental sia positivo, o con un bemolle nel caso sia negativo.</param>
  283.         public Note CloneAndTranspose(Int32 offset, SByte defaultAccidental = 0)
  284.         {
  285.             Note result = this.Clone() as Note;
  286.             result.Pitch = result.Pitch.Transpose(offset, defaultAccidental);
  287.             return result;
  288.         }
  289.  
  290.         public Note CloneAndTrasposeByInterval(Key key, Int32 interval)
  291.         {
  292.             Note result = this.TypedClone();
  293.             result.Pitch = key.GetDiatonicHarmonic(this.Pitch, interval);
  294.             return result;
  295.         }
  296.     }
  297.  
  298.     /// <summary>
  299.     /// Rappresenta una pausa.
  300.     /// </summary>
  301.     public class Pause : IGuidoElement
  302.     {
  303.         public static Type Type = typeof(Pause);
  304.  
  305.         private static Regex guidoPauseRegex = new Regex("_\\*(?<Num>\\d+)/(?<Den>\\d+)(?<Dots>\\.+)?", RegexOptions.IgnoreCase);
  306.  
  307.         /// <summary>
  308.         /// Indica la durata della pausa, sottoforma di frazione.
  309.         /// </summary>
  310.         public Fraction Duration { get; set; }
  311.         /// <summary>
  312.         /// Specifica il numero di punti usati per modificare la durata della pausa.
  313.         /// </summary>
  314.         public Byte NumberOfDots { get; set; }
  315.  
  316.         public String ToGuidoNotation()
  317.         {
  318.             return String.Format("_*{0}/{1}{2}",
  319.                 this.Duration.Numerator,
  320.                 this.Duration.Denumerator,
  321.                 (this.NumberOfDots > 0 ? new String('.', this.NumberOfDots) : ""));
  322.         }
  323.  
  324.         /// <summary>
  325.         /// Crea un oggetto Pause a partire dalla sua rappresentazione in notazione GUIDO.
  326.         /// </summary>
  327.         /// <param name="pauseString">Un'opportuna stringa contenente la rappresentazione della pause.</param>
  328.         public static Pause FromGuidoNotation(String pauseString)
  329.         {
  330.             Pause result = null;
  331.             Match m = guidoPauseRegex.Match(pauseString);
  332.  
  333.             if (m.Success)
  334.             {
  335.                 result = new Pause();
  336.                 result.Duration = new Fraction(Convert.ToInt32(m.Groups["Num"].Value), Convert.ToInt32(m.Groups["Den"].Value));
  337.                 if (m.Groups["Dots"] != null)
  338.                     result.NumberOfDots = (Byte)m.Groups["Dots"].Length;
  339.             }
  340.             else
  341.                 throw new FormatException("La stringa '" + pauseString + "' non è conforme alla notazione GUIDO standard.");
  342.  
  343.             return result;
  344.         }
  345.  
  346.         public IGuidoElement Clone()
  347.         {
  348.             return this.MemberwiseClone() as IGuidoElement;
  349.         }
  350.     }
  351.  
  352.     /// <summary>
  353.     /// Rappresenta un accordo. All'atto pratico è una lista di oggetti Note.
  354.     /// </summary>
  355.     public class Chord : List<Note>, IGuidoElement
  356.     {
  357.         public static Type Type = typeof(Chord);
  358.  
  359.         public String ToGuidoNotation()
  360.         {
  361.             return "{" + this.Select(note => note.ToGuidoNotation())
  362.                 .Aggregate("", (partial, str) => partial + (partial.Length > 0 ? ", " : "") + str) + "}";
  363.         }
  364.  
  365.         public IGuidoElement Clone()
  366.         {
  367.             Chord result = new Chord();
  368.             foreach (Note note in this)
  369.                 result.Add(note.Clone() as Note);
  370.             return result as IGuidoElement;
  371.         }
  372.     }
  373.  
  374.     /// <summary>
  375.     /// Rappresenta la tonalità di un brano.
  376.     /// </summary>
  377.     public class Key : IGuidoElement
  378.     {
  379.         public static Type Type = typeof(Key);
  380.  
  381.         private static NoteName[] flatsOrder = { NoteName.B, NoteName.E, NoteName.A, NoteName.D, NoteName.G, NoteName.C, NoteName.F };
  382.         private static NoteName[] sharpsOrder = { NoteName.F, NoteName.C, NoteName.G, NoteName.D, NoteName.A, NoteName.E, NoteName.B };
  383.         private static Byte[] majorKeyIntervals = { 2, 2, 1, 2, 2, 2 }; // semintoni tra una nota e l'altra della scala maggiore
  384.         private static Byte[] minorKeyIntervals = { 2, 1, 2, 2, 1, 3 }; // semitoni tra una nota e l'altra della scala minore armonica
  385.  
  386.         private SByte accidentals;
  387.         private Boolean isMajor;
  388.         private NotePitch[] keyNotes;
  389.  
  390.         /// <summary>
  391.         /// Indica il numero di alterazioni presenti nell'armatura in chiave. Un valore positivo indica il numero di diesis,
  392.         /// mentre uno negativo il numero di bemolli.
  393.         /// </summary>
  394.         public SByte Accidentals
  395.         {
  396.             get { return accidentals; }
  397.             set
  398.             {
  399.                 accidentals = value;
  400.                 if (isMajor)
  401.                     keyNotes = this.GetMajorKeyNotes();
  402.                 else
  403.                     keyNotes = this.GetMinorKeyNotes();
  404.             }
  405.         }
  406.  
  407.         /// <summary>
  408.         /// Indica se si tratta di una tonalità maggiore o minore.
  409.         /// </summary>
  410.         public Boolean IsMajor
  411.         {
  412.             get { return isMajor; }
  413.             set
  414.             {
  415.                 isMajor = value;
  416.                 if (isMajor)
  417.                     keyNotes = this.GetMajorKeyNotes();
  418.                 else
  419.                     keyNotes = this.GetMinorKeyNotes();
  420.             }
  421.         }
  422.  
  423.         /// <summary>
  424.         /// Crea un nuovo oggetto per rappresentare una tonalità.
  425.         /// </summary>
  426.         /// <param name="accidentals">Indica il numero di alterazioni presenti nell'armatura in chiave. Un valore positivo indica il numero di diesis,
  427.         /// mentre uno negativo il numero di bemolli.</param>
  428.         /// <param name="isMajor">Indica se si tratta di una tonalità maggiore o minore.</param>
  429.         public Key(SByte accidentals, Boolean isMajor)
  430.         {
  431.             this.accidentals = accidentals;
  432.             this.IsMajor = IsMajor;
  433.         }
  434.  
  435.         /// <summary>
  436.         /// Restituisce le prime 7 note (a partire dall'ottava centrale) della scala maggiore o minore armonica.
  437.         /// </summary>
  438.         public NotePitch[] KeyNotes { get { return keyNotes; } }
  439.  
  440.         public String ToGuidoNotation()
  441.         {
  442.             return String.Format("\\key<{0}>", this.Accidentals);
  443.         }
  444.  
  445.         /// <summary>
  446.         /// Restituisce la nota che forma un intervallo diatonico maggiore, minore o giusto (a seconda della tonalità) con la nota specificata, purché tale nota compaia nella tonalità corrente.
  447.         /// Ad esempio, se la tonalità è Do maggiore, baseNote è un Re e interval vale 3, restituirà un Fa. Se interval vale -3 restituirà un Si dell'ottava inferiore.
  448.         /// Se la tonalità è Re minore e baseNote è un La, se interval vale 1, restituirà Sib.
  449.         /// </summary>
  450.         /// <param name="baseNote">La nota di base dell'intervallo.</param>
  451.         /// <param name="interval">L'intervallo da formare.</param>
  452.         /// <returns></returns>
  453.         public NotePitch GetDiatonicHarmonic(NotePitch baseNote, Int32 interval)
  454.         {
  455.             if (Math.Abs(interval) <= 1)
  456.                 return baseNote;
  457.  
  458.             for (Int32 i = 0; i < 7; i++)
  459.                 if (baseNote.Name == this.KeyNotes[i].Name)
  460.                 {
  461.                     Int32 newNoteIndex = i + interval - 1 * Math.Sign(interval);
  462.                     Int32 index = newNoteIndex;
  463.                     NoteName name;
  464.                     SByte accidentals, octave;
  465.  
  466.                     octave = baseNote.Octave;
  467.                     if (newNoteIndex >= 7)
  468.                         index = newNoteIndex % 7;
  469.                     else if (newNoteIndex < 0)
  470.                         index = (7 - Math.Abs(newNoteIndex % 7)) % 7;
  471.                    
  472.                     name = this.KeyNotes[index].Name;
  473.                     accidentals = this.keyNotes[index].Accidentals;
  474.                     octave += (SByte)(interval / 8);
  475.                     if (name < baseNote.Name && interval > 0)
  476.                         octave++;
  477.                     else if (name > baseNote.Name && interval < 0)
  478.                         octave--;
  479.  
  480.                     return NotePitch.CreateFromName(name, accidentals, octave);
  481.                 }
  482.  
  483.             return null;
  484.         }
  485.  
  486.         public IGuidoElement Clone()
  487.         {
  488.             return this.MemberwiseClone() as IGuidoElement;
  489.         }  
  490.  
  491.         private NotePitch GetTonicKeyNote()
  492.         {
  493.             NotePitch keyNote;
  494.             if (this.Accidentals > 0)
  495.                 keyNote = NotePitch.CreateFromName(sharpsOrder[this.Accidentals - 1]).Transpose(+2, 1);  // tonica: x# + 1 semitono
  496.             else if (this.Accidentals < 0)
  497.             {
  498.                 Int32 index = -this.Accidentals - 2;  // tonica: il bemolle precedente a xb nella scala
  499.                 SByte acc = -1;
  500.                 if (index < 0) // significa che la tonalità è Fa
  501.                 {
  502.                     index += 7;
  503.                     acc = 0;
  504.                 }
  505.                 keyNote = NotePitch.CreateFromName(flatsOrder[index], acc);
  506.             }
  507.             else
  508.                 keyNote = NotePitch.CreateFromName(NoteName.C);
  509.             return keyNote;
  510.         }
  511.  
  512.         private NotePitch[] GetMajorKeyNotes()
  513.         {
  514.             NotePitch[] result = new NotePitch[7];
  515.             NotePitch keyNote;
  516.  
  517.             keyNote = this.GetTonicKeyNote();
  518.             result[0] = keyNote;
  519.             for(Int32 i = 0; i < majorKeyIntervals.Length; i++)
  520.             {
  521.                 keyNote = keyNote.Transpose(majorKeyIntervals[i], this.Accidentals);
  522.                 result[i + 1] = keyNote;
  523.             }
  524.  
  525.             return result;
  526.         }
  527.  
  528.         private NotePitch[] GetMinorKeyNotes()
  529.         {
  530.             NotePitch[] result = new NotePitch[7];
  531.             NotePitch keyNote;
  532.  
  533.             keyNote = this.GetTonicKeyNote();
  534.             keyNote = keyNote.Transpose(-3, this.Accidentals);
  535.             result[0] = keyNote;
  536.             for (Int32 i = 0; i < minorKeyIntervals.Length; i++)
  537.             {
  538.                 //
  539.                 keyNote = keyNote.Transpose(minorKeyIntervals[i], (SByte)(i < minorKeyIntervals.Length - 1 ? this.Accidentals : +1));
  540.                 result[i + 1] = keyNote;
  541.             }
  542.  
  543.             return result;
  544.         }
  545.  
  546.     }
  547.  
  548.     /// <summary>
  549.     /// Rappresenta l'indicazione di tempo di un pezzo.
  550.     /// </summary>
  551.     public class Tempo : IGuidoElement
  552.     {
  553.         public static Type Type = typeof(Tempo);
  554.  
  555.         /// <summary>
  556.         /// Indica quanto vale la figura musiclae che si pone come unità (battito).
  557.         /// </summary>
  558.         public Fraction BeatDuration { get; set; }
  559.         /// <summary>
  560.         /// Indica il numero di battiti per minuto.
  561.         /// </summary>
  562.         public Int32 BeatsPerMinute { get; set; }
  563.  
  564.         public Tempo(Fraction beatDuration, Int32 beatsPerMinute)
  565.         {
  566.             this.BeatDuration = beatDuration;
  567.             this.BeatsPerMinute = beatsPerMinute;
  568.         }
  569.  
  570.         public String ToGuidoNotation()
  571.         {
  572.             return String.Format("\\tempo<\"\",\"{0}/{1}={2}\">", this.BeatDuration.Numerator, this.BeatDuration.Denumerator, this.BeatsPerMinute);
  573.         }
  574.  
  575.         public IGuidoElement Clone()
  576.         {
  577.             return this.MemberwiseClone() as IGuidoElement;
  578.         }
  579.     }
  580.  
  581.     /// <summary>
  582.     /// Rappresenta l'indicazione metrica che stabilisce la dimensione delle misure.
  583.     /// </summary>
  584.     public class Meter : IGuidoElement
  585.     {
  586.         public static Type Type = typeof(Meter);
  587.  
  588.         /// <summary>
  589.         /// La frazione che indica la metrica da adottare (es. 4/4, 3/8, 7/16).
  590.         /// </summary>
  591.         public Fraction TimeSignature { get; set; }
  592.  
  593.         public Meter(Int32 num, Int32 denum)
  594.         {
  595.             this.TimeSignature = new Fraction(num, denum);
  596.         }
  597.  
  598.         public String ToGuidoNotation()
  599.         {
  600.             return String.Format("\\meter<\"{0}/{1}\">", this.TimeSignature.Numerator, this.TimeSignature.Denumerator);
  601.         }
  602.  
  603.         public IGuidoElement Clone()
  604.         {
  605.             return this.MemberwiseClone() as IGuidoElement;
  606.         }
  607.     }
  608.  
  609.     /// <summary>
  610.     /// Rappresenta un pezzo del fregio musicale, ossia una frase musicale con propria tonalità, tempo e metrica.
  611.     /// </summary>
  612.     public class FretPiece : List<IGuidoElement>
  613.     {
  614.         private static Char[] validChars = new Char[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '#', '&', '*', '_', '.', '/' };
  615.        
  616.         /// <summary>
  617.         /// Indica la metrica della frase.
  618.         /// </summary>
  619.         public Meter Meter { get; set; }
  620.         /// <summary>
  621.         /// Indica la tonalità della frase.
  622.         /// </summary>
  623.         public Key Key { get; set; }
  624.         /// <summary>
  625.         /// Indica il tempo adottato per la frase.
  626.         /// </summary>
  627.         public Tempo Tempo { get; set; }
  628.  
  629.         public FretPiece()
  630.         {
  631.             this.Key = new Key(0, true);
  632.             this.Tempo = new Tempo(new Fraction(1, 4), 120);
  633.         }
  634.  
  635.         /// <summary>
  636.         /// Traduce tutte le informazioni di questa frase musicale in una stringa di notazione secondo lo standard GUIDO.
  637.         /// </summary>
  638.         /// <param name="onlyNotes">Se true, le parentesi graffe e quadre all'inizio e alla fine della stringa vengono
  639.         /// omesse. Serve per unire più frasi musicali in un unico pezzo senza staccarle sintatticamente. </param>
  640.         /// <param name="includeSignature">Se true, include nella stringa anche le sequenze di tempo, metrica e tonalità.</param>
  641.         /// <returns></returns>
  642.         public String ToGuidoNotation(Boolean onlyNotes = false, Boolean includeSignature = true)
  643.         {
  644.             StringBuilder result = new StringBuilder();
  645.  
  646.             if (!onlyNotes)
  647.                 result.Append("{[");
  648.  
  649.             if (includeSignature)
  650.             {
  651.                 result.AppendFormat(" {0} ", this.Key.ToGuidoNotation());
  652.                 result.AppendFormat(" {0} ", this.Tempo.ToGuidoNotation());
  653.                 result.AppendFormat(" {0} ", this.Meter.ToGuidoNotation());
  654.                 result.AppendLine();
  655.             }
  656.  
  657.             foreach (IGuidoFormattable element in this)
  658.                 result.AppendFormat(" {0} ", element.ToGuidoNotation());
  659.  
  660.             if (!onlyNotes)
  661.                 result.Append("]}");
  662.  
  663.             return result.ToString();
  664.         }
  665.  
  666.         /// <summary>
  667.         /// Salva questa frase come un sinolo brano in un file di notazione specificato.
  668.         /// </summary>
  669.         /// <param name="path">Il path di un file *.gmn in cui salvare la stringa.</param>
  670.         public void SaveGuidoNotationFile(String path)
  671.         {
  672.             System.IO.File.WriteAllText(path, this.ToGuidoNotation());
  673.         }
  674.  
  675.         public void AddFromGuidoString(String gmnString)
  676.         {
  677.             StringBuilder buffer = new StringBuilder();
  678.             Boolean chordOpen = false;
  679.             Chord tmp = null;
  680.             Int32 startCount = this.Count;
  681.  
  682.             gmnString += ' ';
  683.             foreach (Char c in gmnString)
  684.             {
  685.                 if (validChars.Contains(c))
  686.                     buffer.Append(c);
  687.                 else if (c == '{')
  688.                     chordOpen = true;
  689.                 else if (c == '}')
  690.                 {
  691.                     if (tmp != null)
  692.                         this.Add(tmp);
  693.                     chordOpen = false;
  694.                 }
  695.                 else if (buffer.Length > 0)
  696.                 {
  697.                     String strBuffer = buffer.ToString();
  698.                     if (strBuffer.Contains('_'))
  699.                     {
  700.                         try
  701.                         {
  702.                             Pause pause = Pause.FromGuidoNotation(strBuffer);
  703.                             this.Add(pause);
  704.                         }
  705.                         catch (Exception ex)
  706.                         {
  707.                             this.RemoveRange(startCount, this.Count - startCount);
  708.                             throw ex;
  709.                         }
  710.                     }
  711.                     else
  712.                     {
  713.                         try
  714.                         {
  715.                             Note note = Note.FromGuidoNotation(strBuffer);
  716.                             if (chordOpen)
  717.                             {
  718.                                 if (tmp == null)
  719.                                     tmp = new Chord();
  720.                                 tmp.Add(note);
  721.                             }
  722.                             else
  723.                                 this.Add(note);
  724.                         }
  725.                         catch (Exception ex)
  726.                         {
  727.                             this.RemoveRange(startCount, this.Count - startCount);
  728.                             throw ex;
  729.                         }
  730.                     }
  731.                     buffer.Remove(0, buffer.Length);
  732.                 }
  733.             }
  734.             buffer = null;
  735.         }
  736.  
  737.         public FretPiece TypedClone()
  738.         {
  739.             FretPiece result = new FretPiece();
  740.             result.Meter = this.Meter.Clone() as Meter;
  741.             result.Key = this.Key.Clone() as Key;
  742.             result.Tempo = this.Tempo.Clone() as Tempo;
  743.             foreach (IGuidoElement element in this)
  744.                 result.Add(element.Clone());
  745.             return result;
  746.         }
  747.     }
  748.  
  749.     /// <summary>
  750.     /// Rappresenta un fregio musicale completo, come insieme di oggetti FretPiece.
  751.     /// </summary>
  752.     public class Fret : List<FretPiece>
  753.     {
  754.         /// <summary>
  755.         /// Salva tutto il brano in un dato percorso.
  756.         /// </summary>
  757.         /// <param name="path">Il path di un file *.gmn in cui salvare la stringa.</param>
  758.         public void SaveGuidoNotationFile(String path)
  759.         {
  760.             System.IO.StreamWriter writer = new System.IO.StreamWriter(path);
  761.             Boolean first = true;
  762.             writer.Write("{[");
  763.             foreach (FretPiece piece in this)
  764.             {
  765.                 writer.WriteLine(piece.ToGuidoNotation(true, first));
  766.                 first = false;
  767.             }
  768.             writer.Write("]}");
  769.             writer.Close();
  770.         }
  771.     }
  772. }