Introduction
This article is a full project example of a simple music ML using design patterns for converting music theories into a C# windows form application and creating cusomizable output results by via the windows form user interface. This pianist has learned to create music bar templates and use them to create musical melodies for each bar at run time with fully randomized results. This project used Sanford.Multimedia.Midi library to playing outputs, but it is possible to use this library to convert output results into MIDI file format too. I tried to create a simple music template outputs by writing all needed music theories into design patterns. This project can play many randomized melodies and all converted music theories are customizable in the user interface.
Backgrounds
- Intermediate C#
- Abstract Factory Design Pattern
- Decorator Design Pattern
- Template Method Design Pattern
- Encapsulation
- Basic Music Theories (Beginner)
- Read Sample Project In Git
Author Introduction
There is much artificial intelligence for sound and music that fall into different types. For example, sound analysis, human voice analysis, reading brain signals when hearing sounds, music assistant and writing music notes software, software for music production, and more. Some of them are amazing because they can create a work of art, and for us, the difference may be that we can create art with programming and data, and that's great. But the truth is that there are sophisticated algorithms that have been developed over the years by various researchers in a variety of technologies, including electronics and information technology. Some algorithms may not work with big data, but they have many complexities, most of which are related to analysis. This is because they are supposed to have outputs that excite humans.
I have a simple ML example if you like to start art programming. For example, if you want to create a music note writing program, you need to create all basics theories like what I did categorize in this project and include more of yours. I am interested in auto-generating music algorithms so I created this project based on simple auto-generating melodies for 4/4 and 3/4 music bars. But you need to discover patterns of harmony, rhythm, and style in music to create full music to excite humans. The fact is that this requires a professional team that does not fit into an article.
Some music applications compose by synthesizer, so you have to work with frequencies, and your data mining maybe have to render big data. Then, you will have a difficult time finding fast algorithms for rendering.
For example, you have to listen to a song, and detect the tempo of the music, scale, and rhythm and export all sounds of track separated by vocals, instruments, or drums.
Therefore, you need to write your algorithms by frequencies in these steps:
- Find an algorithm to divide all instrumental, drums, effects and vocal frequencies from each other
- Find an algorithm to detect ticks of tempo and a clean timeline
- Find an algorithm to detect all bars of the soundtrack by using detected tempo and timeline of the track by the rhythm
- Find an algorithm to detect each beat of the detected bars
- Find an algorithm to detect how many notes are in each detected bar by their properties like divided sounds
- Find an algorithm to detect which frequency is used for starting note
- Find an algorithm to detect the notes of the music scale by detected first note frequency
"Each step need using some ML libraries too, you can not write these algorithms without the help of other AI libraries, for example you have to detect which frequency is for an instrument"
Here are some complexities in each step, for example in step 7 (scales), some music has modulation in scales, and you have to discover the modulations happened in the music. Therefore, you have to separate their notes when they have a close relationship together by their notes and you need to explain that modulation happens in music, or some times some scales have descending notes that they are different with the base scale notes and they are close to another scale, so you have a difficult time detecting scale by data mining when it is difficult by human too, but it is possible.
Your music application should learn basic music theories, like what I did in the example project:
- Octaves and notes, by their properties
- Scales and their types like heptatonic or pentatonic and more
- Tempo
- Note Values and Silence (time length for each note by tempo)
- Rhythm and bars
These basic theories are enough for example project, but I want to explain to you what probability we might have in output results of the example project for 4/4 bars in heptatonic scales.
Count of Templates = COT = 6561
Avrage of Note Count in All Templates = AVG = 10
Scale Notes Count = SNC = 7
Scales Count = SC = 14
So the minimum output result count in this project is
Count of output melodies in 4/4 bars in example project =
(COT * (AVG ^ SNC)) * SC =
(6561 * (10 ^ 7)) * 14 =
918540000000000 possible melody bars for play just for 4/4 rhythm bars
This ML can generate 5 hours of music templates for each request in a second with a tempo of 120.
You can create a table in a database and save all generated melodies in this table to check to identify any melody. This helps you to run a loop until it geta a new melody or you can find out that you need more new randomization methods for generating new melodies. Saving more properties for each melody help you to discover how to generate a piece of music. You need to read many MIDI sample music by categorized titles, like their music genre or properties by their musical theories. Then your machine has learned harmonic fluctuations by your algorithms and has many templates for an infinity full music generator. MIDI format is a standard music data for compting music. MIDI is a small size music file with many properties in detail. If you have discovered cool generating algorithms, you have so many standard music templates for your machine learning to start.
I tried to write the project based on the design patterns so that I could explain it to you by C#
Topics
- Basic Music ML Data Mining: a simple data mining for creating a randomized melody
- Notes: Create note models by MIDI note number for usage by Sanford.Multimedia.Midi library
- Tempo and Bars: Create tempo interface and models and create a tempo calculator for note values in each rhythm bar
- Bar Template Creator Loop: use a simple algorithm for rendering many templates at run time in less than a second by using models and a loop
- Decorating Scales: use decorate design pattern for music scales
- Generate Method: use template method design pattern for melody generator method
- Generator: use the abstract factory design pattern for creating a generator factory
- Randomization: needed methods for creating note orders in melody generator with encapsulation
- Windows Form: create a user interface by all parameters for listening to the run time generated melodies
Topic 1 - Basic Music ML Data Mining
It is simple if you just want to create a melody for a bar of a rhythm. I did play all my templates in a queue without a data mining selection algorithm and just by random selection and sometimes interesting results are heard. Now imagine if you could find a simple algorithm to create a melody with many bars, you could have great results. But to make full music, you have to analyze other music and learn their harmonies based on data and base your program to composing by popular mix and mastering ways. There are many algorithms that can convert a simple one note for the human with exciting frequency modes, while you can't create such a work with any instrument, the basic old algorithm example is Echo for Vocals.
This project needs a simple data mining for generating a melody for a bar. If we define how a melody is created in a bar, then we can reach the melody with a few simple choices. Each bar has a queue of notes with a playtime length. Each length is calculated by tempo number and the sum of the note value lengths are standard in each bar for each rhythm by tempo.
In each place of the bar note values, we can put a note selected from a requested scale. Now if your scale has 7 notes and your template is created by 4 notes, then you have 4^7 possible melodies for your template.
So you just need to do the simple steps below:
- Step 1: Create a list of notes from requested or randomized octave
- Step 2: Create a separate note list by requested scale from the created list in step 1
- Step 3: Choose a template by requested tempo and rhythm bar, length of each note value must calculate by tempo number
- Step 4: Choose a note for starting a melody and select notes with a count of your template to create an int array of note numbers for usage in next step
- Step 5: Using a randomization method by the requested randomize type and use note numbers array for selection in the randomize method
- Step 6: Create a queue of randomized results with the length of tempo note values, in this step you have to put each index of randomized note numbers into the template note index places in each beat of the bar
The usage of these steps is in topic 6.
Topic 2 - Notes
Create a note model like this with the number of MIDI note data and create a list from your notes with a loop.
- public class Note
- {
- private string name = "";
- private double frequence = 0;
- private int octave = 0;
- private int number = 0;
-
- public string Name { get { return name; } }
- public int Number { get { return number; } }
- public double Frequence { get { return frequence; } }
- public int Octave { get { return octave; } }
- }
-
-
- public static class NoteList
- {
- public static List<Note> GetList(int? ftOcv = null ,int? flOcv = null )
- {
- List<Note> result = new List<Note>();
-
- for (int i = 12; i < 128; i++)
- {
- Note n = new Note(i);
- result.Add(n);
- }
-
- if(ftOcv != null)
- {
- result = result.Where(a => a.Octave >= ftOcv).ToList();
- }
-
- if(flOcv != null)
- {
- result = result.Where(a => a.Octave <= flOcv).ToList();
- }
-
- return result;
- }
- }
Topic 3 – Tempo and Bars
Create a tempo calculator for each rhythm bar by tempo number for note values. Create your model by inherited from an interface for usage in template method design pattern in topic 6
- public class Tempo
- {
- private int _tempo = 120;
-
- public Tempo()
- {
-
- }
-
- public Tempo(int tempo)
- {
- _tempo = tempo;
- }
-
- public TempoFor4_4Bars Tempo4_4
- {
- get
- {
- return new TempoFor4_4Bars(_tempo);
- }
- }
-
- public TempoFor3_4Bars Tempo3_4
- {
- get
- {
- return new TempoFor3_4Bars(_tempo);
- }
- }
- }
-
- public interface ITempoForBars
- {
- double ComputeBarLength { get; }
- double CountOfBeats { get; }
- double BarLength { get; }
- double QuarterLength { get; }
- double DottedQuarterLength { get; }
- double TripletQuarterLength { get; }
- bool SupportingHalfLength { get; }
- double HalfLength { get; }
- double EighthLength { get; }
- double DottedEighthLength { get; }
- double TripletEighthLength { get; }
- double SixteenthLength { get; }
- double DottedSixteenthLength { get; }
- double TripletSixteenthLength { get; }
- double ThirtySecondLength { get; }
- double DottedThirtySecondLength { get; }
- double TripletThirtySecondLength { get; }
- double SixtyFourthLength { get; }
- double DottedSixtyFourthLength { get; }
- double TripletSixtyFourthLength { get; }
- double HundredTwentyEighthLength { get; }
- }
-
- public class TempoFor4_4Bars : ITempoForBars
- {
- private readonly short _binary = 2;
- private readonly int _tempoBaseNumber = 120;
- private readonly int _minute = 60000;
- private int _tempo = 120;
- private int _distance = 0;
-
- public TempoFor4_4Bars(int tempo)
- {
- _tempo = tempo;
- _distance = _tempoBaseNumber - tempo;
- }
-
- public double ComputeBarLength
- {
- get
- {
- return QuarterLength * CountOfBeats;
- }
- }
- public double CountOfBeats { get { return 4; } }
- public double BarLength { get { return ComputeBarLength; } }
- public double QuarterLength { get { return _minute / _tempo; } }
- public double DottedQuarterLength { get { return QuarterLength + HalfLength; } }
- public double TripletQuarterLength { get { return QuarterLength / 3; } }
- public bool SupportingHalfLength { get { return CountOfBeats >= 2; } }
- public double HalfLength { get { return SupportingHalfLength ? QuarterLength * 2 : 0; } }
- public double EighthLength { get { return QuarterLength / _binary; } }
- public double DottedEighthLength { get { return EighthLength + SixteenthLength; } }
- public double TripletEighthLength { get { return EighthLength / 3; } }
- public double SixteenthLength { get { return EighthLength / _binary; } }
- public double DottedSixteenthLength { get { return SixteenthLength + ThirtySecondLength; } }
- public double TripletSixteenthLength { get { return SixteenthLength / 3; } }
- public double ThirtySecondLength { get { return SixteenthLength / _binary; } }
- public double DottedThirtySecondLength { get { return ThirtySecondLength + SixtyFourthLength; } }
- public double TripletThirtySecondLength { get { return ThirtySecondLength / 3; } }
- public double SixtyFourthLength { get { return ThirtySecondLength / _binary; } }
- public double DottedSixtyFourthLength { get { return SixtyFourthLength + HundredTwentyEighthLength; } }
- public double TripletSixtyFourthLength { get { return SixtyFourthLength / 3; } }
- public double HundredTwentyEighthLength { get { return SixtyFourthLength / _binary; } }
- }
Topic 4 – Bar Template Creator Loop
First, create a model for beats then create beat types for each bar model and run a loop to create your templates.
- public class BeatModel
- {
- public int OrderIndex { get; set; }
- public List<NoteValueLengthIdentity> NoteValues { get; set; }
- }
-
- public class NoteValueLengthIdentity
- {
- public int OrderIndex { get; set; }
- public NoteValue NoteValue { get; set; }
- public double NormalLength
- {
- get
- {
- TempoFor4_4Bars barModel = new TempoFor4_4Bars(120);
- switch (NoteValue)
- {
- case NoteValue.Quarter:
- return barModel.QuarterLength;
- case NoteValue.DottedQuarter:
- return barModel.DottedQuarterLength;
- case NoteValue.Half:
- return barModel.HalfLength;
- case NoteValue.Eighth:
- return barModel.EighthLength;
- case NoteValue.DottedEighth:
- return barModel.DottedEighthLength;
- case NoteValue.Sixteenth:
- return barModel.SixteenthLength;
- case NoteValue.DottedSixteenth:
- return barModel.DottedSixteenthLength;
- case NoteValue.ThirtySecond:
- return barModel.ThirtySecondLength;
- case NoteValue.DottedThirtySecond:
- return barModel.DottedThirtySecondLength;
- case NoteValue.SixtyFourth:
- return barModel.SixtyFourthLength;
- case NoteValue.DottedSixtyFourth:
- return barModel.DottedSixtyFourthLength;
- case NoteValue.HundredTwentyEighth:
- return barModel.HundredTwentyEighthLength;
- default:
- return 0;
- }
- }
- }
- public bool IsPulledFromPrevious { get; set; }
- }
-
- public enum NoteValue
- {
- Quarter = 0,
- DottedQuarter = 1,
- Half = 2,
- Eighth = 3,
- DottedEighth = 4,
- Sixteenth = 5,
- DottedSixteenth = 6,
- ThirtySecond = 7,
- DottedThirtySecond = 8,
- SixtyFourth = 9,
- DottedSixtyFourth = 10,
- HundredTwentyEighth = 11
- }
-
- public interface IBarTemplateModel
- {
- List<NoteValueLengthIdentity> AllNoteValues { get; }
- int NoteCount { get; }
- bool HaveError { get; }
- }
-
- public class BarTemplateModel : IBarTemplateModel
- {
- public BeatModel Beat1 { get; set; }
- public BeatModel Beat2 { get; set; }
- public BeatModel Beat3 { get; set; }
- public BeatModel Beat4 { get; set; }
- public List<NoteValueLengthIdentity> AllNoteValues
- {
- get
- {
- List<NoteValueLengthIdentity> result = new List<NoteValueLengthIdentity>();
-
- result.AddRange(Beat1.NoteValues);
- result.AddRange(Beat2.NoteValues);
- result.AddRange(Beat3.NoteValues);
- result.AddRange(Beat4.NoteValues);
-
- return result;
- }
- }
- public int NoteCount
- {
- get
- {
- return AllNoteValues.Count;
- }
- }
- public bool HaveError
- {
- get
- {
- var sum1 = Beat1.NoteValues.Sum(a => a.NormalLength);
- var sum2 = Beat2.NoteValues.Sum(a => a.NormalLength);
- var sum3 = Beat3.NoteValues.Sum(a => a.NormalLength);
- var sum4 = Beat4.NoteValues.Sum(a => a.NormalLength);
- return sum1 + sum2 + sum3 + sum4 != 2000;
- }
- }
- }
-
- public static IEnumerable<IBarTemplateModel> GetAllAvailableTemplates()
- {
- for (int i = 1; i < 10; i++)
- {
- for (int j = 1; j < 10; j++)
- {
- for (int k = 1; k < 10; k++)
- {
- for (int l = 1; l < 10; l++)
- {
- yield return new BarTemplateModel() { Beat1 = GetBeatModel1(i), Beat2 = GetBeatModel2(j), Beat3 = GetBeatModel3(k), Beat4 = GetBeatModel4(l) };
- }
- }
- }
- }
- }
-
- public static BeatModel GetBeatModel1(int swichMethod)
- {
- switch (swichMethod)
- {
- case 1:
- return SimpleBeat1(1);
- case 2:
- return SimpleBeat2(1);
- case 3:
- return SimpleBeat3(1);
- case 4:
- return SimpleBeat4(1);
- case 5:
- return SimpleBeat5(1);
- case 6:
- return SimpleBeat6(1);
- case 7:
- return SimpleBeat7(1);
- case 8:
- return SimpleBeat8(1);
- case 9:
- return SimpleBeat9(1);
- default:
- return SimpleBeat1(1);
- }
- }
-
- private static BeatModel SimpleBeat1(int i)
- {
- return new BeatModel()
- {
- OrderIndex = i,
- NoteValues = SimpleNoteValueTemp1()
- };
- }
- private static List<NoteValueLengthIdentity> SimpleNoteValueTemp2()
- {
- return new List<NoteValueLengthIdentity>()
- {
- new NoteValueLengthIdentity()
- {
- OrderIndex = 1,
- NoteValue = NoteValue.Eighth,
- IsPulledFromPrevious = false
- },
- new NoteValueLengthIdentity()
- {
- OrderIndex = 2,
- NoteValue = NoteValue.Eighth,
- IsPulledFromPrevious = false
- },
- };
- }
Topic 5 – Decorating Scales
Usage of scales is by some parameters of their properties in music theories. So decorating of scales helps for simple access of scales. Here is an example for C# decorating design pattern codes into music C# scales
- public interface IScale
- {
- ScaleType Type { get; }
- ScalePos Pos { get; }
- }
-
- public interface IHeptaScaleIdentity
- {
- string Name { get; }
- int ID { get; }
- string[] AscendingNotes { get; }
- string[] DescendingNotes { get; }
- string StartNote { get; }
- string DescendingStartNote { get; }
- string FinishNote { get; }
- string DescendingFinishNote { get; }
- }
-
- public interface IPentaScaleIdentity
- {
- string Name { get; }
- int ID { get; }
- string[] AscendingNotes { get; }
- string[] DescendingNotes { get; }
- string StartNote { get; }
- string DescendingStartNote { get; }
- string FinishNote { get; }
- string DescendingFinishNote { get; }
- }
-
- public interface IMajorHeptatonicScale : IScale
- {
- bool AcceptModelation { get; }
- int[] ModelationSupportScalesId { get; }
- }
-
- public interface IMinorHeptatonicScale : IScale
- {
- bool AcceptModelation { get; }
- int[] ModelationSupportScalesId { get; }
- }
-
- public interface IMajorPentatonicScale : IScale
- {
- bool AcceptModelation { get; }
- int[] ModelationSupportScalesId { get; }
- }
-
- public interface IMinorPentatonicScale : IScale
- {
- bool AcceptModelation { get; }
- int[] ModelationSupportScalesId { get; }
- }
-
- public class Major_Scale_Hepta : IMajorHeptatonicScale
- {
- public ScaleType Type { get { return ScaleType.Heptatonic; } }
- public ScalePos Pos { get { return ScalePos.Major; } }
- public bool AcceptModelation { get { return false; } }
- public int[] ModelationSupportScalesId { get { return null; } }
- }
-
- public class Minor_Scale_Hepta : IMinorHeptatonicScale
- {
- public ScaleType Type { get { return ScaleType.Heptatonic; } }
- public ScalePos Pos { get { return ScalePos.Minor; } }
- public bool AcceptModelation { get { return false; } }
- public int[] ModelationSupportScalesId { get { return null; } }
- }
-
- public class Major_Scale_Penta : IMajorPentatonicScale
- {
- public ScaleType Type { get { return ScaleType.Pentatonic; } }
- public ScalePos Pos { get { return ScalePos.Major; } }
- public bool AcceptModelation { get { return false; } }
- public int[] ModelationSupportScalesId { get { return null; } }
- }
-
- public class Minor_Scale_Penta : IMinorPentatonicScale
- {
- public ScaleType Type { get { return ScaleType.Pentatonic; } }
- public ScalePos Pos { get { return ScalePos.Minor; } }
- public bool AcceptModelation { get { return false; } }
- public int[] ModelationSupportScalesId { get { return null; } }
- }
-
-
- public class C_Sharp_Major_Scale_Hepta : IMajorHeptatonicScale, IHeptaScaleIdentity
- {
- IMajorHeptatonicScale Heptatonic;
- public C_Sharp_Major_Scale_Hepta(IMajorHeptatonicScale heptaScale)
- {
- this.Heptatonic = heptaScale;
- }
- public ScaleType Type { get { return this.Heptatonic.Type; } }
- public ScalePos Pos { get { return this.Heptatonic.Pos; } }
- public string Name { get { return "Heptatonic C# Major"; } }
- public int ID { get { return 1; } }
- public virtual bool AcceptModelation { get { return true; } }
- public virtual int[] ModelationSupportScalesId { get { return new int[] { 2 }; } }
- public virtual string[] AscendingNotes { get { return new string[] { "C#", "D#", "F", "F#", "G#", "A#", "C", "C#" }; } }
- public virtual string[] DescendingNotes { get { return new string[] { "C#", "D#", "F", "F#", "G#", "A#", "C", "C#" }; } }
- public virtual string StartNote { get { return "C#"; } }
- public virtual string DescendingStartNote { get { return "G#"; } }
- public virtual string FinishNote { get { return "F#"; } }
- public virtual string DescendingFinishNote { get { return "C#"; } }
- }
-
- public class C_Sharp_Minor_Scale_Hepta : IMinorHeptatonicScale, IHeptaScaleIdentity
- {
- IMinorHeptatonicScale Heptatonic;
- public C_Sharp_Minor_Scale_Hepta(IMinorHeptatonicScale heptaScale)
- {
- this.Heptatonic = heptaScale;
- }
- public ScaleType Type { get { return this.Heptatonic.Type; } }
- public ScalePos Pos { get { return this.Heptatonic.Pos; } }
- public string Name { get { return "Heptatonic C# Minor"; } }
- public int ID { get { return 2; } }
- public virtual bool AcceptModelation { get { return true; } }
- public virtual int[] ModelationSupportScalesId { get { return new int[] { 1 }; } }
- public virtual string[] AscendingNotes { get { return new string[] { "C#", "D#", "E", "F#", "G#", "A#", "B#", "C#" }; } }
- public virtual string[] DescendingNotes { get { return new string[] { "C#", "D#", "E", "F#", "G#", "A#", "B#", "C#" }; } }
- public virtual string StartNote { get { return "C#"; } }
- public virtual string DescendingStartNote { get { return "G#"; } }
- public virtual string FinishNote { get { return "F#"; } }
- public virtual string DescendingFinishNote { get { return "C#"; } }
- }
Topic 6 – Generate Mehod
Create a generate method for using template method design pattern for run steps in topic 1
- public abstract class GenerateTemplateCreator
- {
- public static PlayLineHarmony GetResult(ITempoForBars tempo, IBarTemplateModel template, List<Note.Note> noteList, PlayOrderType orderType)
- {
- PlayLineHarmony playLine = new PlayLineHarmony();
- playLine.Instrument = Sanford.Multimedia.Midi.GeneralMidiInstrument.Accordion;
- playLine.Order = 1;
- playLine.Vol = 127;
- playLine.NoteQueue = new List<PlayLineNotes>();
- Random r = new Random();
- var repeatRandom = r.Next(0, 19);
- var minOctav = r.Next(3, 5);
- var maxOctav = r.Next(5, 7);
- var skipRandom = r.Next(1, 8);
- int[] notes = noteList.Where(a => a.Octave >= minOctav && a.Octave <= maxOctav).Skip(skipRandom).Take(template.NoteCount).OrderByDescending(a => a.Number).Select(a => a.Number).ToArray();
- if (orderType == PlayOrderType.Ascending)
- notes = notes.OrderBy(a => a).ToArray();
- else if (orderType == PlayOrderType.Randomize)
- notes = notes.Randomize().Take(notes.Length).ToArray();
- for (int j = 0; j < notes.Length; j++)
- {
- var newNote = GetNote(playLine.NoteQueue.Count + 1, tempo, template.AllNoteValues[j].NoteValue, noteList, notes[j]);
- playLine.NoteQueue.Add(newNote);
- }
-
- return playLine;
- }
-
- private static PlayLineNotes GetNote(int order, ITempoForBars tempo, NoteValue value, List<Note.Note> noteList, int noteNumber)
- {
- return new PlayLineNotes()
- {
- IsPlayed = false,
- Length = tempo.LengthByTempo(value),
- Note = noteNumber != 0 ? noteList.FirstOrDefault(a => a.Number == noteNumber) : null,
- Order = order,
- Silence = false
- };
- }
- }
-
- public enum PlayOrderType
- {
- Ascending = 0,
- Descending = 1,
- Randomize = 2
- }
Topic 7 – Generator
I created the generator classes by the abstract factory design pattern to using divided by scales and keys or rhythm generate.
- public class GenerateTemplate
- {
- private HeptatonicGenerator _heptatonicGenerator;
- private PentatonicGenerator _pentatonicGenerator;
-
-
- public GenerateTemplate(TemplateGeneratorFactory factory)
- {
- _heptatonicGenerator = factory.HeptatonicGenerator();
- _pentatonicGenerator = factory.PentatonicGenerator();
- }
-
- public HeptatonicGenerator GenerateHeptatonic()
- {
- return _heptatonicGenerator;
- }
-
- public PentatonicGenerator GeneratePentatonic()
- {
- return _pentatonicGenerator;
- }
- }
-
- public class DescendingFactory : TemplateGeneratorFactory
- {
- public override PentatonicGenerator PentatonicGenerator()
- {
- return new PentatonicDescendingTemplate();
- }
- public override HeptatonicGenerator HeptatonicGenerator()
- {
- return new HeptatonicDescendingTemplate();
- }
- }
-
- public class AccendingFactory : TemplateGeneratorFactory
- {
- public override PentatonicGenerator PentatonicGenerator()
- {
- return new PentatonicAscendingTemplate();
- }
- public override HeptatonicGenerator HeptatonicGenerator()
- {
- return new HeptatonicAscendingTemplate();
- }
- }
-
- public abstract class TemplateGeneratorFactory
- {
- public abstract PentatonicGenerator PentatonicGenerator();
- public abstract HeptatonicGenerator HeptatonicGenerator();
- }
-
- public abstract class HeptatonicGenerator
- {
- public abstract PlayLineHarmony Get4_4Templates(ITempoForBars tempo, IBarTemplateModel template, List<Note.Note> noteList, PlayOrderType orderType);
- public abstract PlayLineHarmony Get3_4Templates(ITempoForBars tempo, IBarTemplateModel template, List<Note.Note> noteList, PlayOrderType orderType);
- }
-
- public abstract class PentatonicGenerator
- {
- public abstract PlayLineHarmony Get4_4Templates(ITempoForBars tempo, IBarTemplateModel template, List<Note.Note> noteList, PlayOrderType orderType);
- public abstract PlayLineHarmony Get3_4Templates(ITempoForBars tempo, IBarTemplateModel template, List<Note.Note> noteList, PlayOrderType orderType);
- }
-
- public class HeptatonicAscendingTemplate : HeptatonicGenerator
- {
- public override PlayLineHarmony Get4_4Templates(ITempoForBars tempo, IBarTemplateModel template, List<Note.Note> noteList, PlayOrderType orderType)
- {
- return GenerateTemplateCreator.GetResult(tempo,template,noteList,orderType);
- }
- public override PlayLineHarmony Get3_4Templates(ITempoForBars tempo, IBarTemplateModel template, List<Note.Note> noteList, PlayOrderType orderType)
- {
- return GenerateTemplateCreator.GetResult(tempo, template, noteList, orderType);
- }
- }
-
- public class PentatonicAscendingTemplate : PentatonicGenerator
- {
- public override PlayLineHarmony Get4_4Templates(ITempoForBars tempo, IBarTemplateModel template, List<Note.Note> noteList, PlayOrderType orderType)
- {
- return GenerateTemplateCreator.GetResult(tempo, template, noteList, orderType);
- }
- public override PlayLineHarmony Get3_4Templates(ITempoForBars tempo, IBarTemplateModel template, List<Note.Note> noteList, PlayOrderType orderType)
- {
- return GenerateTemplateCreator.GetResult(tempo, template, noteList, orderType);
- }
- }
-
- public class HeptatonicDescendingTemplate : HeptatonicGenerator
- {
- public override PlayLineHarmony Get4_4Templates(ITempoForBars tempo, IBarTemplateModel template, List<Note.Note> noteList, PlayOrderType orderType)
- {
- return GenerateTemplateCreator.GetResult(tempo, template, noteList, orderType);
- }
- public override PlayLineHarmony Get3_4Templates(ITempoForBars tempo, IBarTemplateModel template, List<Note.Note> noteList, PlayOrderType orderType)
- {
- return GenerateTemplateCreator.GetResult(tempo, template, noteList, orderType);
- }
- }
-
- public class PentatonicDescendingTemplate : PentatonicGenerator
- {
- public override PlayLineHarmony Get4_4Templates(ITempoForBars tempo, IBarTemplateModel template, List<Note.Note> noteList, PlayOrderType orderType)
- {
- return GenerateTemplateCreator.GetResult(tempo, template, noteList, orderType);
- }
- public override PlayLineHarmony Get3_4Templates(ITempoForBars tempo, IBarTemplateModel template, List<Note.Note> noteList, PlayOrderType orderType)
- {
- return GenerateTemplateCreator.GetResult(tempo, template, noteList, orderType);
- }
- }
Topic 8 – Randomization
Generator needs an encapsulation class to return a list of randomized note numbers for replace in template note values queue. Create loop methods like this:
- public class Randomization
- {
- private int[] Notes;
- private int length = 0;
- private Random r = new Random();
- private List<int> Result = new List<int>();
-
- public Randomization(PlayTypeSign sign, int[] notes)
- {
- Notes = notes;
- length = Notes.Length;
-
- switch (sign)
- {
- case PlayTypeSign.OrderNotes:
- Result = OrderNotes();
- break;
- case PlayTypeSign.Melody:
- Result = Melody();
- break;
- case PlayTypeSign.Melody2:
- Result = Melody2();
- break;
- case PlayTypeSign.Shuffle1:
- Result = Shuffle1();
- break;
- case PlayTypeSign.Shuffle2:
- Result = Shuffle2();
- break;
- case PlayTypeSign.Shuffle3:
- Result = Shuffle3();
- break;
- case PlayTypeSign.OrderAsc:
- Result = OrderAsc();
- break;
- case PlayTypeSign.OrderDes:
- Result = OrderDes();
- break;
- case PlayTypeSign.OrderAscBy2Step:
- Result = OrderAscBy2Step();
- break;
- case PlayTypeSign.OrderDesBy2Step:
- Result = OrderDesBy2Step();
- break;
- case PlayTypeSign.OrderAscBy3Step:
- Result = OrderAscBy3Step();
- break;
- case PlayTypeSign.OrderDesBy3Step:
- Result = OrderDesBy3Step();
- break;
- case PlayTypeSign.OrderingStep3:
- Result = OrderingStep3();
- break;
- case PlayTypeSign.OrderingStep3Backward:
- Result = OrderingStep3Backward();
- break;
- case PlayTypeSign.GetByBase:
- Result = GetByBase();
- break;
- default:
- break;
- }
- }
-
- public List<int> GetList()
- {
- return Result;
- }
-
- public List<int> GetByBase()
- {
- List<int> result = new List<int>();
-
- for (int i = 0; i < length && result.Count < length; i++)
- {
- var randomPlace = r.Next(0, length);
- result.Add(Notes[i]);
- if (randomPlace % 2 == 0)
- result.Add(0);
- result.Add(Notes[randomPlace]);
- }
-
- return result;
- }
-
- public List<int> OrderAsc()
- {
- List<int> result = new List<int>();
- Notes = Notes.OrderBy(a => Guid.NewGuid()).ToArray();
- for (int i = 0; i < length && result.Count < length; i++)
- {
- result.Add(Notes[i + 1 < length ? i + 1 : i]);
- result.Add(Notes[i + 2 < length ? i + 2 : i]);
- }
-
- return result;
- }
-
- public List<int> OrderDes()
- {
- List<int> result = new List<int>();
- Notes = Notes.OrderBy(a => Guid.NewGuid()).ToArray();
- for (int i = length; i >= 0 && result.Count < length; i--)
- {
- result.Add(Notes[i - 1 > 0 ? i - 1 : i + 1]);
- result.Add(Notes[i - 2 > 0 ? i - 2 : i]);
- }
-
- return result;
- }
-
- public List<int> OrderingStep3()
- {
- List<int> result = new List<int>();
- Notes = Notes.OrderBy(a => a).ToArray();
- int last = 0;
-
- for (int i = 0; i < length && result.Count < length; i++)
- {
- last = last + (i + 3);
- result.Add(Notes[last < length ? last : i]);
- }
-
- return result;
- }
-
- public List<int> OrderingStep3Backward()
- {
- List<int> result = new List<int>();
- Notes = Notes.OrderByDescending(a => a).ToArray();
- int last = 0;
-
- for (int i = 0; i < length && result.Count < length; i++)
- {
- last = last + (i - 3);
- result.Add(Notes[last >= 0 && last < length? last : i]);
- }
-
- return result;
- }
-
- public List<int> OrderAscBy2Step()
- {
- List<int> result = new List<int>();
- Notes = Notes.OrderBy(a => a).ToArray();
- for (int i = 0; i < length && result.Count < length; i++)
- {
- result.Add(Notes[i + 1 < length ? i + 1 : i]);
- result.Add(Notes[i + 3 < length ? i + 3 : i]);
- }
-
- return result;
- }
-
- public List<int> OrderDesBy2Step()
- {
- List<int> result = new List<int>();
- Notes = Notes.OrderByDescending(a => a).ToArray();
- for (int i = length; i >= 0 && result.Count < length; i--)
- {
- result.Add(Notes[i - 1 > 0 ? i - 1 : i + 1]);
- result.Add(Notes[i - 3 > 0 ? i - 3 : i]);
- }
-
- return result;
- }
-
- public List<int> OrderAscBy3Step()
- {
- List<int> result = new List<int>();
- Notes = Notes.OrderBy(a => a).ToArray();
- for (int i = 0; i < length && result.Count < length; i++)
- {
- result.Add(Notes[i - 1 > 0 && i - 1 < length ? i - 1 : i]);
- result.Add(Notes[i + 4 < length ? i + 4 : 0]);
- result.Add(Notes[i + 2 < length ? i + 2 : i]);
- }
-
- return result;
- }
-
- public List<int> OrderDesBy3Step()
- {
- List<int> result = new List<int>();
- Notes = Notes.OrderByDescending(a => a).ToArray();
- for (int i = length; i >= 0 && result.Count < length; i--)
- {
- result.Add(Notes[i - 1 > 0 ? i - 1 : i + 1]);
- result.Add(Notes[i - 4 > 0 ? i - 4 : 0]);
- result.Add(Notes[i - 2 > 0 ? i - 2 : i]);
- }
-
- return result;
- }
-
- private List<int> OrderNotes()
- {
- List<int> result = new List<int>();
- Notes = Notes.OrderBy(a => Guid.NewGuid()).ToArray();
- int n = r.Next(1, 3);
- int mod = r.Next(3, 6);
- if (n == 1)
- for (int i = 1; i < length; i += 2)
- {
- result.Add(Notes[i]);
- result.Add(Notes[i - 1]);
- if (i % mod == 1)
- result.Add(0);
- }
-
- if (n == 2)
- for (int i = 0; i < length; i++)
- {
- if (i < length - 1)
- {
- result.Add(Notes[i + 1]);
- result.Add(Notes[i]);
- if (i % mod == 1)
- result.Add(0);
- }
- }
-
- if (n == 3)
- for (int i = length - 1; i >= 0; i--)
- {
- if (i > 1)
- {
- result.Add(Notes[i + 1]);
- result.Add(Notes[i]);
- if (i % mod == 1)
- result.Add(0);
- }
- }
-
- return result;
- }
-
- private List<int> Melody()
- {
- List<int> result = new List<int>();
- int n = r.Next(1, 4);
- int mod = r.Next(2, 6);
- for (int i = length - 1; i >= 0; i--)
- {
- for (int j = 0; j < i; j += 2)
- {
- int r1 = r.Next(1, 2);
- int r2 = r.Next(2, 3);
- int r3 = r.Next(3, 4);
- int r4 = r.Next(4, 5);
- if (i + r2 < length && i + r2 > 0)
- result.Add(Notes[i + r2]);
- result.Add(Notes[i]);
- if (i - r1 < length && i - r1 > 0)
- result.Add(Notes[i - r1]);
- if (i + r3 < length && i + r3 > 0)
- result.Add(Notes[i + r3]);
- if (i - 1 < length && i > 0)
- result.Add(Notes[i - 1]);
- if (i % mod == 0)
- result.Add(0);
- }
- }
- return result;
- }
-
- private List<int> Melody2()
- {
- List<int> result = new List<int>();
- Notes = Notes.OrderBy(a => Guid.NewGuid()).ToArray();
- int n = r.Next(1, 5);
- int mod = r.Next(2, 6);
- if (n == 1)
- for (int i = 0; i < length; i += 2)
- {
- if (i - 2 > 0)
- result.Add(Notes[i - 2]);
- if (i + 1 < length)
- result.Add(Notes[i + 1]);
- result.Add(Notes[i]);
- if (i + 2 < length)
- result.Add(Notes[i + 2]);
- if (i % mod == 1)
- result.Add(0);
- }
-
- if (n == 2)
- for (int i = 0; i < length; i += 3)
- {
- if (i - 2 > 0)
- result.Add(Notes[i - 2]);
- if (i + 1 < length)
- result.Add(Notes[i + 1]);
- if (i + 3 < length)
- result.Add(Notes[i + 3]);
- result.Add(Notes[i]);
- if (i + 2 < length)
- result.Add(Notes[i + 2]);
- if (i % mod == 1)
- result.Add(0);
- }
- if (n == 3)
- for (int i = 0; i < length; i++)
- {
- if (i + 3 < length)
- result.Add(Notes[i + 3]);
- result.Add(Notes[i]);
- if (i + 2 < length)
- result.Add(Notes[i + 2]);
- if (i + 4 < length)
- result.Add(Notes[i + 4]);
- if (i + 1 < length)
- result.Add(Notes[i + 1]);
- if (i % mod == 1)
- result.Add(0);
- }
- if (n == 4)
- for (int i = length - 1; i >= 0; i--)
- {
- if (i - 3 > 0)
- result.Add(Notes[i - 3]);
- result.Add(Notes[i]);
- if (i - 2 > 0)
- result.Add(Notes[i - 2]);
- if (i - 4 > 0)
- result.Add(Notes[i - 4]);
- if (i - 1 > 0)
- result.Add(Notes[i - 1]);
- if (i + 2 < length)
- result.Add(Notes[i + 2]);
- if (i % mod == 1)
- result.Add(0);
- }
- return result;
- }
-
- private List<int> Shuffle1()
- {
- List<int> result = new List<int>();
- Notes = Notes.OrderBy(a => Guid.NewGuid()).ToArray();
- int n = r.Next(1, 4);
- int mod = r.Next(2, 6);
- for (int i = 0; i < length; i++)
- {
- if (mod % 2 == 1)
- for (int j = 0; j < i; j += 2)
- {
- int r1 = r.Next(1, 3);
- int r2 = r.Next(1, 4);
- int r3 = r.Next(3, 5);
- int r4 = r.Next(4, 6);
- if (i + r2 < length)
- result.Add(Notes[i + r2]);
- result.Add(Notes[i]);
- if (i + r1 < length)
- result.Add(Notes[i + r1]);
- if (i + r3 < length)
- result.Add(Notes[i + r3]);
- if (i + 1 < length)
- result.Add(Notes[i + 1]);
- if (i % mod == 0)
- result.Add(0);
- }
- else
- for (int j = 0; j < i; j++)
- {
- int r1 = r.Next(2, 3);
- int r2 = r.Next(2, 4);
- int r3 = r.Next(4, 5);
- int r4 = r.Next(3, 6);
- if (i + r2 < length)
- result.Add(Notes[i + r2]);
- result.Add(Notes[i]);
- if (i + r1 < length)
- result.Add(Notes[i + r1]);
- if (i + r3 < length)
- result.Add(Notes[i + r3]);
- if (i + 1 < length)
- result.Add(Notes[i + 1]);
- if (i % mod == 1)
- result.Add(0);
- }
- }
- return result;
- }
-
- private List<int> Shuffle2()
- {
- List<int> result = new List<int>();
- Notes = Notes.OrderBy(a => Guid.NewGuid()).ToArray();
- int n = r.Next(1, 4);
- int mod = r.Next(2, 6);
- for (int i = length - 1; i >= 0; i--)
- {
- for (int j = 0; j < i; j += 2)
- {
- int r1 = r.Next(2, 3);
- int r2 = r.Next(1, 3);
- int r3 = r.Next(3, 6);
- int r4 = r.Next(2, 5);
- if (i - r2 < length && i - r2 > 0)
- result.Add(Notes[i - r2]);
- result.Add(Notes[i]);
- if (i - r1 < length && i - r1 > 0)
- result.Add(Notes[i - r1]);
- if (i - r3 < length && i - r3 > 0)
- result.Add(Notes[i - r3]);
- if (i - 1 < length && i > 0)
- result.Add(Notes[i - 1]);
- if (i % mod == 0)
- result.Add(0);
- }
- }
- return result;
- }
-
- private List<int> Shuffle3()
- {
- List<int> result = new List<int>();
- int n = r.Next(1, 4);
- int mod = r.Next(2, 6);
- for (int i = 0; i < length; i++)
- {
- for (int j = i; j >= 0; j--)
- {
- int r1 = r.Next(1, 4);
- int r2 = r.Next(1, 3);
- int r3 = r.Next(3, 6);
- int r4 = r.Next(3, 5);
- if (i + r2 < length)
- result.Add(Notes[i + r2]);
- result.Add(Notes[i]);
- if (i + r1 < length)
- result.Add(Notes[i + r1]);
- if (i + r3 < length)
- result.Add(Notes[i + r3]);
- if (i + 1 < length)
- result.Add(Notes[i + 1]);
- if (i % mod == 1)
- result.Add(0);
- }
- }
- return result;
- }
- }
-
- public enum PlayTypeSign
- {
- OrderNotes = 0,
- Melody = 1,
- Shuffle1 = 2,
- Shuffle2 = 3,
- Shuffle3 = 4,
- OrderAsc = 5,
- OrderDes = 6,
- OrderAscBy2Step = 7,
- OrderDesBy2Step = 8,
- OrderAscBy3Step = 9,
- OrderDesBy3Step = 10,
- OrderingStep3 = 11,
- OrderingStep3Backward = 12,
- GetByBase = 13,
- Melody2 = 14
- }
Topic 9 – Windows Form
Create a Windows form to hear the results by usage of design patterns like these void methods.
- private void BuildPlay()
- {
- switch (comboBoxScaleNoteOrder.SelectedIndex)
- {
- case 0:
- TemplateGeneratorFactory desGnerator = new DescendingFactory();
- GenerateTemplate generatorDes = new GenerateTemplate(desGnerator);
- BuildScaleType(generatorDes);
- break;
- case 1:
- TemplateGeneratorFactory ascGnerator = new DescendingFactory();
- GenerateTemplate generatorAsc = new GenerateTemplate(ascGnerator);
- BuildScaleType(generatorAsc);
- break;
- default:
- break;
- }
- }
-
- public void BuildScaleType(GenerateTemplate generator)
- {
- switch (comboBoxScaleType.SelectedIndex)
- {
- case 0:
- GenerateByHeptaFactory(generator.GenerateHeptatonic());
- break;
- case 1:
- GenerateByPentaFactory(generator.GeneratePentatonic());
- break;
- default:
- break;
- }
-
- }
-
- public void GenerateByPentaFactory(PentatonicGenerator pentaGenerator)
- {
- switch (comboBoxScalePos.SelectedIndex)
- {
- case 0:
- var scaleMinor = PentaMinor();
- List<Note.Note> noteListMinor = Note.NoteList.GetList(0, 9).Where(n => scaleMinor.Any(a => a == n.Name)).ToList();
- _currentPlay = pentaGenerator.Get4_4Templates(_tempoForBars, _templates[(int)numericTemplates.Value], noteListMinor, PlayOrderType());
- break;
- case 1:
- var scaleMajor = PentaMajor();
- List<Note.Note> noteListMajor = Note.NoteList.GetList(0, 9).Where(n => scaleMajor.Any(a => a == n.Name)).ToList();
- _currentPlay = pentaGenerator.Get3_4Templates(_tempoForBars, _templates[(int)numericTemplates.Value], noteListMajor, PlayOrderType());
- break;
- default:
- break;
- }
- }
-
- public void GenerateByHeptaFactory(HeptatonicGenerator heptaGenerator)
- {
- switch (comboBoxScalePos.SelectedIndex)
- {
- case 0:
- var scaleMinor = HeptaMinor();
- List<Note.Note> noteListMinor = Note.NoteList.GetList(0, 9).Where(n => scaleMinor.Any(a => a == n.Name)).ToList();
- _currentPlay = heptaGenerator.Get4_4Templates(_tempoForBars, _templates[(int)numericTemplates.Value], noteListMinor, PlayOrderType());
- break;
- case 1:
- var scaleMajor = HeptaMajor();
- List<Note.Note> noteListMajor = Note.NoteList.GetList(0, 9).Where(n => scaleMajor.Any(a => a == n.Name)).ToList();
- _currentPlay = heptaGenerator.Get3_4Templates(_tempoForBars, _templates[(int)numericTemplates.Value], noteListMajor, PlayOrderType());
- break;
- default:
- break;
- }
- }
-
- public string[] HeptaMinor()
- {
- switch (_scales[listBoxScale.SelectedIndex].Name)
- {
- case "C#":
- IMinorHeptatonicScale iMinorHeptaScaleC = new Minor_Scale_Hepta();
- C_Sharp_Minor_Scale_Hepta scaleC = new C_Sharp_Minor_Scale_Hepta(iMinorHeptaScaleC);
- if (comboBoxScaleNoteOrder.SelectedIndex == 0)
- return scaleC.AscendingNotes;
- else
- return scaleC.DescendingNotes;
- case "Hungarian":
- IMinorHeptatonicScale iMinorHeptaScaleHungarian = new Minor_Scale_Hepta();
- Hungarian_Minor_Scale_Hepta scaleHungarian = new Hungarian_Minor_Scale_Hepta(iMinorHeptaScaleHungarian);
- if (comboBoxScaleNoteOrder.SelectedIndex == 0)
- return scaleHungarian.AscendingNotes;
- else
- return scaleHungarian.DescendingNotes;
- case "Natural":
- IMinorHeptatonicScale iMinorHeptaScaleNatural = new Minor_Scale_Hepta();
- Natural_Minor_Scale_Hepta scaleNatural = new Natural_Minor_Scale_Hepta(iMinorHeptaScaleNatural);
- if (comboBoxScaleNoteOrder.SelectedIndex == 0)
- return scaleNatural.AscendingNotes;
- else
- return scaleNatural.DescendingNotes;
- case "Double Harmonic":
- IMinorHeptatonicScale iMinorHeptaScaleDouble = new Minor_Scale_Hepta();
- Double_Harmonic_Minor_Scale_Hepta scaleDouble = new Double_Harmonic_Minor_Scale_Hepta(iMinorHeptaScaleDouble);
- if (comboBoxScaleNoteOrder.SelectedIndex == 0)
- return scaleDouble.AscendingNotes;
- else
- return scaleDouble.DescendingNotes;
-
- case "Persian":
- IMinorHeptatonicScale iMinorHeptaScalePersian = new Minor_Scale_Hepta();
- Persian_Minor_Scale_Hepta scalePersian = new Persian_Minor_Scale_Hepta(iMinorHeptaScalePersian);
- if (comboBoxScaleNoteOrder.SelectedIndex == 0)
- return scalePersian.AscendingNotes;
- else
- return scalePersian.DescendingNotes;
- case "":
-
- break;
- default:
- break;
- }
- return null;
- }
Summary
In this article, we learned how to write music theories into C# using design patterns and how to create a create rhythm bars melodies generator at run time.