Esempio n. 1
0
        /// <summary>
        /// Replaces a random concrete note pitch with a hold note.
        /// </summary>
        /// <param name="melody"> The candidate melody which contains the bar sequence to operate on. </param>
        /// <param name="barIndex"> An index of specific requested bar to operate on. </param>
        private protected virtual void ToggleToHoldNoteMutation(MelodyCandidate melody, int?barIndex)
        {
            // intialize random generator
            Random random = new Random();

            // fetch the requested bar randomly select one if no specific bar is requested
            barIndex = barIndex ?? random.Next(melody.Bars.Count);
            IBar selectedBar = melody.Bars[(int)barIndex];

            // fetch potential notes for the toggle (filter rest, hold & first notes in bar)
            IList <INote> relevantNotes = selectedBar.Notes.Where((note, noteIndex) =>
                                                                  noteIndex > 0 &&
                                                                  note.Pitch != NotePitch.RestNote &&
                                                                  note.Pitch != NotePitch.HoldNote).ToList();

            // assure at least one note as found
            if (!relevantNotes.Any())
            {
                return;
            }

            // fetch a random note from the potential notes found
            int   randomNoteIndex = random.Next(relevantNotes.Count);
            INote selectedNote    = relevantNotes[randomNoteIndex];

            // get original index of the selected note in the original sequence
            int originalNoteIndex = selectedBar.Notes.IndexOf(selectedNote);

            // replace selected note with a hold note
            INote holdNote = MusicTheoryFactory.CreateNote(NotePitch.HoldNote, selectedNote.Duration);

            selectedBar.Notes.RemoveAt(originalNoteIndex);
            selectedBar.Notes.Insert(originalNoteIndex, holdNote);
        }
Esempio n. 2
0
        /// <summary>
        ///  Constructs a new candidate with the given generation, based on the
        ///  given composition structure bar sequence.
        ///  <para> If <paramref name="includeExistingMelody"/> is set to true,
        ///  the existing melody in the given composition structure would be copied
        ///  into the bar sequence of the new instansiated candidate.
        ///  Otherwise, only time signature and chord data would be copied,
        ///  and the notes sequence in this candidate's bar sequence would be empty.</para>
        /// </summary>
        /// <param name="generation"> The generation of this candidate regarding the candidate
        /// population of the genetic algorithm which has this candidate in context. </param>
        /// <param name="compositionStructure"> Fully initialized bar sequence with
        /// time signatures, chords, and possibly notes, to base on this candidates bar sequence. </param>
        /// <param name="includeExistingMelody"> If set to true, melody from the given
        /// composition structure bar sequence would be copied into the candidate's bar sequence
        /// in addition to the time signature and chord, otherwise, only time signature and chord
        /// would be copied and notes would be set to empty collections. </param>
        internal MelodyCandidate(uint generation, IEnumerable <IBar> compositionStructure, bool includeExistingMelody = false)
        {
            // initialize the generation of the current candidate
            Generation = generation;

            // mark new candidate as "dirty" for evaluation
            IsDirty = true;

            // initialize bar sequence
            if (includeExistingMelody)
            {
                /* If this candidate should be based on an existing melody seed,
                 * initialize bar sequence to include all data from the given composition
                 * structure bar sequence: time signature, chords and notes as well. */
                Bars = new List <IBar>(compositionStructure.Count());
                foreach (IBar bar in compositionStructure)
                {
                    Bars.Add(MusicTheoryFactory.CreateBar(bar));
                }
            }
            else
            {
                /* If this candidate should not be based on any seed,
                 * initialize bars to contain only the time signature and chords
                 * from the given composition structure bar sequence, setting the
                 * notes in each bar to an empty melody. */
                Bars = CompositionContext.CloneChordProgressionBars(compositionStructure);
            }
        }
