private Prefix PickRandomKey(MarkovTable frequencies) { var candidates = frequencies.Keys .Where(key => char.IsUpper(key.Elements[0][0])) .ToList(); int startIndex = RandomInt(0, candidates.Count); Prefix current = candidates.ElementAt(startIndex); return(current); }
/// <summary> /// Given a <see cref="MarkovTable"/> and an initial starting <see cref="Prefix"/>, looks up the /// prefix in the MarkovTable to construct a new prefix. This process continues until we reach /// the end of the original tokens (if ever!). /// </summary> private IEnumerable <string> RandomWalk(MarkovTable frequencies, Prefix initial) { return(EnumerableEx.Generate( initialState: initial, condition: prefix => !prefix.Elements.All(e => e == null), iterate: prefix => { string suffix = ChooseRandomSuffix(frequencies, prefix); // slide suffix onto old prefix to create new prefix var newPrefix = prefix.Elements.Skip(1).Append(suffix).ToArray(); return new Prefix(newPrefix); }, resultSelector: prefixes => prefixes.Elements.First() )); }
/// <summary> /// Create a new markov generator. /// </summary> /// <param name="tokens">source sequence of tokens to analyze</param> /// <param name="ngramSize"> /// The ngram size, i.e. how many consecutive tokens to group by. Larger values /// result in more similarity to the <paramref name="tokens"/> source. /// See https://en.wikipedia.org/wiki/N-gram /// </param> /// <param name="randomInt"> /// A function that produces a random int between two bounds. /// If not provided, <see cref="Random.Next(int, int)"/> is used. /// This parameter is intended to be used mostly in unit testing. /// </param> public MarkovGenerator(IReadOnlyCollection <string> tokens, int ngramSize, Func <int, int, int> randomInt = null) { frequencies = BuildMarkovTable(tokens, ngramSize); RandomInt = randomInt ?? new Random().Next; }
/// <summary> /// Look up the possible suffixes in <paramref name="frequencies"/> for the <paramref name="current"/> prefix. /// Each suffix has a weight -- randomly choose one based on the weighting. /// </summary> /// <returns>a randomly chosen string suffix</returns> private string ChooseRandomSuffix(MarkovTable frequencies, Prefix current) { if (!frequencies.TryGetValue(current, out SuffixFrequency[] nextFrequencies))
void Add(CompositionCategory category) { table = new MarkovTable<Note>(ORDER); foreach (var c in category.Compositions) { if (c.Tracks.Count < 2) continue; var mainSeq = NormalizeSequence((c.Tracks[0].GetMainSequence() as MelodySequence)); for (int i = 1; i < c.Tracks.Count; i++) { var accompSeq = NormalizeSequence(c.Tracks[i].GetMainSequence() as MelodySequence); if (accompSeq.TotalRestDuration() > accompSeq.TotalNoteDuration()) continue; Console.WriteLine("Adding comp {0}:{1}", c.NameTag,i); table.Add(mainSeq.ToArray(), accompSeq.ToArray()); // Console.WriteLine("Adding track"); } } }