Skip to content

Commit

Permalink
Refactor Ornamentation (#212)
Browse files Browse the repository at this point in the history
  • Loading branch information
wbaldoumas authored Dec 14, 2024
1 parent 3d52763 commit 8ecfa2a
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ public void Process(OrnamentationCleaningItem item)
var notePair = configuration.NotePairSelector.Select(item);

var hasStrongPulseConflicts = configuration.NoteIndexPairs
.Where(noteIndexPair => noteIndexPair.OccursOnStrongPulse)
.Any(noteIndexPair => notePair.PrimaryNote.Ornamentations[noteIndexPair.PrimaryNoteIndex].IsDissonantWith(notePair.SecondaryNote.Ornamentations[noteIndexPair.SecondaryNoteIndex]));
.Any(noteIndexPair =>
noteIndexPair.OccursOnStrongPulse &&
notePair.PrimaryNote.Ornamentations[noteIndexPair.PrimaryNoteIndex].IsDissonantWith(notePair.SecondaryNote.Ornamentations[noteIndexPair.SecondaryNoteIndex])
);

if (hasStrongPulseConflicts)
{
Expand All @@ -33,8 +35,10 @@ public void Process(OrnamentationCleaningItem item)
}

var hasWeakPulseConflicts = configuration.NoteIndexPairs
.Where(noteIndexPair => !noteIndexPair.OccursOnStrongPulse)
.Any(noteIndexPair => notePair.PrimaryNote.Ornamentations[noteIndexPair.PrimaryNoteIndex].IsDissonantWith(notePair.SecondaryNote.Ornamentations[noteIndexPair.SecondaryNoteIndex]));
.Any(noteIndexPair =>
!noteIndexPair.OccursOnStrongPulse &&
notePair.PrimaryNote.Ornamentations[noteIndexPair.PrimaryNoteIndex].IsDissonantWith(notePair.SecondaryNote.Ornamentations[noteIndexPair.SecondaryNoteIndex])
);

if (hasWeakPulseConflicts && weightedRandomBooleanGenerator.IsTrue(ChanceOfCleaningWeakConflicts))
{
Expand All @@ -44,8 +48,6 @@ public void Process(OrnamentationCleaningItem item)

private void CleanConflictingOrnamentation(OrnamentationCleaningItem item)
{
var noteToClean = configuration.NoteTargetSelector.Select(item);

noteToClean.ResetOrnamentation(compositionConfiguration.DefaultNoteTimeSpan);
item.Note.ResetOrnamentation(compositionConfiguration.DefaultNoteTimeSpan);
}
}
26 changes: 18 additions & 8 deletions src/BaroquenMelody.Library/Ornamentation/CompositionDecorator.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Atrea.PolicyEngine.Processors;
using Atrea.PolicyEngine;
using BaroquenMelody.Infrastructure.Collections;
using BaroquenMelody.Library.Configurations;
using BaroquenMelody.Library.Domain;
Expand All @@ -8,12 +8,12 @@ namespace BaroquenMelody.Library.Ornamentation;

/// <inheritdoc cref="ICompositionDecorator"/>
internal sealed class CompositionDecorator(
IProcessor<OrnamentationItem> ornamentationEngine,
IProcessor<OrnamentationItem> sustainEngine,
IPolicyEngine<OrnamentationItem> ornamentationEngine,
IPolicyEngine<OrnamentationItem> sustainEngine,
CompositionConfiguration configuration
) : ICompositionDecorator
{
public void Decorate(Composition composition) => Decorate(composition, ornamentationEngine);
public void Decorate(Composition composition) => Decorate(composition, ornamentationEngine, shuffleProcessors: true);

public void ApplySustain(Composition composition) => Decorate(composition, sustainEngine);

Expand All @@ -22,22 +22,27 @@ public void Decorate(Composition composition, Instrument instrument)
var compositionContext = new FixedSizeList<Beat>(configuration.CompositionContextSize);
var beats = composition.Measures.SelectMany(static measure => measure.Beats).ToList();

Decorate(instrument, beats, compositionContext, ornamentationEngine);
Decorate(instrument, beats, compositionContext, ornamentationEngine, shuffleProcessors: true);
}

private void Decorate(Composition composition, IProcessor<OrnamentationItem> processor)
private void Decorate(Composition composition, IPolicyEngine<OrnamentationItem> processor, bool shuffleProcessors = false)
{
var compositionContext = new FixedSizeList<Beat>(configuration.CompositionContextSize);
var instruments = configuration.InstrumentConfigurations.Select(static instrumentConfiguration => instrumentConfiguration.Instrument);
var beats = composition.Measures.SelectMany(static measure => measure.Beats).ToList();

foreach (var instrument in instruments)
{
Decorate(instrument, beats, compositionContext, processor);
Decorate(instrument, beats, compositionContext, processor, shuffleProcessors);
}
}

private static void Decorate(Instrument instrument, List<Beat> beats, FixedSizeList<Beat> compositionContext, IProcessor<OrnamentationItem> processor)
private static void Decorate(
Instrument instrument,
List<Beat> beats,
FixedSizeList<Beat> compositionContext,
IPolicyEngine<OrnamentationItem> processor,
bool shuffleProcessors = false)
{
for (var i = 0; i < beats.Count; ++i)
{
Expand All @@ -53,6 +58,11 @@ private static void Decorate(Instrument instrument, List<Beat> beats, FixedSizeL

processor.Process(ornamentationItem);

if (shuffleProcessors)
{
processor.Shuffle();
}

compositionContext.Add(beats[i]);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,39 +25,59 @@
namespace BaroquenMelody.Library.Ornamentation.Engine;

[ExcludeFromCodeCoverage(Justification = "Trivial builder methods.")]
internal sealed class OrnamentationEngineBuilder(CompositionConfiguration compositionConfiguration, IMusicalTimeSpanCalculator musicalTimeSpanCalculator, ILogger logger)
internal sealed class OrnamentationEngineBuilder
{
private readonly IWeightedRandomBooleanGenerator _weightedRandomBooleanGenerator = new WeightedRandomBooleanGenerator();

private readonly IInputPolicy<OrnamentationItem> _hasNoOrnamentation = new Not<OrnamentationItem>(new HasOrnamentation());

private readonly NoteIndexPairSelector _noteIndexPairSelector = new(new NoteOnsetCalculator(musicalTimeSpanCalculator, compositionConfiguration));
private readonly NoteIndexPairSelector _noteIndexPairSelector;

private readonly OrnamentationProcessorFactory _processorFactory = new(
musicalTimeSpanCalculator,
new OrnamentationProcessorConfigurationFactory(
new ChordNumberIdentifier(compositionConfiguration),
new WeightedRandomBooleanGenerator(),
compositionConfiguration,
logger
)
);
private readonly OrnamentationProcessorFactory _processorFactory;

private readonly CompositionConfiguration _compositionConfiguration;

private readonly IMusicalTimeSpanCalculator _musicalTimeSpanCalculator;

private readonly ILogger _logger;

public OrnamentationEngineBuilder(
CompositionConfiguration compositionConfiguration,
IMusicalTimeSpanCalculator musicalTimeSpanCalculator,
ILogger logger)
{
_compositionConfiguration = compositionConfiguration;
_musicalTimeSpanCalculator = musicalTimeSpanCalculator;
_logger = logger;
_noteIndexPairSelector = new NoteIndexPairSelector(new NoteOnsetCalculator(musicalTimeSpanCalculator, compositionConfiguration));

_processorFactory = new OrnamentationProcessorFactory(
musicalTimeSpanCalculator,
new OrnamentationProcessorConfigurationFactory(
new ChordNumberIdentifier(compositionConfiguration),
new WeightedRandomBooleanGenerator(),
compositionConfiguration,
logger
),
new CleanConflictingOrnamentations(BuildOrnamentationCleaningEngine())
);
}

public IPolicyEngine<OrnamentationItem> BuildOrnamentationEngine() => PolicyEngineBuilder<OrnamentationItem>.Configure()
.WithoutInputPolicies()
.WithProcessors(_processorFactory.Create(compositionConfiguration).ToArray())
.WithOutputPolicies(new CleanConflictingOrnamentations(BuildOrnamentationCleaningEngine()))
.WithProcessors(_processorFactory.Create(_compositionConfiguration).ToArray())
.WithoutOutputPolicies()
.Build();

public IProcessor<OrnamentationItem> BuildSustainedNoteEngine() => PolicyEngineBuilder<OrnamentationItem>.Configure()
public IPolicyEngine<OrnamentationItem> BuildSustainedNoteEngine() => PolicyEngineBuilder<OrnamentationItem>.Configure()
.WithInputPolicies(
new WantsToOrnament(_weightedRandomBooleanGenerator),
new IsRepeatedNote(),
_hasNoOrnamentation,
new IsApplicableInterval(compositionConfiguration, SustainedNoteProcessor.Interval)
new IsApplicableInterval(_compositionConfiguration, SustainedNoteProcessor.Interval)
)
.WithProcessors(new SustainedNoteProcessor(musicalTimeSpanCalculator, compositionConfiguration))
.WithOutputPolicies(new LogOrnamentation(OrnamentationType.Sustain, logger))
.WithProcessors(new SustainedNoteProcessor(_musicalTimeSpanCalculator, _compositionConfiguration))
.WithOutputPolicies(new LogOrnamentation(OrnamentationType.Sustain, _logger))
.Build();

private IPolicyEngine<OrnamentationCleaningItem> BuildOrnamentationCleaningEngine()
Expand Down Expand Up @@ -99,7 +119,7 @@ and not OrnamentationType.Rest
var processor = PolicyEngineBuilder<OrnamentationCleaningItem>
.Configure()
.WithInputPolicies(new HasTargetOrnamentations(primaryOrnamentation, secondaryOrnamentation))
.WithProcessors(new OrnamentationCleaner(ornamentationCleaningConfiguration, compositionConfiguration, _weightedRandomBooleanGenerator))
.WithProcessors(new OrnamentationCleaner(ornamentationCleaningConfiguration, _compositionConfiguration, _weightedRandomBooleanGenerator))
.Build();

processors.Add(processor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using Atrea.PolicyEngine.Policies.Output;
using BaroquenMelody.Library.Enums;
using BaroquenMelody.Library.Ornamentation.Cleaning;
using LazyCart;

namespace BaroquenMelody.Library.Ornamentation.Engine.Policies.Output;

Expand All @@ -13,26 +12,22 @@ internal sealed class CleanConflictingOrnamentations(IPolicyEngine<Ornamentation
{
public void Apply(OrnamentationItem item)
{
var instruments = item.CurrentBeat.Chord.Notes.Select(note => note.Instrument).ToList();
var instrumentCombinations = new LazyCartesianProduct<Instrument, Instrument>(instruments, instruments);
var instruments = item.CurrentBeat.Chord.Notes.Select(note => note.Instrument).ToHashSet();
var processedInstrumentCombinations = new HashSet<(Instrument, Instrument)>();

for (var i = 0; i < instrumentCombinations.Size; ++i)
foreach (var otherInstrument in instruments)
{
var (instrumentA, instrumentB) = instrumentCombinations[i];

if (instrumentA == instrumentB || processedInstrumentCombinations.Contains((instrumentA, instrumentB)))
if (item.Instrument == otherInstrument || processedInstrumentCombinations.Contains((item.Instrument, otherInstrument)))
{
continue;
}

var noteA = item.CurrentBeat[instrumentA];
var noteB = item.CurrentBeat[instrumentB];
var note = item.CurrentBeat[item.Instrument];
var otherNote = item.CurrentBeat[otherInstrument];

ornamentationCleaningEngine.Process(new OrnamentationCleaningItem(noteA, noteB));
ornamentationCleaningEngine.Process(new OrnamentationCleaningItem(note, otherNote));

processedInstrumentCombinations.Add((instrumentA, instrumentB));
processedInstrumentCombinations.Add((instrumentB, instrumentA));
processedInstrumentCombinations.Add((item.Instrument, otherInstrument));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Atrea.PolicyEngine.Builders;
using Atrea.PolicyEngine.Policies.Output;
using Atrea.PolicyEngine.Processors;
using BaroquenMelody.Library.Configurations;
using BaroquenMelody.Library.Ornamentation.Utilities;
Expand All @@ -7,15 +8,17 @@ namespace BaroquenMelody.Library.Ornamentation.Engine.Processors.Factories;

internal sealed class OrnamentationProcessorFactory(
IMusicalTimeSpanCalculator musicalTimeSpanCalculator,
IOrnamentationProcessorConfigurationFactory configurationFactory
IOrnamentationProcessorConfigurationFactory configurationFactory,
IOutputPolicy<OrnamentationItem> ornamentationCleaningOutputPolicy
) : IOrnamentationProcessorFactory
{
public IEnumerable<IProcessor<OrnamentationItem>> Create(CompositionConfiguration compositionConfiguration) =>
from ornamentationConfiguration in compositionConfiguration.AggregateOrnamentationConfiguration.Configurations.Where(configuration => configuration.IsEnabled)
from processorConfiguration in configurationFactory.Create(ornamentationConfiguration)
from configuration in compositionConfiguration.AggregateOrnamentationConfiguration.Configurations
where configuration.IsEnabled
from processorConfiguration in configurationFactory.Create(configuration)
select PolicyEngineBuilder<OrnamentationItem>.Configure()
.WithInputPolicies(processorConfiguration.InputPolicies)
.WithProcessors(new OrnamentationProcessor(musicalTimeSpanCalculator, compositionConfiguration, processorConfiguration))
.WithOutputPolicies(processorConfiguration.OutputPolicies)
.WithOutputPolicies([.. processorConfiguration.OutputPolicies, ornamentationCleaningOutputPolicy])
.Build();
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,6 @@ public void Apply_Invokes_Expected_Components_Expected_Number_Of_Times()
_cleanConflictingOrnamentations.Apply(ornamentationItem);

// assert
_mockOrnamentationCleaningEngine.Received(6).Process(Arg.Any<OrnamentationCleaningItem>());
_mockOrnamentationCleaningEngine.Received(3).Process(Arg.Any<OrnamentationCleaningItem>());
}
}

0 comments on commit 8ecfa2a

Please sign in to comment.