Esempio n. 3
0
        /// <summary>
        /// Randomly selects two consecutive notes in the given bar, or in a randomly selected
        /// bar if <paramref name="barIndex"/> is null, and unifies the two consecutive notes
        /// into one note by removing the consecutive note entirely and adding it's
        /// duration length to the first note.
        /// </summary>
        /// <param name="melody"> The candidate melody to operate on.</param>
        /// <param name="barIndex"> Index of the bar to do the unification in. If no bar index supplied, then a random bar would be selected. </param>
        private protected virtual void DurationUnifyMutation(MelodyCandidate melody, int?barIndex = null)
        {
            // initialization
            INote     note1, note2, newNote;
            IDuration newDuration;
            int       note1Index, note2Index;
            Random    random = new Random();

            // if no specific bar has been requested then set it randomly
            int  selectedBarIndex = barIndex ?? random.Next(melody.Bars.Count);
            IBar bar = melody.Bars[selectedBarIndex];

            // assure selected bar contains at least two notes
            if (bar.Notes.Count < 2)
            {
                return;
            }

            // randomly select two consecutive notes from within the selected bar
            note1Index = random.Next(1, bar.Notes.Count);
            note2Index = note1Index - 1;
            note1      = bar.Notes[note1Index];
            note2      = bar.Notes[note2Index];

            /* create a new note with the pitch of the first note and overall duration
             * duration of the sum of the two notes durations */
            newDuration = note1.Duration.Add(note2.Duration);
            newNote     = MusicTheoryFactory.CreateNote(note1.Pitch, newDuration);

            // replace the two consecutive note with the new note
            bar.Notes.RemoveAt(note1Index);
            bar.Notes.Insert(note1Index, newNote);
            bar.Notes.RemoveAt(note2Index);
        }
Esempio n. 4
0
        /// <summary>
        /// Clones the structure and chords from the given bar chord progression
        /// bar sequence, and returns a new bar sequence which contain the same
        /// time signatures and chords as the chord progression, but with an empty
        /// list of notes in each bar (empty melody).
        /// </summary>
        /// <param name="chordProgression"> Chord progression bar sequence to clone. </param>
        /// <returns> A new bar sequence which contains the same time signature durations
        /// and the same chord progression as the given bar sequence, but with an empty melody.</returns>
        internal static IList <IBar> CloneChordProgressionBars(IEnumerable <IBar> chordProgression)
        {
            // create a new empty bar sequence for the result
            IList <IBar> clonedBars = new List <IBar>(chordProgression.Count());

            /* fill up the new bar sequence with the original bars' time signature &
            * chords but with an empty note sequence in each bar (empty melody).  */
            foreach (IBar bar in chordProgression)
            {
                clonedBars.Add(MusicTheoryFactory.CreateBar(bar.TimeSignature, bar.Chords));
            }

            // return the cloned bars
            return(clonedBars);
        }
Esempio n. 5
0
        /// <summary> Constructs an IMidiFile instance based on a MIDI file stream.</summary>
        /// <param name="stream"> stream of the midi file content. </param>
        /// <param name="disposeStream"> Set true if stream is to be closed by the constructor once it's done reading the file. </param>
        internal DryWetMidiAdapter(Stream stream, bool disposeStream = false)
        {
            // initialization
            Stream          = stream;
            stream.Position = 0;
            _midiContent    = MidiFile.Read(stream);
            _tempoMap       = _midiContent.GetTempoMap();
            IList <TrackChunk> trackChunks = _midiContent.GetTrackChunks().ToList();

            _metadataTrack = trackChunks.First();
            _metaEvents    = _metadataTrack.Events.ToList();

            // set midi title property
            Title = (((from e in _metaEvents
                       where e.EventType == MidiEventType.SequenceTrackName
                       select e).First()) as BaseTextEvent)?.Text ?? "Undefined";

            // set key signature property
            TimeSignatureEvent timeSignatureEvent = (from e in _metaEvents
                                                     where e.EventType == MidiEventType.TimeSignature
                                                     select e)?.First() as TimeSignatureEvent;

            KeySignature = MusicTheoryFactory.CreateDuration(timeSignatureEvent.Numerator, timeSignatureEvent.Denominator, false);

            // set number of bars property
            BarBeatFractionTimeSpan duration = _midiContent.GetDuration <BarBeatFractionTimeSpan>();

            NumberOfBars = (int)duration.Bars + (int)Math.Ceiling(duration.Beats / timeSignatureEvent.Numerator);

            // set BPM property
            BeatsPerMinute = (byte)(_midiContent.GetTempoMap()?.Tempo.AtTime(0).BeatsPerMinute);

            // set MIDI pitch range
            IEnumerable <DWMidiI.Note> notes = _midiContent.GetNotes();

            LowestPitch  = (NotePitch)(byte)(notes.Min(note => note.NoteNumber));
            HighestPitch = (NotePitch)(byte)(notes.Max(note => note.NoteNumber));

            // dispose stream if requested
            if (disposeStream)
            {
                stream.Dispose();
            }
        }
