Рис.12. Интерфейс генератора

       Все эти генераторы очень хорошо справляются со своей задачей. Наше приложение имеет ряд достоинств, которые отличают его от аналогичных приложений.

Интуитивно понятный интерфейс и интерактивность. В приложении имеется изображение гитарного грифа, на котором вы можете задавать тонику, а также на нем видны проигрываемые ноты. Также приложение имеет черты музыкального тренажера, в котором можно начать освоение гитарной соло импровизации. Есть возможность изучить аппликатуры гамм, возможность развития музыкального слуха путем «подбора» сгенерированной мелодии. Полученный генератор можно воспринимать как музыкальный редактор, так как имеется возможность отображения сетки, выбрав тонику на любом ладу любой струны, что является удобным инструментом даже для опытных музыкантов. Возможность проигрывания мелодии от другой тоники и в другом темпе в несколько кликов. Возможность корректировки полученной мелодии. Удобная система изменения, сохранения и удаления мелодий.

4.4. Листинг программы



Файл Note. cs

public static partial class Notes

  {

  public static int[] GetNotes(MyScale scale, int countOfNotes, int[] rhythm)

  {

  if (countOfNotes == 0)

  return new int[0];

  int[] notes = new int[countOfNotes];

  notes[0] = 1;

  int[] scaleIntervals = scale. scaleIntervals;

  /* Создадим массив сдвигов и массив вероятностей выбора этого сдвига */

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

  int[] shiftValues = new int[scaleIntervals. Length + 1];

  int[] shiftProbab = MyRandom. GetProbabilities(shiftValues, scale. scaleName);

  MyRandom shifts = new MyRandom(shiftValues, shiftProbab);

  Random random = new Random();

  /* Сдвиг - на сколько ступеней гаммы сдвигаемся. Случайная величина */

  int shift;

  /* Куда сдвигаем: 0 - вниз, 1 - наверх. Случайная величина */

  int upOrDown;

  /* Показывает, на какой ступени гаммы мы сейчас находимся(номер элемента массива) */

  int position = 0; 

  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;

  }

  int sum = 0, k = position; 

  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;

  }

  /* Случайное повышение или понижение на октаву (для разнообразия). Вероятность 0.1 */

  if (random. Next(0, 10) == 1)

  {

  if (upOrDown == 1)

  {

  sum += 12;

  }

  else

  {

  sum -= 12;

  }

  }

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

  }

  //notes[notes. Length - 1] = 1;

  return notes;        }

  Файл Rhythm. cs

public static class Rhythm

  {

  /// <summary>

  /// Возвращает длительность такта в секундах

  /// </summary>

  /// <param name="tempo">Значение темпа от -60 до -5</param>

  /// <param name="notesCount">Количество нот</param>

  /// <returns></returns>

  public static double GetDuration(int tempo, int notesCount)

  {

  return (  ((double)tempo * (-1) / 10) * (double)notesCount  ) / 8;

  }

  /// <summary>

  /// Метод возвращает массив, описывающий ритм

  /// </summary>

  /// <param name="segmentsCount">Количество долей в такте (количество нот и пауз)</param>

  /// <param name="notesCount">Количество звучащих нот</param>

  /// <returns></returns>

  public static int[] GetRhythm(int segmentsCount, int notesCount)

  { 

  HashSet<int> set = new HashSet<int>();

  for (int i = 0; i < segmentsCount; i++)

  {

  set. Add(i);

  }

  int[] rhythm = new int[segmentsCount];

  Random rand = new Random();

  for (int i = 0; i < notesCount; i++)

  {

  int pos = rand. Next(0, segmentsCount);

  int position = set. ElementAt(pos);

  rhythm[position] = 1;

  set. Remove(position);

  segmentsCount--;

  }

  return rhythm;

  }

  }

Файл MelodyPlayer. cs

