Skip to content

Commit

Permalink
Introduce 5/8 Meter (#193)
Browse files Browse the repository at this point in the history
  • Loading branch information
wbaldoumas authored Oct 9, 2024
1 parent 6fe88b0 commit 59b7395
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using BaroquenMelody.Library.Store.State;
using Fluxor;
using Melanchall.DryWetMidi.Common;
using Melanchall.DryWetMidi.MusicTheory;
using System.Collections.Frozen;

namespace BaroquenMelody.Library.Configurations.Services;
Expand Down Expand Up @@ -88,8 +87,13 @@ private void Randomize(Instrument instrument)
var minVelocity = ThreadLocalRandom.Next(MinMinRandomVelocity, MaxMinRandomVelocity);
var maxVelocity = ThreadLocalRandom.Next(minVelocity, MaxMaxRandomVelocity);

var minRandomIndex = compositionConfigurationState.Value.AvailableNotes.IndexOf(Notes.C1);
var maxRandomIndex = compositionConfigurationState.Value.AvailableNotes.IndexOf(Notes.C7);
var availableNotes = compositionConfigurationState.Value.AvailableNotes;

var lowerNoteBoundary = availableNotes.Skip(CompositionConfiguration.MinInstrumentRange).First();
var upperNoteBoundary = availableNotes.SkipLast(CompositionConfiguration.MinInstrumentRange).Last();

var minRandomIndex = availableNotes.IndexOf(lowerNoteBoundary);
var maxRandomIndex = availableNotes.IndexOf(upperNoteBoundary);

var minNoteIndex = ThreadLocalRandom.Next(minRandomIndex, maxRandomIndex - CompositionConfiguration.MinInstrumentRange);
var maxNoteIndex = ThreadLocalRandom.Next(minNoteIndex + CompositionConfiguration.MinInstrumentRange, Math.Min(maxRandomIndex, minNoteIndex + CompositionConfiguration.MaxInstrumentRange));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ public static class MeterExtensions
public static int BeatsPerMeasure(this Meter meter) => meter switch
{
Meter.FourFour => 4,
Meter.ThreeFour => 3,
Meter.ThreeFour => 4,
Meter.FiveEight => 4,
_ => throw new ArgumentOutOfRangeException(nameof(meter), meter, "Meter not supported.")
};

Expand All @@ -30,6 +31,7 @@ public static class MeterExtensions
{
Meter.FourFour => MusicalTimeSpan.Half,
Meter.ThreeFour => MusicalTimeSpan.Half.Dotted(1),
Meter.FiveEight => MusicalTimeSpan.Half + MusicalTimeSpan.Eighth,
_ => throw new ArgumentOutOfRangeException(nameof(meter), meter, "Meter not supported.")
};

Expand All @@ -43,6 +45,7 @@ public static class MeterExtensions
{
Meter.FourFour => "4/4",
Meter.ThreeFour => "3/4",
Meter.FiveEight => "5/8",
_ => throw new ArgumentOutOfRangeException(nameof(meter), meter, "Meter not supported.")
};

Expand All @@ -56,6 +59,7 @@ public static class MeterExtensions
{
Meter.FourFour => new TimeSignature(4, 4),
Meter.ThreeFour => new TimeSignature(3, 4),
Meter.FiveEight => new TimeSignature(5, 8),
_ => throw new ArgumentOutOfRangeException(nameof(meter), meter, "Meter not supported.")
};
}
7 changes: 6 additions & 1 deletion src/BaroquenMelody.Library/Enums/Meter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,10 @@ public enum Meter
/// <summary>
/// 3/4 time.
/// </summary>
ThreeFour
ThreeFour,