Esempio n. 6
0
        /// <summary>
        /// Implements a crossover between two or more candidate participants in N distinct points.
        /// </summary>
        /// <param name="participants"> Collection of candidates that are intended to paricipate
        /// as parents in the crossover proccess. </param>
        /// <param name="n"> The number of the crossover points for slicing the candidates. </param>
        /// <param name="optimizeCrossoverPointsSelection"> If set to true, an optimized wised
        /// selection of the n crossover points is made, by selecting points which reduce the intervals
        /// between the bars around the crossover points. If set to false, the crossover points
        /// are selected randomly. </param>
        /// <returns> New candidate solutions which are the offsprings of the participating candidates.
        /// The participating candidates are not changed or modified during the proccess. </returns>
        protected ICollection <MelodyCandidate> NPointCrossover(
            IList <MelodyCandidate> participants,
            int n,
            bool optimizeCrossoverPointsSelection = true)
        {
            // assure N isn't too big
            int numberOfBars = participants[0].Bars.Count;

            n = (n < numberOfBars) ? n : numberOfBars - 1;

            // initialization
            int                    currentPosition = 0;
            List <IBar>            offspring1Bars, offspring2Bars;
            MelodyCandidate        parent1, parent2, temp, offspring1, offspring2;
            List <MelodyCandidate> offsprings = new List <MelodyCandidate>(2 * n);

            // generate n uniqe crossover points
            int[] crossoverPoints;

            // crossover each pair of parent particpants
            for (int i = 0; i < participants.Count; i++)
            {
                // set first parent
                parent1 = participants[i];

                // pair him with each other parent participant
                for (int j = i + 1; j < participants.Count; j++)
                {
                    // set second parent participant
                    parent2 = participants[j];

                    // iniate new empty twin offsprings
                    offspring1Bars = new List <IBar>(numberOfBars);
                    offspring2Bars = new List <IBar>(numberOfBars);

                    // select n crossover points, either wisely or randomly
                    crossoverPoints = optimizeCrossoverPointsSelection ?
                                      SelectOptimizedCrossoverPoints(parent1, parent2, n) :
                                      SelectRandomCrossoverPoints(ChordProgression.Count, n);

                    // do the actual crossover
                    currentPosition = 0;
                    foreach (int crossoverPoint in crossoverPoints)
                    {
                        while (currentPosition < crossoverPoint)
                        {
                            offspring1Bars.Add(MusicTheoryFactory.CreateBar(parent1.Bars[currentPosition]));
                            offspring2Bars.Add(MusicTheoryFactory.CreateBar(parent2.Bars[currentPosition]));
                            currentPosition++;
                        }

                        // swap parents roll for the crossover switch
                        temp    = parent1;
                        parent1 = parent2;
                        parent2 = temp;
                    }

                    // complete filling rest of candidate bars from the other parent
                    for (int k = currentPosition; k < numberOfBars; k++)
                    {
                        offspring1Bars.Add(MusicTheoryFactory.CreateBar(parent1.Bars[k]));
                        offspring2Bars.Add(MusicTheoryFactory.CreateBar(parent2.Bars[k]));
                    }

                    // create two new twin offsprings based on the pre-filled bars from the crossover
                    offspring1 = new MelodyCandidate(_currentGeneration, offspring1Bars, true);
                    offspring2 = new MelodyCandidate(_currentGeneration, offspring2Bars, true);

                    // add the new born offsprings to the result list
                    offsprings.Add(offspring1);
                    offsprings.Add(offspring2);
                }
            }
            return(offsprings);
        }