public class SoundDevices

  {

  public OutputDevice output;

  public Channel channel;

  public SoundDevices(OutputDevice o, Channel c)

  {

  output = o;

  channel = c;

  }

  }

  public static class MelodyPlayer

  {

  public static Button FindButtonAndChangeColor(Note note, Button[,] buttons, Note[,] grifNotes, out bool visible)

  {

  for (int i = 0; i < 6; i++)

  {

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

  {

  if (grifNotes[i, j] == note)

  {

  buttons[i, j].BackColor = System. Drawing. Color. Red;

  visible = buttons[i, j].Visible;

  if(buttons[i, j].InvokeRequired)

  buttons[i, j].Invoke(new Action(() => {buttons[i, j].Visible = true;}));

  if (Form1.ActiveForm!= null && Form1.ActiveForm. InvokeRequired)

  Form1.ActiveForm. Invoke(new Action(() => { Form1.ActiveForm. Refresh(); }));

  return buttons[i, j];

  }

  }

  }

  visible = false;

  return buttons[0, 0];

  }

  /// <summary>

  /// Проигрывает мелодию в указанном ритме от указанной ноты.

  /// С фиксированной продолжительностью такта.

  /// Играется столько нот, сколько звучащих

  /// нот в ритме(столько, сколько единиц в массиве ритма)

  /// </summary>

  /// <param name="sd">Aggregates OutputDevice and Channel</param>

  /// <param name="notes">Массив нот</param>

  /// <param name="rhythm">Массив, описывающий ритм</param>

  /// <param name="tonica">Нота, от которой играется мелодия</param>

  /// <param name="duration">Продолжительность такта в секундах с точностью до тысячных</param>

  public static void PlayMelody(SoundDevices sd, int[] notes, int[] rhythm, Note tonica, double duration, Note[,] grifNotes, Button[,] buttons, CancellationToken ct)

  {

  /* Продолжительность такта в миллисекундах */

  int dur = (int)(duration * 1000); 

  int divisor = 0, k = 0, l = 0;

  while(k < notes. Length && l < rhythm. Length)

  {

  divisor++;

  if (rhythm[l] == 1)

  k++;

  l++;

  }

  /* Продолжительность звучания одной ноты(или паузы) */

  if (divisor == 0)

  return;

  int oneNoteDur = dur / divisor;

  Note note = Note. A0;

  int j = 0;

  for (int i = 0; i < rhythm. Length; i++)

  {

  if (ct. IsCancellationRequested)

  return;

  if (rhythm[i] == 0)

  {

  System. Threading. Thread. Sleep(oneNoteDur);

  }

  else

  {

  /* 40 - E2,

  * 79 - G5 */

  int n = ((int)tonica - 1 + notes[j]);

  while (n > 79)

  n = n - 12;

  while (n < 40)

  n = n + 12;

  sd. output. SendNoteOff(sd. channel, note, 80);

  note = (Note)n;

  sd. output. SendNoteOn(sd. channel, note, 80);

  bool visible;

  Button button = FindButtonAndChangeColor(note, buttons, grifNotes, out visible); 

  System. Threading. Thread. Sleep(oneNoteDur); 

  button. BackColor = default(System. Drawing. Color);

  if (Form1.ActiveForm. InvokeRequired)

  {

  Form1.ActiveForm. Invoke(new Action(() =>

  {

  if (!visible)

  button. Visible = false;

  if (Form1.ActiveForm!= null )

  Form1.ActiveForm. Refresh();

  }));

  }

  j++;

  if (j == notes. Length)

  return;

  }

  }

  sd. output. SendNoteOff(sd. channel, note, 80);

  }

  public static void PlayMelody(SoundDevices sd, Melody melody, Note tonica, double duration, Note[,] grifNotes, Button[,] buttons, CancellationToken ct)

  {

  int[] notes = melody. Notes;

  int[] rhythm = melody. Rhythm;

  PlayMelody(sd, notes, rhythm, tonica, duration, grifNotes, buttons, ct);

  } 

  }

Файл MyRandom. cs

/// <summary>

  /// Класс для генерации псевдослучайных последовательностей

  /// с неравномерным дискретным распределением вероятностей

  /// </summary>

  class MyRandom//:Random

  {

  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