/// <summary> /// The divide. /// </summary> /// <param name="scoreTrack"> /// The score track. /// </param> /// <param name="paramPauseTreatment"> /// The param pause treatment. /// </param> /// <param name="paramEqualFM"> /// The param equal fm. /// </param> /// <returns> /// The <see cref="List{FmotivChain}"/>. /// </returns> public List<FmotivChain> Divide(ScoreTrack scoreTrack, ParamPauseTreatment paramPauseTreatment, ParamEqualFM paramEqualFM) { var chains = new List<FmotivChain>(); foreach (CongenericScoreTrack congenericTrack in scoreTrack.CongenericScoreTracks) { var fmotivChain = (FmotivChain)Divide(congenericTrack, paramPauseTreatment, paramEqualFM).Clone(); fmotivChain.Id = chains.Count; chains.Add(fmotivChain); } return chains; }
/// <summary> /// The divide. /// </summary> /// <param name="congenericTrack"> /// The congeneric track. /// </param> /// <param name="paramPauseTreatment"> /// The param pause treatment. /// параметр как учитывать паузу : /// игнорировать, звуковой след предыдущего звука, вырожденныый звук /// </param> /// <param name="paramEqualFM"> /// The param equal fm. /// как сравнивать ф-мотивы с секвентым переносом, либо нет /// </param> /// <returns> /// The <see cref="FmotivChain"/>. /// </returns> public FmotivChain Divide(CongenericScoreTrack congenericTrack, ParamPauseTreatment paramPauseTreatment, ParamEqualFM paramEqualFM) { // сохраняем имя цепи фмотивов как имя монотрека var priorityDiscover = new PriorityDiscover(); var fmotivDivider = new FmotivDivider(); var fmotivIdentifier = new FmotivIdentifier(); // подсчет приоритетов priorityDiscover.Calculate(congenericTrack); // разбиение FmotivChain chain = fmotivDivider.GetDivision(congenericTrack, paramPauseTreatment); // нахождение одинаковых return fmotivIdentifier.GetIdentification(chain, paramPauseTreatment, paramEqualFM); }
/// <summary> /// The get identification. /// </summary> /// <param name="fmotivChain"> /// The fmotiv chain. /// </param> /// <param name="paramPauseTreatment"> /// The param pause treatment. /// </param> /// <param name="paramEqualFM"> /// The param equal fm. /// </param> /// <returns> /// The <see cref="FmotivChain"/>. /// </returns> public FmotivChain GetIdentification(FmotivChain fmotivChain, ParamPauseTreatment paramPauseTreatment, ParamEqualFM paramEqualFM) { var chain = (FmotivChain)fmotivChain.Clone(); for (int i = 0; i < chain.FmotivList.Count; i++) { for (int j = i; j < chain.FmotivList.Count; j++) { if (chain.FmotivList[i].FmEquals(chain.FmotivList[j], paramPauseTreatment, paramEqualFM)) { chain.FmotivList[j].Id = chain.FmotivList[i].Id; } } } for (int i = 0; i < chain.FmotivList.Max(fl => fl.Id); i++) { bool haveId = chain.FmotivList.Any(t => t.Id == i); // флаг того что есть такой id в цепочке if (!haveId) { foreach (Fmotiv fmotiv in chain.FmotivList) { if (fmotiv.Id > i) { // уменьшаем на 1 id тех фмотивов которые больше текущей id - i, которой не нашлось в цепи fmotiv.Id--; } } // уменьшаем i на 1 чтобы еще раз проверить есть ли это i среди цепи после уменьшения id-ек больших i i--; } } return chain; }
/// <summary> /// The get division. /// </summary> /// <param name="congenericTrack"> /// The congeneric track. /// </param> /// <param name="paramPauseTreatment"> /// The param pause treatment. /// </param> /// <returns> /// The <see cref="FmotivChain"/>. /// </returns> /// <exception cref="Exception"> /// Thrown in many cases. /// </exception> public FmotivChain GetDivision(CongenericScoreTrack congenericTrack, ParamPauseTreatment paramPauseTreatment) { var chain = new FmotivChain { Name = congenericTrack.Name }; // выходная, результирующая цепочка разбитых ф-мотивов this.paramPauseTreatment = paramPauseTreatment; var fmotivBuffer = new Fmotiv(string.Empty); // буффер для накопления нот, для последующего анализа его содержимого var noteChain = new List<ValueNote>(); // цепочка нот, куда поочередно складываются ноты из последовательности тактов // для дальнейшего их анализа и распределения по ф-мотивам. // заполняем NoteChain всеми нотам из данной монофонической цепи unitrack foreach (Measure measure in congenericTrack.MeasureList) { foreach (ValueNote note in measure.NoteList) { noteChain.Add((ValueNote)note.Clone()); } } // счетчик реальных нот/пауз для первой группировки в реальную нот int n = 0; // флаг который говорит, что была нота перемещена в буфер после последнего флага Next, для pause notetrace bool wasNote = false; // флаг, говорит что собралась очередная нота для рассмотрения bool next = false; // флаг, говорящий что собирается последовательность равнодлительных звуков (1,2 тип фмотива - ПМТ,ЧМТ) bool sameDurationChain = false; // флаг, говорящий что собирается возрастающая последовательность (3 тип фмотива) bool growingDurationChain = false; // флаг, говорящий что собирается комбинация - ПМТ/ЧМТ и возрастающая последовательность (4 тип фмотива) bool combination = false; // пока анализируемая цепь содержит элементы, идет выполнение анализа ее содержимого while (noteChain.Count > 0) { fmotivBuffer.NoteList.Add((ValueNote)noteChain[0].Clone()); noteChain.RemoveAt(0); // проверка на наличие лиги у очередной ноты, если есть то заносим в буффер все ноты, объединенные данной лигой if (fmotivBuffer.NoteList[fmotivBuffer.NoteList.Count - 1].Tie != Tie.None) { // если есть флаг начала лиги, то записываем в буфер все остальные лигованные ноты, пока не будет флага конца лиги if (fmotivBuffer.NoteList[fmotivBuffer.NoteList.Count - 1].Tie == Tie.Start) { // TODO: желательно сделать проверку когда собирается очередная лига, // не будет ли пуста цепь нот, до того как лига закончится (будет флаг конца лиги) while (noteChain[0].Tie == Tie.StartStop) { // пока продолжается лига, заносим ноты в буфер fmotivBuffer.NoteList.Add((ValueNote)noteChain[0].Clone()); noteChain.RemoveAt(0); } if (noteChain[0].Tie == Tie.Stop) { // если есть флаг конца лиги у очередной ноты, то заносим конечную ноту лиги в буфер fmotivBuffer.NoteList.Add((ValueNote)noteChain[0].Clone()); noteChain.RemoveAt(0); wasNote = true; // была нота пермещена в буфер switch (paramPauseTreatment) { case ParamPauseTreatment.Ignore: // удаляем все паузы в возвращаемом объекте (0) (паузы игнорируются) // если у очередной ноты нет лиги, то проверяем: если нота - не пауза, то выставляем флаг о следущей рассматриваемой ноте if (fmotivBuffer.NoteList[fmotivBuffer.NoteList.Count - 1].Pitch.Count > 0) { next = true; } break; case ParamPauseTreatment.NoteTrace: // длительность паузы прибавляется к предыдущей ноте, а она сама удаляется из текста (1) (пауза - звуковой след ноты) if (noteChain.Count > 0) { // если следующая не паузы то переходим к анализу буфера if (noteChain[0].Pitch.Count > 0) { next = true; } } else { next = true; } break; case ParamPauseTreatment.SilenceNote: // Пауза - звук тишины, рассматривается как нота без высоты звучания (2) // ничего не треуется next = true; break; default: throw new Exception("Error Fmotiv.PauseTreatment parameter contains wrong value!"); } } else { // когда лига не заканчивается флагом конца, то ошибка throw new Exception("LibiadaMusic: FmotivDivider, wrong Tie organization!End!"); } } else { // когда начинается лига не с флага начала, а с какого то другого, то ошибка throw new Exception("LibiadaMusic: FmotivDivider, wrong Tie organization!Begining!"); } } else { // если у очередной ноты нет лиги switch (paramPauseTreatment) { case ParamPauseTreatment.Ignore: // удаляем все паузы в возвращаемом объекте (0) (паузы игнорируются) // если у очередной ноты нет лиги, то проверяем: если нота - не пауза, то выставляем флаг о следущей рассматриваемой ноте if (fmotivBuffer.NoteList[fmotivBuffer.NoteList.Count - 1].Pitch.Count > 0) { next = true; } break; case ParamPauseTreatment.NoteTrace: // длительность паузы прибавляется к предыдущей ноте, а она сама удаляется из текста (1) (пауза - звуковой след ноты) // проверяем: если нота - не пауза, то выставляем флаг о следущей рассматриваемой ноте if (fmotivBuffer.NoteList[fmotivBuffer.NoteList.Count - 1].Pitch.Count > 0) { wasNote = true; } if (noteChain.Count > 0) { // если следующая в н. тексте не пауза то переходим к анализу буфера if ((noteChain[0].Pitch.Count > 0) && wasNote) { next = true; } } else { if (wasNote) { next = true; } } break; case ParamPauseTreatment.SilenceNote: // Пауза - звук тишины, рассматривается как нота без высоты звучания (2) // ничего не треуется next = true; break; default: throw new Exception("Error Fmotiv.PauseTreatment parameter contains wrong value!"); } } // если готова (собрана) следущая нота для анализа if (next) { // убираем флаг следущей готовой (собранной ноты), так как после анализа не известно что там будет next = false; wasNote = false; if (ExtractNoteList(fmotivBuffer).Count == 1) { // сохранили сколько нот/пауз входит в первую рассматриваемую ноту n = fmotivBuffer.NoteList.Count; } if (ExtractNoteList(fmotivBuffer).Count == 2) { // если длительность первой собранной ноты больше длительности второй собранной ноты if (TempExtractor(fmotivBuffer, 0).Duration.Value > TempExtractor(fmotivBuffer, 1).Duration.Value) { // заносим ноты/паузы первой собранной ноты в очередной фмотив с типом ЧМТ, и удаляем из буфера var fm = new Fmotiv("ЧМТ", chain.FmotivList.Count); for (int i = 0; i < n; i++) { // заносим fm.NoteList.Add((ValueNote)fmotivBuffer.NoteList[0].Clone()); // удаляем fmotivBuffer.NoteList.RemoveAt(0); } // добавляем в выходную цепочку получившийся фмотив chain.FmotivList.Add((Fmotiv)fm.Clone()); // сохранили n на случай если за этим фмотивом следует еще один ЧМТ n = fmotivBuffer.NoteList.Count; // сохранили сколько нот/пауз входит в первую рассматриваемую ноту } else { if (AnotherTempComparator(fmotivBuffer)) { // выставляем флаг для сбора последовательности равнодлительных звуков sameDurationChain = true; n = fmotivBuffer.NoteList.Count; // сохранили сколько нот/пауз входит в буфер } else { if (SecondAnotherTempComparator(fmotivBuffer)) { // выставляем флаг для сбора возрастающей последовательности growingDurationChain = true; // сохранили сколько нот/пауз входит в буфер n = fmotivBuffer.NoteList.Count; } } } } if (ExtractNoteList(fmotivBuffer).Count > 2) { if (sameDurationChain) { // если длительность предпоследнего меньше длительности последнего if (TempExtractor(fmotivBuffer, ExtractNoteList(fmotivBuffer).Count - 2).Duration.Value < TempExtractor(fmotivBuffer, ExtractNoteList(fmotivBuffer).Count - 1).Duration.Value) { var fmotivBuffer2 = new Fmotiv(string.Empty); // помещаем в буффер2 последнюю собранную ноту - большей длительности чем все равнодлительные // так как меняется в процессе int count = fmotivBuffer.NoteList.Count; for (int i = n; i < count; i++) { fmotivBuffer2.NoteList.Add((ValueNote)fmotivBuffer.NoteList[n].Clone()); fmotivBuffer.NoteList.RemoveAt(n); } // отправляем последовательность равнодлительных звуков на анализ, получаем цепочку фмотивов и заносим их в выходную последовательность // заисключением последнего фмотива - он останется в буфере вместе с нотой длительность которой больше последней ноты этого фмотива List<Fmotiv> dividedSameDuration = DivideSameDurationNotes(fmotivBuffer); for (int i = 0; i < (dividedSameDuration.Count - 1); i++) { // заносим очередной фмотив chain.FmotivList.Add((Fmotiv)dividedSameDuration[i].Clone()); // присваиваем очередной id chain.FmotivList[chain.FmotivList.Count - 1].Id = chain.FmotivList.Count - 1; } // в буфер заносим последний фмотив цепочки фмотивов нот с равнодлительностью fmotivBuffer = (Fmotiv)dividedSameDuration[dividedSameDuration.Count - 1].Clone(); // добавляем сохраненную ноту с большой длительностью for (int i = 0; i < fmotivBuffer2.NoteList.Count; i++) { fmotivBuffer.NoteList.Add((ValueNote)fmotivBuffer2.NoteList[i].Clone()); } // флаг комбинации combination = true; // флаг возрастающей последовательности, чтобы завершить фмотив - комбинация growingDurationChain = true; // убираем флаг для сбора равнодлительных нот sameDurationChain = false; // сохранили сколько нот/пауз входит в текущий буфер n = fmotivBuffer.NoteList.Count; } // если длительность предпоследнего равна длительности последнего if (TempExtractor(fmotivBuffer, ExtractNoteList(fmotivBuffer).Count - 2).Duration.Equals(TempExtractor(fmotivBuffer, ExtractNoteList(fmotivBuffer).Count - 1).Duration)) { // записываем очередную ноты в фмотив с типом последовательность равнодлительных звуков // (она уже записана, поэтому просто сохраняем число входящих в фмотив на данный момент нот/пауз) // сохранили сколько нот/пауз входит в буфер n = fmotivBuffer.NoteList.Count; } // если длительность предпоследнего больше длительности последнего if (FifthTempComparator(fmotivBuffer)) { var fmotivBuffer2 = new Fmotiv(string.Empty); // помещаем в буффер2 последнюю собранную ноту - меньшей длительности чем все равнодлительные int count = fmotivBuffer.NoteList.Count; // так как меняется в процессе for (int i = n; i < count; i++) { fmotivBuffer2.NoteList.Add((ValueNote)fmotivBuffer.NoteList[n].Clone()); fmotivBuffer.NoteList.RemoveAt(n); } // отправляем последовательность равнодлительных звуков на анализ, получаем цепочку фмотивов и заносим их в выходную последовательность foreach (Fmotiv fmotiv in DivideSameDurationNotes(fmotivBuffer)) { // заносим очередной фмотив chain.FmotivList.Add((Fmotiv)fmotiv.Clone()); // присваиваем очередной id chain.FmotivList[chain.FmotivList.Count - 1].Id = chain.FmotivList.Count - 1; } // очищаем буффер fmotivBuffer.NoteList.Clear(); // добавляем состав сохраненной ноты (паузы/лиги) с меньшей длительностью в буфер for (int i = 0; i < fmotivBuffer2.NoteList.Count; i++) { fmotivBuffer.NoteList.Add((ValueNote)fmotivBuffer2.NoteList[i].Clone()); } // убираем флаг для сбора равнодлительных нот sameDurationChain = false; // сохранили сколько нот/пауз входит в текущий буфер n = fmotivBuffer.NoteList.Count; } } else { if (growingDurationChain) { // если длительность предпоследнего меньше длительности последнего if (ForthTempComparator(fmotivBuffer)) { // записываем очередную ноты в фмотив с типом возрастающая последовательность // (она уже записана, поэтому просто сохраняем число входящих в фмотив на данный момент нот) n = fmotivBuffer.NoteList.Count; // сохранили сколько нот/пауз входит в буфер } else { // иначе если длительности равны, или последняя по длительности меньше предпоследней, то составляем новый фмотив // также сохраняем не вошедшую последнюю ноту (не удаляем ее) if (combination) { var fm = new Fmotiv(fmotivBuffer.Type + "ВП", chain.FmotivList.Count); // ЧМТВП или ПМТВП for (int i = 0; i < n; i++) { // заносим fm.NoteList.Add((ValueNote)fmotivBuffer.NoteList[0].Clone()); // удаляем fmotivBuffer.NoteList.RemoveAt(0); } // добавляем в выходную цепочку получившийся фмотив chain.FmotivList.Add((Fmotiv)fm.Clone()); // сохранили сколько нот/пауз осталось в буфере от последней не вошедшей в фмотив ноты n = fmotivBuffer.NoteList.Count; // убрали флаг сбора возрастающей последовательности growingDurationChain = false; // убрали флаг сбора возрастающей последовательности combination = false; } else { var fm = new Fmotiv("ВП", chain.FmotivList.Count); for (int i = 0; i < n; i++) { // заносим fm.NoteList.Add((ValueNote)fmotivBuffer.NoteList[0].Clone()); // удаляем fmotivBuffer.NoteList.RemoveAt(0); } // добавляем в выходную цепочку получившийся фмотив chain.FmotivList.Add((Fmotiv)fm.Clone()); // сохранили сколько нот/пауз осталось в буфере от последней не вошедшей в фмотив ноты n = fmotivBuffer.NoteList.Count; // убрали флаг сбора возрастающей последовательности growingDurationChain = false; } } } } } } } // если в буфере осталась 1 непроанализированная нота if (ExtractNoteList(fmotivBuffer).Count == 1) { // заносим ноты/паузы 1 собранной ноты в очередной фмотив с типом ЧМТ, и удаляем из буфера var fm = new Fmotiv("ЧМТ", chain.FmotivList.Count); // for (int i = 0; i < FmotivBuffer.NoteList.Count; i++) foreach (ValueNote note in fmotivBuffer.NoteList) { // заносим fm.NoteList.Add((ValueNote)note.Clone()); } // добавляем в выходную цепочку получившийся фмотив chain.FmotivList.Add((Fmotiv)fm.Clone()); // очищаем буффер fmotivBuffer.NoteList.Clear(); } // если в буфере остались непроанализированные ноты (больше 1) if (ExtractNoteList(fmotivBuffer).Count > 1) { if (sameDurationChain) { // отправляем последовательность равнодлительных звуков на анализ, получаем цепочку фмотивов и заносим их в выходную последовательность foreach (Fmotiv fmotiv in DivideSameDurationNotes(fmotivBuffer)) { // заносим очередной фмотив chain.FmotivList.Add((Fmotiv)fmotiv.Clone()); // присваиваем очередной id chain.FmotivList[chain.FmotivList.Count - 1].Id = chain.FmotivList.Count - 1; } // очищаем буффер fmotivBuffer.NoteList.Clear(); } else { if (growingDurationChain) { if (combination) { // заносим оставшиеся ноты в комбинированный фмотив ЧМТ/ПМТ + ВП и в выходную цепочку var fm = new Fmotiv(fmotivBuffer.Type + "ВП", chain.FmotivList.Count); // ЧМТВП или ПМТВП foreach (ValueNote note in fmotivBuffer.NoteList) { // заносим fm.NoteList.Add((ValueNote)note.Clone()); } // добавляем в выходную цепочку получившийся фмотив chain.FmotivList.Add((Fmotiv)fm.Clone()); // очищаем буффер fmotivBuffer.NoteList.Clear(); } else { // заносим оставшиеся ноты в фмотив ВП и в выходную цепочку var fm = new Fmotiv("ВП", chain.FmotivList.Count); foreach (ValueNote note in fmotivBuffer.NoteList) { // заносим fm.NoteList.Add((ValueNote)note.Clone()); } // добавляем в выходную цепочку получившийся фмотив chain.FmotivList.Add((Fmotiv)fm.Clone()); // очищаем буффер fmotivBuffer.NoteList.Clear(); } } } } return chain; }
// TODO: убрать все частные и заменить на общие!!!!!!!!! заменил все withoutpauses() на PauseTreatment с параметорм ignore /// <summary> /// The pause treatment. /// </summary> /// <param name="paramPauseTreatment"> /// The param pause treatment. /// </param> /// <returns> /// The <see cref="Fmotiv"/>. /// </returns> /// <exception cref="Exception"> /// Thrown if paramPauseTreatment is unknown. /// </exception> public Fmotiv PauseTreatment(ParamPauseTreatment paramPauseTreatment) { // возвращает копию этого объекта switch (paramPauseTreatment) { case ParamPauseTreatment.Ignore: // удаляем все паузы в возвращаемом объекте (0) (паузы игнорируются) var temp = (Fmotiv)Clone(); for (int i = 0; i < temp.NoteList.Count; i++) { if (temp.NoteList[i].Pitch != null && temp.NoteList[i].Pitch.Count == 0) { temp.NoteList.RemoveAt(i); i--; } } return temp; case ParamPauseTreatment.NoteTrace: // длительность паузы прибавляется к предыдущей ноте, // а она сама удаляется из текста (1) (пауза - звуковой след ноты) var temp2 = (Fmotiv)Clone(); // если пауза стоит вначале текста (и текст не пустой) то она удаляется while (temp2.NoteList.Count > 0) { if (temp2.NoteList[0].Pitch != null && temp2.NoteList[0].Pitch.Count > 0) { break; } temp2.NoteList.RemoveAt(0); } for (int i = 0; i < temp2.NoteList.Count; i++) { if (temp2.NoteList[i].Pitch.Count == 0) { // к длительности предыдущего звука добавляем длительность текущей паузы temp2.NoteList[i - 1].Duration.AddDuration((Duration)temp2.NoteList[i].Duration.Clone()); // удаляем паузу temp2.NoteList.RemoveAt(i); i--; } } return temp2; case ParamPauseTreatment.SilenceNote: // Пауза - звук тишины, рассматривается как нота без высоты звучания (2) // ничего не треуется return (Fmotiv)Clone(); default: throw new Exception("Error Fmotiv.PauseTreatment parameter contains wrong value!"); } }
/// <summary> /// The fm equals. /// </summary> /// <param name="obj"> /// The object. /// </param> /// <param name="paramPauseTreatment"> /// The param pause treatment. /// </param> /// <param name="paramEqualFM"> /// The param equal fm. /// </param> /// <returns> /// The <see cref="bool"/>. /// </returns> /// <exception cref="Exception"> /// Thrown if paramEqualFM is unknown. /// </exception> public bool FmEquals(object obj, ParamPauseTreatment paramPauseTreatment, ParamEqualFM paramEqualFM) { // для сравнения паузы не нужны, поэтому сравнивае ф-мотивы без пауз (они игнорируются, но входят в состав ф-мотива) Fmotiv self = PauseTreatment(paramPauseTreatment).TieGathered(); Fmotiv other = ((Fmotiv)obj).PauseTreatment(paramPauseTreatment).TieGathered(); int modulation = 0; bool firstTime = true; if (self.NoteList.Count != other.NoteList.Count) { // фмотивы - неодинаковы, так как входит разное количество нот return false; } for (int i = 0; i < self.NoteList.Count; i++) { // одинаково ли количество высот у нот? if (self.NoteList[i].Pitch.Count != other.NoteList[i].Pitch.Count) { // если нет - фмотивы - неодинаковы return false; } // одинаковы ли длительности у нот? if (!self.NoteList[i].Duration.Equals(other.NoteList[i].Duration)) { // если нет - фмотивы - неодинаковы return false; } if ((self.NoteList[i].Pitch.Count == 0) || (other.NoteList[i].Pitch.Count == 0)) { if (!((self.NoteList[i].Pitch.Count == 0) && (other.NoteList[i].Pitch.Count == 0))) { // если одна из нот пауза, а вторая - нет, то ф-мотивы не одинаковы return false; } // если две паузы одно длительности то идем дальше. пропуская их (считаем что это две одинаковые ноты в любом случае) } else { // если две ноты - не паузы // в зависимости от параметра учета секвентного переноса switch (paramEqualFM) { case ParamEqualFM.Sequent: // учитывая секентный перенос (Sequent) for (int j = 0; j < self.NoteList[i].Pitch.Count; j++) { if (firstTime) { // при первом сравнении вычисляем на сколько полутонов отличаются первые ноты, // последущие должны отличаться на столько же, чтобы фмотивы были равны modulation = self.NoteList[i].Pitch[j].MidiNumber - other.NoteList[i].Pitch[j].MidiNumber; firstTime = false; } // одинаковы ли при этом высоты / правильно ли присутствует секвентный перенос (модуляция) if (modulation != (self.NoteList[i].Pitch[j].MidiNumber - other.NoteList[i].Pitch[j].MidiNumber)) { return false; } } break; case ParamEqualFM.NonSequent: // без секвентного переноса (NonSequent) for (int j = 0; j < self.NoteList[i].Pitch.Count; j++) { if (self.NoteList[i].Pitch[j].MidiNumber != other.NoteList[i].Pitch[j].MidiNumber) { return false; } } break; default: throw new Exception("Error Fmotiv.ParamEqualFM parameter contains wrong value!"); } } } return true; }