Esempio n. 7
0
        /// <inheritdoc cref="ReadChordsFromFile(string)"/>
        /// <param name="streamReader"> Input stream which contains the chords data. </param>
        public static IList <IBar> ReadChordsFromFile(StreamReader streamReader)
        {
            streamReader.BaseStream.Position = 0;

            // initialization
            uint        lineNumber       = 1;
            string      currentLine      = null;
            List <IBar> chordProgression = new List <IBar>();
            IBar        bar = null;
            NoteName    chordRoot;
            ChordType   chordType;
            byte        barNumerator    = 0;
            byte        barDenominator  = 0;
            byte        numberOfBeats   = 0;
            byte        totalBeatsInBar = 0;

            string[] lineTokens          = null;
            string[] barTimeSignature    = null;
            string[] chordProperties     = null;
            string   customErrorMessage  = string.Empty;
            string   genericErrorMessage = $"Error parsing chord progression file.\n";

            // parse file line after line
            while ((currentLine = streamReader.ReadLine()) != null)
            {
                // skip empty lines
                if (string.IsNullOrWhiteSpace(currentLine))
                {
                    continue;
                }

                // validate minimum tokens in line (at least a time signature and 1 chord)
                lineTokens = currentLine.Split('\b', '\t', ' ');
                if (lineTokens?.Length < 2)
                {
                    customErrorMessage = $"Line {lineNumber} must include a time signature and at least one chord.";
                    throw new FormatException(genericErrorMessage + customErrorMessage);
                }

                // set bar's time signature (first token of each line)
                barTimeSignature = lineTokens[0].Split('/');
                if ((barTimeSignature?.Length != 2) ||
                    (!Byte.TryParse(barTimeSignature?[0], out barNumerator)) ||
                    (!Byte.TryParse(barTimeSignature?[1], out barDenominator)))
                {
                    customErrorMessage = $"Invalid time signature format in line {lineNumber}: '{lineTokens[0]}'. The required format is 'numerator/denominator', for example 4/4.";
                    throw new FormatException(genericErrorMessage + customErrorMessage);
                }
                bar = MusicTheoryFactory.CreateBar(MusicTheoryFactory.CreateDuration(barNumerator, barDenominator, false));

                // set bar's chords (rest of tokens in line)
                for (int i = 1; i < lineTokens.Length; i++)
                {
                    // skip white spaces between the tokens
                    if (string.IsNullOrWhiteSpace(lineTokens[i]))
                    {
                        continue;
                    }

                    // parse chord properties: root, type & duration
                    chordProperties = lineTokens[i].Split('-');
                    if ((!Enum.TryParse(chordProperties?[0], out chordRoot)) ||
                        (!Enum.TryParse(chordProperties?[1], out chordType)) ||
                        (!Byte.TryParse(chordProperties?[2], out numberOfBeats)))
                    {
                        customErrorMessage = $"Invalid chord format in line {lineNumber}: '{lineTokens[i]}'. The required format is '{typeof(NoteName)}-{typeof(ChordType)}-DurationInBeats', for example F-Major7-2.";
                        throw new FormatException(genericErrorMessage + customErrorMessage);
                    }
                    totalBeatsInBar += numberOfBeats;
                    bar.Chords.Add(MusicTheoryFactory.CreateChord(chordRoot, chordType, MusicTheoryFactory.CreateDuration(numberOfBeats, bar.TimeSignature.Denominator)));
                }

                // validate bar's chords total duration == bar's duration
                if (bar.TimeSignature.Numerator != totalBeatsInBar)
                {
                    customErrorMessage = $"Line {lineNumber}: Total number of beats of chords in bar {chordProgression.Count + 1} which is {totalBeatsInBar} must be equal to the bar's key signature numerator, which is {bar.TimeSignature.Numerator}.";
                    throw new FormatException(genericErrorMessage + customErrorMessage);
                }

                // add curent line's bar
                chordProgression.Add(bar);
                lineNumber++;

                // clean variables old values for next iteration
                bar          = null;
                currentLine  = customErrorMessage = string.Empty;
                lineTokens   = chordProperties = barTimeSignature = null;
                barNumerator = barDenominator = numberOfBeats = totalBeatsInBar = 0;
            }
            return(chordProgression);
        }