C# является популярным высокоуровневым языком программирования, который предоставляет достаточный уровень абстрагирования от инструкций низкого уровня. Он является представителем языков, использующих эффективную парадигму ООП. Также язык C# является строго типизированным, что является плюсом к надежности системы, написанной на этом языке.

       

  2.2. Постановка задачи. Функционал приложения.



Требуется написать приложение, работающее на операционных системах семейства Windows, которое будет иметь следующий функционал:

Приложение должно генерировать случайную мелодию; Мелодия должна иметь определенный стиль; Ноты мелодии должны иметь возможность находиться в разных октавах; Возможность отображения аппликатурных сеток различных гамм; Воспроизведение мелодии при помощи средств операционной системы; Интерактивный гриф гитары для выбора тоники и отображения аппликатурных сеток различных гамм; Ползунок выбора темпа; Выбор количества нот мелодии; Подсвечивание нот, воспроизводимых в данный момент; Сохранение, изменение и удаление сгенерированных и уже сохраненных мелодий.

На вход генератору можно подавать:

Стиль; Количество нот; Темп; Тонику. Её можно выбирать непосредственно на грифе.

На выходе мы должны получить мелодию с заданными параметрами,

которую можно изменить, сохранить и удалить. После генерации, должна иметься возможность проиграть заданную мелодию с уже другими параметрами:

С другой тоникой; С другим темпом.

Мелодия должна быть по возможности благозвучной. Понятие благозвучности является субъективным, но, по крайней мере, в программе не должно быть хаотичного перебора нот.

НЕ нашли? Не то? Что вы ищете?

2.3. Кодирование гаммы



       Гамма задается шаблоном, про который мы говорили в параграфе 1.3. Как мы выяснили, шаблон является циклическим. Отсюда следует, что такой шаблон будет иметь фиксированный размер и будет задаваться лишь набором расстояний от соседних нот. Приведем пример кодирования Минорной гаммы. Номера полутонов Минорной гаммы выглядят так:

1        3        4        6        8        9        11        13

Наглядное представление шаблона изображено на рис.1, с.11.

Таким образом расстояния между соседними нотами гаммы такие:

2        1        2        2        1        2        2

       Такое определение гаммы (или лада) никак не противоречит определению из теории музыки. Наоборот, оно полностью ему тождественно.

       «1        Ѕ        1        1        Ѕ        1        1        Знакомьтесь. Это минорный лад. Или натуральный минор. Это его структура»3.

Это и будет нашим шаблоном гаммы. В таком представлении у нас будут храниться данные о гамме. Получается, что в машинном представлении гамма хранится в виде массива целых чисел: расстояний между соседними нотами. Как мы видим, их количество на один меньше, чем количество нот в гамме, что совершенно логично.

Вот пример кода программы, объявление массива, хранящего структуру гаммы:

minorScale = new int[7] { 2, 1, 2, 2, 1, 2, 2 };

majorScale = new int[7] { 2, 2, 1, 2, 2, 2, 1 };

flamencoScale = new int[7] { 1, 3, 1, 2, 1, 3, 1 };

bluesScale = new int[] { 3, 2, 1, 1, 3, 2 };

flamenco2Scale = new int[7] { 1, 3, 1, 2, 1, 2, 2 };

Значение любой гаммы может быть подставлено во время выполнения программы, т. е. динамически.

Вот пример такой подстановки при отображении аппликатурной сетки.

int[] scaleIntervals = selectedScale. scaleIntervals;

//Идем по струне вправо, используя шаблон

for (int k = j; k < 16; k += scaleIntervals[index % scaleIntervals. Length])

{…}

Таким образом алгоритм отображения сетки не зависит от шаблона гаммы. Получается, что мы можем подставить любой шаблон гаммы, удовлетворяющий условию цикличности. Здесь мы также можем заметить модульную арифметику. О ней будет сказано пару слов в соответствующем параграфе.

2.4. Кодирование и генерация нот



       Сейчас мы поговорим о том, как же мы будем генерировать последовательность нот по определенной гамме. Но сначала подумаем, как мы будем представлять ноты в машинной памяти. Очевидно, что нота будет храниться в виде числового значения. Но в языке C# предусмотрено ключевое слово enum  для описания перечисления. «Тип данных, описанный с помощью этого средства, представляет из себя список именованных констант»4. По умолчанию первый перечислитель имеет значение 0, а к последующим значениям прибавляется единица.

       В нашей программе описан большой диапазон нот от ноты До в -1-й октаве до ноты Соль 9-й октавы. В нем хранится 127 значений нот.

       Так выглядит новый тип данных Note:

public enum Note

  {

       …

  /// <summary>C in octave 4, also known as Middle C.</summary>

  C4 = 60,

  /// <summary>C# in octave 4.</summary>

  CSharp4 = 61,

  /// <summary>D in octave 4.</summary>

  D4 = 62,

  /// <summary>D# in octave 4.</summary>

  DSharp4 = 63,

       …

  }

       Если у нас имеется шаблон гаммы, то мы имеем определенную последовательность допустимых нот. Т. е. не все числовые значения принадлежат гамме. Выбрав гамму, мы выбираем себе область допустимых значений. Значения эти относительны. Они зависят от тоники. Если принимать тонику за 1, то можно получить список допустимых нот для каждой гаммы. Например, список допустимых нот для Минорной гаммы:

1        3        4        6        8        9        11        13        15        16        18        20        21        …

И в обратную сторону:

-1        -3        -4        -6        -8        -9        -11        …

       Значения напоминают нам значения шаблона гаммы. Здесь мы двигались по шаблону «вверх» и «вниз». Этот список можно продолжать до бесконечности. Ну или хотя бы пока мы не переберем все наши 127 нот, которые у нас описаны. Но это необязательно, так как у нас имеется циклический шаблон гаммы.

       Алгоритм генерации нот будет примерно таким: в цикле на каждом шаге мы решаем, какую ноту выбрать. На первый взгляд, у нас очень много вариантов. Но можно все структурировать примерно в таком виде. На каждом шаге цикла мы:

Выбираем направление движения (вниз или вверх); Выбираем интервал между ступенями (не путать с интервалами нот между нотами гаммы. Они имеют абсолютное значение в полутонах); Переходим на следующий шаг цикла

Посмотрим, как это выглядит в коде:

  for (int i = 1; i < notes. Length; i++)

  {

  upOrDown =  random. Next(0, 2);

  shift = shifts. Next();

  /* Повтор ноты */

  if (shift == 0)

  {

  notes[i] = notes[i - 1];

  continue;

  }

               //Идем вверх        

  if (upOrDown == 1)

  {

  for (int j = 0; j < shift; j++)

  {

  sum = sum + scaleIntervals[k % scaleIntervals. Length];

  k++;

  }

  position = (position + shift) % scaleIntervals. Length;

  }

  Else//Идем вниз

  {

  for (int j = 0; j < shift; j++)

  {

  if (k - 1 < 0)

  k = k  + scaleIntervals. Length;

  sum = sum - scaleIntervals[(k - 1) % scaleIntervals. Length];

  k--; 

  }

  position = (position + scaleIntervals. Length - shift) % scaleIntervals. Length;

  }

  notesOut[i] = position;

       Случайными величинами являются:

Направление, вверх или вниз; Сдвиг.

Поговорим о сдвиге. Его можно сделать равновероятным, но тогда мелодия начнет слишком хаотично себя вести. Слишком велик будет «разброс» нот. Поэтому лучше всего ввести распределение вероятностей для каждого сдвига. В данном случае более вероятным будет сдвиг на одну ступень, и далее по убывающей. Задать такое распределение можно интуитивно, если у вас есть опыт импровизационной игры. Также можно было бы провести статистическое исследование, что является довольно затратным занятием. Получаем дискретное распределение вероятностей, заданное на сдвигах. Для этой цели был реализован класс MyRandom. Стандартный класс Random не удовлетворяет нашим требованиям, так как значения на выходе имеют равномерное распределение вероятностей. Поэтому приходится реализовывать класс, который на входе будет принимать список объектов и набор вероятностей для этих объектов, а на выходе будет давать значение с заданной вероятностью. В реализации используется равномерное распределение класса Random. Вот пример кода класса MyRandom:

Файл MyRandom. cs

class MyRandom

  {

  int[] section;

  Random random;

  /// <summary>

  /// Создает экземпляр класса с набором значений values

  /// и с набором вероятностей probabilities

  /// </summary>

  /// <param name="values">Массив значений. Число значений должно совпадать

  /// с числом вероятностей</param>

  /// <param name="probabilities">Массив вероятностей для массива значений.

  /// Число значений должно совпадать

Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 9 10 11