using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Fret;
namespace FretMutations
{
/// <summary>
/// Rappresenta una mutazione, ossia una variazione casuale, sul tema proposto.
/// </summary>
public abstract class FretMutation
{
protected Random generator
= new Random
((Int32
)(DateTime.
Now.
Ticks & 0xFFFFFFFF
));
private Double weight = 1.0;
/// <summary>
/// Determina il peso di questa mutazione nell'elaborazione finale, ossia quante volte è probabile che questa
/// variazione venga scelta per modificare il tema originale dal generatore di mutazioni (FretMutationGenerator).
/// </summary>
public Double Weight { get { return weight; } set { weight = value; } }
/// <summary>
/// Applica la mutazione e restituisce il tema modificato.
/// </summary>
/// <param name="original">Il tema originale da modificare.</param>
public abstract FretPiece Apply(FretPiece original);
}
/// <summary>
/// Rappresenta una mutazione lambda. Questo tipo di variazione aleatoria può essere applicata più volte ad un singolo
/// pezzo. La probabilità che venga applicata ad ogni iterazione varia in funzione del parametro lambda, in modo che
/// all'n-esima iterazione, la probabilità che la mutazione venga applicata almeno ancora una volta è lambda^n.
/// Perciò per lambda compreso tra 0 e 1 questa probabilità diminuisce esponenzialmente.
/// </summary>
public abstract class LambdaFretMutation : FretMutation
{
protected Double lambda = 0.5;
/// <summary>
/// Indica il parametro lambda della mutazione (compreso tra 0 e 1, estremi esclusi).
/// </summary>
public Double Lambda
{
get
{ return lambda; }
set
{
lambda = value;
if (lambda < 0)
lambda *= -1;
if (lambda >= 1.0)
lambda -= Math.Truncate(lambda);
}
}
}
/// <summary>
/// Rappresenta una variazione sulla nota. E' una mutazione lambda che, presa a caso una nota, la sostituisce con un'altra
/// scelta a caso tra quelle coerenti con la tonalità corrente.
/// </summary>
public class NoteVariation : LambdaFretMutation
{
public override FretPiece Apply(FretPiece original)
{
Double threshold = 1.0;
FretPiece result = original.TypedClone();
do
{
Int32 randomIndex = this.generator.Next(original.Count);
if (original[randomIndex].GetType() == Note.Type)
{
Note note = original[randomIndex] as Note;
(result[randomIndex] as Note).Pitch = original.Key.GetDiatonicHarmonic(note.Pitch, generator.Next(2, 8));
}
threshold *= this.Lambda;
} while (generator.NextDouble() < threshold);
return result;
}
}
/// <summary>
/// Rappresenta l'aggiunta di accordi. E' una mutazione lambda che, preso a caso una nota, sviluppa su di essa un accordo
/// con terze, quarte, quinte od ottave (minori, maggiori o giuste a seconda della tonalità ).
/// </summary>
public class ChordAddition : LambdaFretMutation
{
private Byte chordNotes = 1;
/// <summary>
/// Indica il numero di note che si vuole porre in accordo (a meno della fondamentale).
/// </summary>
public Byte ChordNotes
{
get
{ return chordNotes; }
set
{
chordNotes = value;
if (chordNotes < 0)
chordNotes = 1;
else if (chordNotes > 4)
chordNotes = 4;
}
}
public override FretPiece Apply(FretPiece original)
{
Double threshold = 1.0;
FretPiece result = original.TypedClone();
do
{
Int32 randomIndex = generator.Next(original.Count);
if (original[randomIndex].GetType() == Note.Type)
{
Note note = (original[randomIndex] as Note).Clone() as Note;
Chord chord
= new Chord
();
chord.Add(note);
for(Int32 i = 0; i < chordNotes; i++)
{
Int32 interval;
Note newNote;
Double randomValue = generator.NextDouble();
if (randomValue < 0.3)
interval = 3; //terza
else if (randomValue >= 0.3 && randomValue < 0.6)
interval = 4; // quartza
else if (randomValue >= 0.6 && randomValue < 0.9)
interval = 5; // quinta
else
interval = 8;
//if (generator.NextDouble() > 0.5)
// interval = -interval; // sotto
newNote = note.Clone() as Note;
newNote.Pitch = original.Key.GetDiatonicHarmonic(note.Pitch, interval);
if (newNote != null)
chord.Add(newNote);
}
result[randomIndex] = chord;
}
threshold *= lambda;
} while (generator.NextDouble() < threshold);
return result;
}
}
/// <summary>
/// Rappresenta una trasposizione di ottava. E' una mutazione che traspone tutto il pezzo originale di una
/// ottava, sopra o sotto, scegliendo a caso.
/// </summary>
public class OctaveTransposition : FretMutation
{
public override FretPiece Apply(FretPiece original)
{
FretPiece result = original.TypedClone();
SByte octave = (SByte)(generator.NextDouble() > 0.5 ? 1 : -1);
for (Int32 i = 0; i < result.Count; i++)
{
if (result[i].GetType() == Note.Type)
(result[i] as Note).Pitch = (result[i] as Note).Pitch.TransposeByOctave(octave);
else if (result[i].GetType() == Chord.Type)
{
foreach (Note note in (result[i] as Chord))
note.Pitch = note.Pitch.TransposeByOctave(octave);
}
}
return result;
}
}
/// <summary>
/// Rappresenta una divisione di note. E' una mutazione lambda che sostituisce ad una singola nota altre due note
/// di durata dimezzata, di cui la prima ha la stessa altezza e inflessione dell'originale e la seconda è scelta a caso.
/// </summary>
public class NoteSplitting : LambdaFretMutation
{
public override FretPiece Apply(FretPiece original)
{
Double threshold = 1.0;
FretPiece result = original.TypedClone();
do
{
Int32 randomIndex = generator.Next(result.Count);
if (result[randomIndex].GetType() == Note.Type)
{
Note note = result[randomIndex] as Note;
Note nextNote;
note.
Duration = new Fraction
(note.
Duration.
Numerator, note.
Duration.
Denumerator * 2
);
nextNote = note.CloneAndTrasposeByInterval(original.Key, 1 + generator.Next(1, 5));
result.Insert(randomIndex, nextNote);
}
threshold *= lambda;
} while (generator.NextDouble() < threshold);
return result;
}
}
/// <summary>
/// Rappresenta una simmetria verticale. E' una mutazione che traspone tutto il pezzo originale in modo da ottenerne
/// una versione speculare rispetto alla tonica della tonalità .
/// </summary>
public class HarmonicSymmetry : FretMutation
{
public override FretPiece Apply(FretPiece original)
{
FretPiece result
= new FretPiece
();
result.Meter = original.Meter.Clone() as Meter;
result.Key = original.Key.Clone() as Key;
result.Tempo = original.Tempo.Clone() as Tempo;
foreach (IGuidoElement element in original)
{
try
{
if (element.GetType() == Note.Type)
{
Note note = element as Note;
Int32 offset = note.Pitch.GetDistance(original.Key.KeyNotes[0]);
result.Add(note.CloneAndTranspose(2 * offset, note.Pitch.Accidentals));
}
else if (element.GetType() == Chord.Type)
{
Chord chord
= new Chord
();
foreach (Note note in (element as Chord))
{
Int32 offset = note.Pitch.GetDistance(original.Key.KeyNotes[0]);
chord.Add(note.CloneAndTranspose(2 * offset, note.Pitch.Accidentals));
}
result.Add(chord);
}
else if (element.GetType() == Pause.Type)
result.Add((element as Pause).Clone() as Pause);
}
catch
{
throw new ArgumentOutOfRangeException
("Impossibile applicare la mutazione 'HarmonicSymmetry', poiché alcune note distano troppo dalla tonica dell'ottava centrale.");
}
}
return result;
}
}
/// <summary>
/// Rappresenta una traslazione verticale. E' una mutazione che traspone tutto il pezzo originale in su o in giù di intervalli
/// di terza, quarta, quinta o sesta (maggiori o minori), scelti a caso.
/// </summary>
public class VerticalTranslation : FretMutation
{
private static Int32
[] possibleShifts
= new Int32
[] { 3, 4, 5,
-4,
-5,
-6
};
public override FretPiece Apply(FretPiece original)
{
Int32 randomShift = possibleShifts[generator.Next(0, possibleShifts.Length)];
FretPiece result
= new FretPiece
();
randomShift = 4;
result.Meter = original.Meter.Clone() as Meter;
result.Key = original.Key.Clone() as Key;
result.Tempo = original.Tempo.Clone() as Tempo;
foreach (IGuidoElement element in original)
{
if (element.GetType() == Note.Type)
result.Add((element as Note).CloneAndTrasposeByInterval(original.Key, randomShift));
else if (element.GetType() == Chord.Type)
{
Chord chord
= new Chord
();
foreach (Note note in (element as Chord))
chord.Add(note.CloneAndTrasposeByInterval(original.Key, randomShift));
result.Add(chord);
}
else if (element.GetType() == Pause.Type)
result.Add((element as Pause).Clone() as Pause);
}
return result;
}
}
/// <summary>
/// Traspone tutto il pezzo dalla maggiore alla relativa minore, o viceversa.
/// </summary>
public class RelativeTransposition : FretMutation
{
public override FretPiece Apply(FretPiece original)
{
FretPiece result
= new FretPiece
();
Int32 offset = 0;
result.Meter = original.Meter.Clone() as Meter;
result.Key = original.Key.Clone() as Key;
result.Key.IsMajor = !original.Key.IsMajor;
result.Tempo = original.Tempo.Clone() as Tempo;
if (original.Key.IsMajor) // va in minore, -3
offset = -3;
else
offset = +3;
foreach (IGuidoElement element in original)
{
if (element.GetType() == Note.Type)
result.Add((element as Note).CloneAndTrasposeByInterval(original.Key, offset));
else if (element.GetType() == Chord.Type)
{
Chord chord
= new Chord
();
foreach (Note note in (element as Chord))
chord.Add(note.CloneAndTrasposeByInterval(original.Key, offset));
result.Add(chord);
}
else if (element.GetType() == Pause.Type)
result.Add((element as Pause).Clone() as Pause);
}
return result;
}
}
/// <summary>
/// Rappresenta il passaggio a ritmo sincopato. E' una mutazione lambda simile a NoteSplitting, ma in cui la prima delle due note costituisce 3/4 della durata totale.
/// </summary>
public class RagtimeTransformation : LambdaFretMutation
{
public override FretPiece Apply(FretPiece original)
{
Double threshold = 1.0;
FretPiece result = original.TypedClone();
do
{
Int32 randomIndex = generator.Next(result.Count);
if (result[randomIndex].GetType() == Note.Type)
{
Note note = result[randomIndex] as Note;
Note nextNote;
note.
Duration = new Fraction
(note.
Duration.
Numerator, note.
Duration.
Denumerator * 2
);
nextNote = note.CloneAndTrasposeByInterval(original.Key, 1 + generator.Next(1, 5));
note.NumberOfDots++;
nextNote.
Duration = new Fraction
(nextNote.
Duration.
Numerator, nextNote.
Duration.
Denumerator * 2
);
result.Insert(randomIndex, nextNote);
}
threshold *= lambda;
} while (generator.NextDouble() < threshold);
return result;
}
}
/// <summary>
/// Rappresenta un generatore di mutazioni, che gestisce le variazioni su un singolo tema.
/// </summary>
public class FretMutationGenerator
{
protected Random generator
= new Random
((Int32
)(DateTime.
Now.
Ticks & 0xFFFFFFFF
));
/// <summary>
/// Una lista che contiene tutte le mutazioni che questo generatore può applicare, con i rispettivi pesi e parametri.
/// </summary>
public List<FretMutation> Mutations { get; set; }
public FretMutationGenerator()
{
this.
Mutations = new List
<FretMutation
>();
}
/// <summary>
/// Applica le mutazioni contenute in Mutations con una certa casualità . La probabilità di ciascuna mutazione di
/// essere applicata è proporzionale al suo peso. Produce una sequenza di FretPiece, ciascuno mutato diversamente.
/// </summary>
/// <param name="original">Il tema originale da modificare.</param>
/// <param name="iterations">Il numero di iterazioni del processo. Ogni iterazione produce un tema modificato,
/// prendendo come originale uno dei temi già prodotti e applicando una mutazione a caso.</param>
public Fret.Fret ApplyMutations(FretPiece original, Int32 iterations)
{
Double[] mutationsProbabilities;
Double totalWeight = this.Mutations.Sum(item => item.Weight);
Double tmp;
Fret.
Fret result
= new Fret.
Fret();
mutationsProbabilities = this.Mutations.Select(item => (item.Weight / totalWeight)).ToArray();
result.Add(original);
for (Int32 i = 0; i < iterations; i++)
{
Double rnd = generator.NextDouble();
tmp = 0;
for(Int32 j = 0; j < mutationsProbabilities.Length; j++)
{
if (tmp <= rnd && rnd < tmp + mutationsProbabilities[j])
{
result.Add(this.Mutations[j].Apply(result[generator.Next(0, result.Count)]));
break;
}
tmp += mutationsProbabilities[j];
}
}
return result;
}
/// <summary>
/// Applica le mutazioni contenute in Mutations con una certa casualità . La probabilità di ciascuna mutazione di
/// essere applicata è proporzionale al suo peso. A differenza di ApplyMutations, in questo caso tutte le mutazioni vengono
/// riapplicate a un singolo pezzo.
/// </summary>
/// <param name="original">Il tema originale da modificare.</param>
/// <param name="iterations">Il numero di iterazioni del processo. Ogni iterazione produce un tema modificato,
/// prendendo come originale il tema mutato precedentemente.</param>
public Fret.Fret ApplyIncrementalMutations(FretPiece original, Int32 iterations)
{
Double[] mutationsProbabilities;
Double totalWeight = this.Mutations.Sum(item => item.Weight);
Double tmp;
Fret.
Fret result
= new Fret.
Fret();
FretPiece mutatedTheme = original.TypedClone();
mutationsProbabilities = this.Mutations.Select(item => (item.Weight / totalWeight)).ToArray();
for (Int32 i = 0; i < iterations; i++)
{
Double rnd = generator.NextDouble();
tmp = 0;
for (Int32 j = 0; j < mutationsProbabilities.Length; j++)
{
if (tmp <= rnd && rnd < tmp + mutationsProbabilities[j])
{
mutatedTheme = this.Mutations[j].Apply(mutatedTheme);
break;
}
tmp += mutationsProbabilities[j];
}
}
result.Add(mutatedTheme);
return result;
}
}
}