/// <summary>
/// 7/8 time.
/// </summary>
FiveEight
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ private static BitArray InitializeNoteOnsetBitArray(Meter meter)
var bitArray = meter switch
{
Meter.FourFour => new BitArray(16),
Meter.FiveEight => new BitArray(20),
Meter.ThreeFour => new BitArray(24),
_ => throw new ArgumentOutOfRangeException(nameof(meter), meter, "Unsupported meter for note onset calculation.")
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,69 +14,91 @@ internal sealed class MusicalTimeSpanCalculator : IMusicalTimeSpanCalculator
{
OrnamentationType.None when meter == Meter.FourFour => MusicalTimeSpan.Half,
OrnamentationType.None when meter == Meter.ThreeFour => MusicalTimeSpan.Half.Dotted(1),
OrnamentationType.None when meter == Meter.FiveEight => MusicalTimeSpan.Half + MusicalTimeSpan.Eighth,

OrnamentationType.PassingTone when meter == Meter.FourFour => MusicalTimeSpan.Quarter,
OrnamentationType.PassingTone when meter == Meter.ThreeFour => MusicalTimeSpan.Half,
OrnamentationType.PassingTone when meter == Meter.FiveEight => MusicalTimeSpan.Quarter,

OrnamentationType.Pickup when meter == Meter.FourFour => MusicalTimeSpan.Quarter,
OrnamentationType.Pickup when meter == Meter.ThreeFour => MusicalTimeSpan.Half,
OrnamentationType.Pickup when meter == Meter.FiveEight => MusicalTimeSpan.Quarter,

OrnamentationType.DelayedPickup when meter == Meter.FourFour => MusicalTimeSpan.Quarter.Dotted(1),
OrnamentationType.DelayedPickup when meter == Meter.ThreeFour => MusicalTimeSpan.Half + MusicalTimeSpan.Eighth,
OrnamentationType.DelayedPickup when meter == Meter.FiveEight => MusicalTimeSpan.Half,

OrnamentationType.Run when meter == Meter.FourFour => MusicalTimeSpan.Eighth,
OrnamentationType.Run when meter == Meter.ThreeFour => MusicalTimeSpan.Quarter.Dotted(1),
OrnamentationType.Run when meter == Meter.FiveEight => MusicalTimeSpan.Quarter,

OrnamentationType.DelayedPassingTone when meter == Meter.FourFour => MusicalTimeSpan.Quarter.Dotted(1),
OrnamentationType.DelayedPassingTone when meter == Meter.ThreeFour => MusicalTimeSpan.Half + MusicalTimeSpan.Eighth,
OrnamentationType.DelayedPassingTone when meter == Meter.FiveEight => MusicalTimeSpan.Half,

OrnamentationType.Turn when meter == Meter.FourFour => MusicalTimeSpan.Eighth,
OrnamentationType.Turn when meter == Meter.ThreeFour => MusicalTimeSpan.Quarter.Dotted(1),
OrnamentationType.Turn when meter == Meter.FiveEight => MusicalTimeSpan.Quarter,

OrnamentationType.InvertedTurn when meter == Meter.FourFour => MusicalTimeSpan.Eighth,
OrnamentationType.InvertedTurn when meter == Meter.ThreeFour => MusicalTimeSpan.Quarter.Dotted(1),
OrnamentationType.InvertedTurn when meter == Meter.FiveEight => MusicalTimeSpan.Quarter,

OrnamentationType.Sustain when meter == Meter.FourFour => MusicalTimeSpan.Whole,
OrnamentationType.Sustain when meter == Meter.ThreeFour => MusicalTimeSpan.Half.Dotted(1) + MusicalTimeSpan.Half.Dotted(1),
OrnamentationType.Sustain when meter == Meter.FiveEight => MusicalTimeSpan.Half + MusicalTimeSpan.Eighth + MusicalTimeSpan.Half + MusicalTimeSpan.Eighth,

OrnamentationType.DoubleTurn when meter == Meter.FourFour => MusicalTimeSpan.Sixteenth,
OrnamentationType.DoubleTurn when meter == Meter.ThreeFour => MusicalTimeSpan.Quarter + MusicalTimeSpan.Sixteenth,
OrnamentationType.DoubleTurn when meter == Meter.FiveEight => MusicalTimeSpan.Eighth.Dotted(1),

OrnamentationType.DoubleInvertedTurn when meter == Meter.FourFour => MusicalTimeSpan.Sixteenth,
OrnamentationType.DoubleInvertedTurn when meter == Meter.ThreeFour => MusicalTimeSpan.Quarter + MusicalTimeSpan.Sixteenth,
OrnamentationType.DoubleInvertedTurn when meter == Meter.FiveEight => MusicalTimeSpan.Eighth.Dotted(1),

OrnamentationType.DelayedRun when meter == Meter.FourFour => MusicalTimeSpan.Quarter,
OrnamentationType.DelayedRun when meter == Meter.ThreeFour => MusicalTimeSpan.Quarter,
OrnamentationType.DelayedRun when meter == Meter.FiveEight => MusicalTimeSpan.Quarter,

OrnamentationType.DoublePassingTone when meter == Meter.FourFour => MusicalTimeSpan.Quarter,
OrnamentationType.DoublePassingTone when meter == Meter.ThreeFour => MusicalTimeSpan.Quarter,
OrnamentationType.DoublePassingTone when meter == Meter.FiveEight => MusicalTimeSpan.Quarter,

OrnamentationType.DelayedDoublePassingTone when meter == Meter.FourFour => MusicalTimeSpan.Quarter.Dotted(1),
OrnamentationType.DelayedDoublePassingTone when meter == Meter.ThreeFour => MusicalTimeSpan.Half,
OrnamentationType.DelayedDoublePassingTone when meter == Meter.FiveEight => MusicalTimeSpan.Quarter.Dotted(1),

OrnamentationType.DecorateInterval when meter == Meter.FourFour => MusicalTimeSpan.Eighth,
OrnamentationType.DecorateInterval when meter == Meter.ThreeFour => MusicalTimeSpan.Quarter.Dotted(1),
OrnamentationType.DecorateInterval when meter == Meter.FiveEight => MusicalTimeSpan.Quarter,

OrnamentationType.DoubleRun when meter == Meter.FourFour => MusicalTimeSpan.Sixteenth,
OrnamentationType.DoubleRun when meter == Meter.ThreeFour => MusicalTimeSpan.Quarter + MusicalTimeSpan.Sixteenth,
OrnamentationType.DoubleRun when meter == Meter.FiveEight => MusicalTimeSpan.Eighth.Dotted(1),

OrnamentationType.Pedal when meter == Meter.FourFour => MusicalTimeSpan.Eighth,
OrnamentationType.Pedal when meter == Meter.ThreeFour => MusicalTimeSpan.Quarter.Dotted(1),
OrnamentationType.Pedal when meter == Meter.FiveEight => MusicalTimeSpan.Quarter,

OrnamentationType.Mordent when meter == Meter.FourFour => MusicalTimeSpan.Sixteenth,
OrnamentationType.Mordent when meter == Meter.ThreeFour => MusicalTimeSpan.Sixteenth,
OrnamentationType.Mordent when meter == Meter.FiveEight => MusicalTimeSpan.Sixteenth,

OrnamentationType.RepeatedNote when meter == Meter.FourFour => MusicalTimeSpan.Quarter,
OrnamentationType.RepeatedNote when meter == Meter.ThreeFour => MusicalTimeSpan.Half,
OrnamentationType.RepeatedNote when meter == Meter.FiveEight => MusicalTimeSpan.Quarter,

OrnamentationType.DelayedRepeatedNote when meter == Meter.FourFour => MusicalTimeSpan.Quarter.Dotted(1),
OrnamentationType.DelayedRepeatedNote when meter == Meter.ThreeFour => MusicalTimeSpan.Half + MusicalTimeSpan.Eighth,
OrnamentationType.DelayedRepeatedNote when meter == Meter.FiveEight => MusicalTimeSpan.Half,

OrnamentationType.NeighborTone when meter == Meter.FourFour => MusicalTimeSpan.Quarter,
OrnamentationType.NeighborTone when meter == Meter.ThreeFour => MusicalTimeSpan.Half,
OrnamentationType.NeighborTone when meter == Meter.FiveEight => MusicalTimeSpan.Quarter,

OrnamentationType.DelayedNeighborTone when meter == Meter.FourFour => MusicalTimeSpan.Quarter.Dotted(1),
OrnamentationType.DelayedNeighborTone when meter == Meter.ThreeFour => MusicalTimeSpan.Half + MusicalTimeSpan.Eighth,
OrnamentationType.DelayedNeighborTone when meter == Meter.FiveEight => MusicalTimeSpan.Half,

OrnamentationType.MidSustain => Zero,
OrnamentationType.Rest => Zero,
Expand All @@ -91,68 +113,89 @@ internal sealed class MusicalTimeSpanCalculator : IMusicalTimeSpanCalculator

OrnamentationType.PassingTone when meter == Meter.FourFour => MusicalTimeSpan.Quarter,
OrnamentationType.PassingTone when meter == Meter.ThreeFour => MusicalTimeSpan.Quarter,
OrnamentationType.PassingTone when meter == Meter.FiveEight => MusicalTimeSpan.Quarter.Dotted(1),

OrnamentationType.Pickup when meter == Meter.FourFour => MusicalTimeSpan.Quarter,
OrnamentationType.Pickup when meter == Meter.ThreeFour => MusicalTimeSpan.Quarter,
OrnamentationType.Pickup when meter == Meter.FiveEight => MusicalTimeSpan.Quarter.Dotted(1),

OrnamentationType.DelayedPickup when meter == Meter.FourFour => MusicalTimeSpan.Eighth,
OrnamentationType.DelayedPickup when meter == Meter.ThreeFour => MusicalTimeSpan.Eighth,
OrnamentationType.DelayedPickup when meter == Meter.FiveEight => MusicalTimeSpan.Eighth,

OrnamentationType.Run when meter == Meter.FourFour => MusicalTimeSpan.Eighth,
OrnamentationType.Run when meter == Meter.ThreeFour => MusicalTimeSpan.Eighth,
OrnamentationType.Run when meter == Meter.FiveEight => MusicalTimeSpan.Eighth,

OrnamentationType.DelayedPassingTone when meter == Meter.FourFour => MusicalTimeSpan.Eighth,
OrnamentationType.DelayedPassingTone when meter == Meter.ThreeFour => MusicalTimeSpan.Eighth,
OrnamentationType.DelayedPassingTone when meter == Meter.FiveEight => MusicalTimeSpan.Eighth,

OrnamentationType.Turn when meter == Meter.FourFour => MusicalTimeSpan.Eighth,
OrnamentationType.Turn when meter == Meter.ThreeFour => MusicalTimeSpan.Eighth,
OrnamentationType.Turn when meter == Meter.FiveEight => MusicalTimeSpan.Eighth,

OrnamentationType.InvertedTurn when meter == Meter.FourFour => MusicalTimeSpan.Eighth,
OrnamentationType.InvertedTurn when meter == Meter.ThreeFour => MusicalTimeSpan.Eighth,
OrnamentationType.InvertedTurn when meter == Meter.FiveEight => MusicalTimeSpan.Eighth,

OrnamentationType.Sustain => throw new NotSupportedException($"{nameof(OrnamentationType.Sustain)} cannot be applied to an ornamentation."),

OrnamentationType.DoubleTurn when meter == Meter.FourFour => MusicalTimeSpan.Sixteenth,
OrnamentationType.DoubleTurn when meter == Meter.ThreeFour => MusicalTimeSpan.Sixteenth,
OrnamentationType.DoubleTurn when meter == Meter.FiveEight => MusicalTimeSpan.Sixteenth,

OrnamentationType.DoubleInvertedTurn when meter == Meter.FourFour => MusicalTimeSpan.Sixteenth,
OrnamentationType.DoubleInvertedTurn when meter == Meter.ThreeFour => MusicalTimeSpan.Sixteenth,
OrnamentationType.DoubleInvertedTurn when meter == Meter.FiveEight => MusicalTimeSpan.Sixteenth,

OrnamentationType.DelayedRun when meter == Meter.FourFour => MusicalTimeSpan.Sixteenth,
OrnamentationType.DelayedRun when meter == Meter.ThreeFour => MusicalTimeSpan.Eighth,
OrnamentationType.DelayedRun when meter == Meter.FiveEight => MusicalTimeSpan.Sixteenth.Dotted(1),

OrnamentationType.DoublePassingTone when meter == Meter.FourFour => MusicalTimeSpan.Eighth,
OrnamentationType.DoublePassingTone when meter == Meter.ThreeFour => MusicalTimeSpan.Quarter,
OrnamentationType.DoublePassingTone when meter == Meter.FiveEight => MusicalTimeSpan.Eighth.Dotted(1),

OrnamentationType.DelayedDoublePassingTone when meter == Meter.FourFour => MusicalTimeSpan.Sixteenth,
OrnamentationType.DelayedDoublePassingTone when meter == Meter.ThreeFour => MusicalTimeSpan.Eighth,
OrnamentationType.DelayedDoublePassingTone when meter == Meter.FiveEight => MusicalTimeSpan.Eighth,

OrnamentationType.DecorateInterval when meter == Meter.FourFour => MusicalTimeSpan.Eighth,
OrnamentationType.DecorateInterval when meter == Meter.ThreeFour => MusicalTimeSpan.Eighth,
OrnamentationType.DecorateInterval when meter == Meter.FiveEight => MusicalTimeSpan.Eighth,

OrnamentationType.DoubleRun when meter == Meter.FourFour => MusicalTimeSpan.Sixteenth,
OrnamentationType.DoubleRun when meter == Meter.ThreeFour => MusicalTimeSpan.Sixteenth,
OrnamentationType.DoubleRun when meter == Meter.FiveEight => MusicalTimeSpan.Sixteenth,

OrnamentationType.Pedal when meter == Meter.FourFour => MusicalTimeSpan.Eighth,
OrnamentationType.Pedal when meter == Meter.ThreeFour => MusicalTimeSpan.Eighth,
OrnamentationType.Pedal when meter == Meter.FiveEight => MusicalTimeSpan.Eighth,

OrnamentationType.Mordent when meter == Meter.FourFour && ornamentationStep == 1 => MusicalTimeSpan.Sixteenth,
OrnamentationType.Mordent when meter == Meter.ThreeFour && ornamentationStep == 1 => MusicalTimeSpan.Sixteenth,
OrnamentationType.Mordent when meter == Meter.FiveEight && ornamentationStep == 1 => MusicalTimeSpan.Sixteenth,

OrnamentationType.Mordent when meter == Meter.FourFour && ornamentationStep == 2 => MusicalTimeSpan.Quarter.Dotted(1),
OrnamentationType.Mordent when meter == Meter.ThreeFour && ornamentationStep == 2 => MusicalTimeSpan.Half + MusicalTimeSpan.Eighth,
OrnamentationType.Mordent when meter == Meter.FiveEight && ornamentationStep == 2 => MusicalTimeSpan.Half,

OrnamentationType.RepeatedNote when meter == Meter.FourFour => MusicalTimeSpan.Quarter,
OrnamentationType.RepeatedNote when meter == Meter.ThreeFour => MusicalTimeSpan.Quarter,
OrnamentationType.RepeatedNote when meter == Meter.FiveEight => MusicalTimeSpan.Quarter.Dotted(1),

OrnamentationType.DelayedRepeatedNote when meter == Meter.FourFour => MusicalTimeSpan.Eighth,
OrnamentationType.DelayedRepeatedNote when meter == Meter.ThreeFour => MusicalTimeSpan.Eighth,
OrnamentationType.DelayedRepeatedNote when meter == Meter.FiveEight => MusicalTimeSpan.Eighth,

OrnamentationType.NeighborTone when meter == Meter.FourFour => MusicalTimeSpan.Quarter,
OrnamentationType.NeighborTone when meter == Meter.ThreeFour => MusicalTimeSpan.Quarter,
OrnamentationType.NeighborTone when meter == Meter.FiveEight => MusicalTimeSpan.Quarter.Dotted(1),

OrnamentationType.DelayedNeighborTone when meter == Meter.FourFour => MusicalTimeSpan.Eighth,
OrnamentationType.DelayedNeighborTone when meter == Meter.ThreeFour => MusicalTimeSpan.Eighth,
OrnamentationType.DelayedNeighborTone when meter == Meter.FiveEight => MusicalTimeSpan.Eighth,

OrnamentationType.MidSustain => Zero,
OrnamentationType.Rest => Zero,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@ internal sealed class BaroquenMelodyComposerConfiguratorTests

[Test]
[TestCaseSource(nameof(TestCases))]
public void Configure_returns_configured_BaroquenMelodyComposer_which_can_compose_a_BaroquenMelody(CompositionConfiguration compositionConfiguration)
public void Configure_returns_configured_MidiFileComposer_which_can_compose_a_MidiFileComposition(CompositionConfiguration compositionConfiguration)
{
// arrange
var baroquenMelodyComposer = _baroquenMelodyComposerConfigurator.Configure(compositionConfiguration);
var midiFileComposer = _baroquenMelodyComposerConfigurator.Configure(compositionConfiguration);

// act
var baroquenMelody = baroquenMelodyComposer.Compose(CancellationToken.None);
var midiFileComposition = midiFileComposer.Compose(CancellationToken.None);

// assert
baroquenMelody.Should().NotBeNull();
baroquenMelody.MidiFile.Should().NotBeNull();
midiFileComposition.Should().NotBeNull();
midiFileComposition.MidiFile.Should().NotBeNull();
}

private static IEnumerable<TestCaseData> TestCases
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ public void ConfigurableMeters_returns_expected_values()
var expectedConfigurableMeters = new[]
{
Meter.FourFour,
Meter.ThreeFour
Meter.ThreeFour,
Meter.FiveEight
};

// act
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ internal sealed class MeterExtensionsTests
{
[Test]
[TestCase(Meter.FourFour, 4)]
[TestCase(Meter.ThreeFour, 3)]
[TestCase(Meter.ThreeFour, 4)]
[TestCase(Meter.FiveEight, 4)]
public void BeatsPerMeasure_returns_expected_value(Meter meter, int expectedBeatsPerMeasure) =>
meter.BeatsPerMeasure().Should().Be(expectedBeatsPerMeasure);

Expand Down Expand Up @@ -39,6 +40,7 @@ public void DefaultMusicalTimeSpan_throws_on_unsupported_meter()
[Test]
[TestCase(Meter.FourFour, "4/4")]
[TestCase(Meter.ThreeFour, "3/4")]
[TestCase(Meter.FiveEight, "5/8")]
public void AsString_generates_expected_string(Meter meter, string expected)
{
meter.AsString().Should().Be(expected);
Expand All @@ -58,12 +60,14 @@ private static IEnumerable<TestCaseData> DefaultMusicalTimeSpanTestCases
{
yield return new TestCaseData(Meter.FourFour, MusicalTimeSpan.Half);
yield return new TestCaseData(Meter.ThreeFour, MusicalTimeSpan.Half.Dotted(1));
yield return new TestCaseData(Meter.FiveEight, MusicalTimeSpan.Half + MusicalTimeSpan.Eighth);
}
}

[Test]
[TestCase(Meter.FourFour, 4, 4)]
[TestCase(Meter.ThreeFour, 3, 4)]
[TestCase(Meter.FiveEight, 5, 8)]
public void ToTimeSignature_returns_expected_value(Meter meter, int expectedNumerator, int expectedDenominator)
{
// arrange
Expand Down

0 comments on commit 59b7395

Please sign in to comment.