From 31c21f58a9a493a986f4f51837d772cd665c3063 Mon Sep 17 00:00:00 2001 From: Maxim Dobroselsky Date: Sat, 17 Aug 2024 17:18:40 +0300 Subject: [PATCH] Added ControlChange method to PatternBuilder --- .../PatternBuilderTests.ControlChange.cs | 64 +++++++++++++ .../PatternBuilderTests.Misc.cs | 82 +--------------- .../PatternBuilderTests.ProgramChange.cs | 93 +++++++++++++++++++ .../Actions/AddControlChangeEventAction.cs | 47 ++++++++++ .../Composing/PatternBuilder.ControlChange.cs | 16 ++++ 5 files changed, 223 insertions(+), 79 deletions(-) create mode 100644 DryWetMidi.Tests/Composing/PatternBuilder/PatternBuilderTests.ControlChange.cs create mode 100644 DryWetMidi.Tests/Composing/PatternBuilder/PatternBuilderTests.ProgramChange.cs create mode 100644 DryWetMidi/Composing/Actions/AddControlChangeEventAction.cs create mode 100644 DryWetMidi/Composing/PatternBuilder.ControlChange.cs diff --git a/DryWetMidi.Tests/Composing/PatternBuilder/PatternBuilderTests.ControlChange.cs b/DryWetMidi.Tests/Composing/PatternBuilder/PatternBuilderTests.ControlChange.cs new file mode 100644 index 000000000..56808b88f --- /dev/null +++ b/DryWetMidi.Tests/Composing/PatternBuilder/PatternBuilderTests.ControlChange.cs @@ -0,0 +1,64 @@ +using Melanchall.DryWetMidi.Common; +using Melanchall.DryWetMidi.Composing; +using Melanchall.DryWetMidi.Core; +using Melanchall.DryWetMidi.Interaction; +using Melanchall.DryWetMidi.MusicTheory; +using NUnit.Framework; + +namespace Melanchall.DryWetMidi.Tests.Composing +{ + [TestFixture] + public sealed partial class PatternBuilderTests + { + #region Test methods + + [Test] + public void ControlChange_1() + { + var controlNumber = (SevenBitNumber)10; + var controlValue = (SevenBitNumber)70; + var eventTime = MusicalTimeSpan.Quarter; + + var pattern = new PatternBuilder() + + .Note(NoteName.A, eventTime) + .ControlChange(controlNumber, controlValue) + + .Build(); + + PatternTestUtilities.TestTimedEvents(pattern, new[] + { + new TimedEventInfo(new ControlChangeEvent(controlNumber, controlValue) { Channel = PatternTestUtilities.Channel }, eventTime) + }); + } + + [Test] + public void ControlChange_2() + { + var controlNumber1 = (SevenBitNumber)10; + var controlValue1 = (SevenBitNumber)70; + var noteLength1 = MusicalTimeSpan.Quarter; + + var controlNumber2 = (SevenBitNumber)15; + var controlValue2 = (SevenBitNumber)55; + var noteLength2 = MusicalTimeSpan.Sixteenth; + + var pattern = new PatternBuilder() + + .Note(NoteName.A, noteLength1) + .ControlChange(controlNumber1, controlValue1) + .Note(NoteName.CSharp, noteLength2) + .ControlChange(controlNumber2, controlValue2) + + .Build(); + + PatternTestUtilities.TestTimedEvents(pattern, new[] + { + new TimedEventInfo(new ControlChangeEvent(controlNumber1, controlValue1) { Channel = PatternTestUtilities.Channel }, noteLength1), + new TimedEventInfo(new ControlChangeEvent(controlNumber2, controlValue2) { Channel = PatternTestUtilities.Channel }, noteLength1 + noteLength2), + }); + } + + #endregion + } +} diff --git a/DryWetMidi.Tests/Composing/PatternBuilder/PatternBuilderTests.Misc.cs b/DryWetMidi.Tests/Composing/PatternBuilder/PatternBuilderTests.Misc.cs index bffe048da..784fc3b7b 100644 --- a/DryWetMidi.Tests/Composing/PatternBuilder/PatternBuilderTests.Misc.cs +++ b/DryWetMidi.Tests/Composing/PatternBuilder/PatternBuilderTests.Misc.cs @@ -16,85 +16,6 @@ public sealed partial class PatternBuilderTests { #region Test methods - #region ProgramChange - - [Test] - public void ProgramChange_Number() - { - var programNumber = (SevenBitNumber)10; - var eventTime = MusicalTimeSpan.Quarter; - - var pattern = new PatternBuilder() - - .Note(NoteName.A, eventTime) - .ProgramChange(programNumber) - - .Build(); - - PatternTestUtilities.TestTimedEvents(pattern, new[] - { - new TimedEventInfo(new ProgramChangeEvent(programNumber) { Channel = PatternTestUtilities.Channel }, eventTime) - }); - } - - [Test] - public void ProgramChange_GeneralMidiProgram() - { - var program1 = GeneralMidiProgram.Applause; - var program2 = GeneralMidiProgram.AltoSax; - var eventTime = MusicalTimeSpan.Quarter; - - var noteNumber = (SevenBitNumber)100; - var note = DryWetMidi.MusicTheory.Note.Get(noteNumber); - - var pattern = new PatternBuilder() - - .ProgramChange(program1) - .Note(note, eventTime) - .ProgramChange(program2) - - .Build(); - - PatternTestUtilities.TestTimedEventsWithExactOrder(pattern, new[] - { - new TimedEventInfo(new ProgramChangeEvent(program1.AsSevenBitNumber()) { Channel = PatternTestUtilities.Channel }, new MidiTimeSpan()), - new TimedEventInfo(new NoteOnEvent(noteNumber, DryWetMidi.Interaction.Note.DefaultVelocity) { Channel = PatternTestUtilities.Channel }, new MidiTimeSpan()), - new TimedEventInfo(new ProgramChangeEvent(program2.AsSevenBitNumber()) { Channel = PatternTestUtilities.Channel }, eventTime), - new TimedEventInfo(new NoteOffEvent(noteNumber, SevenBitNumber.MinValue) { Channel = PatternTestUtilities.Channel }, eventTime) - }); - } - - [Test] - public void ProgramChange_GeneralMidi2Program() - { - var eventsTime = MusicalTimeSpan.Quarter; - - var bankMsbControlNumber = ControlName.BankSelect.AsSevenBitNumber(); - var bankMsb = (SevenBitNumber)0x79; - - var bankLsbControlNumber = ControlName.LsbForBankSelect.AsSevenBitNumber(); - var bankLsb = (SevenBitNumber)0x03; - - var generalMidiProgram = GeneralMidiProgram.BirdTweet; - var generalMidi2Program = GeneralMidi2Program.BirdTweet2; - - var pattern = new PatternBuilder() - - .Note(NoteName.A, eventsTime) - .ProgramChange(generalMidi2Program) - - .Build(); - - PatternTestUtilities.TestTimedEvents(pattern, new[] - { - new TimedEventInfo(new ControlChangeEvent(bankMsbControlNumber, bankMsb) { Channel = PatternTestUtilities.Channel }, eventsTime), - new TimedEventInfo(new ControlChangeEvent(bankLsbControlNumber, bankLsb) { Channel = PatternTestUtilities.Channel }, eventsTime), - new TimedEventInfo(new ProgramChangeEvent(generalMidiProgram.AsSevenBitNumber()) { Channel = PatternTestUtilities.Channel }, eventsTime), - }); - } - - #endregion - #region ReplayPattern [Test] @@ -213,6 +134,7 @@ public void ClonePatternAction_TypeIsCorrect() { PatternAction patternAction = null; + // TODO: randomize values if (type == typeof(AddChordAction)) patternAction = new AddChordAction(new ChordDescriptor(Enumerable.Empty(), SevenBitNumber.MinValue, MusicalTimeSpan.Eighth)); else if (type == typeof(AddNoteAction)) @@ -221,6 +143,8 @@ public void ClonePatternAction_TypeIsCorrect() patternAction = new AddPatternAction(new PatternBuilder().Build()); else if (type == typeof(AddTextEventAction<>)) patternAction = new AddTextEventAction(string.Empty); + else if (type == typeof(AddControlChangeEventAction)) + patternAction = new AddControlChangeEventAction((SevenBitNumber)70, (SevenBitNumber)30); else if (type == typeof(MoveToAnchorAction)) patternAction = new MoveToAnchorAction(AnchorPosition.First); else if (type == typeof(SetGeneralMidi2ProgramAction)) diff --git a/DryWetMidi.Tests/Composing/PatternBuilder/PatternBuilderTests.ProgramChange.cs b/DryWetMidi.Tests/Composing/PatternBuilder/PatternBuilderTests.ProgramChange.cs new file mode 100644 index 000000000..fbeccb511 --- /dev/null +++ b/DryWetMidi.Tests/Composing/PatternBuilder/PatternBuilderTests.ProgramChange.cs @@ -0,0 +1,93 @@ +using Melanchall.DryWetMidi.Common; +using Melanchall.DryWetMidi.Composing; +using Melanchall.DryWetMidi.Core; +using Melanchall.DryWetMidi.Interaction; +using Melanchall.DryWetMidi.MusicTheory; +using Melanchall.DryWetMidi.Standards; +using NUnit.Framework; + +namespace Melanchall.DryWetMidi.Tests.Composing +{ + [TestFixture] + public sealed partial class PatternBuilderTests + { + #region Test methods + + [Test] + public void ProgramChange_Number() + { + var programNumber = (SevenBitNumber)10; + var eventTime = MusicalTimeSpan.Quarter; + + var pattern = new PatternBuilder() + + .Note(NoteName.A, eventTime) + .ProgramChange(programNumber) + + .Build(); + + PatternTestUtilities.TestTimedEvents(pattern, new[] + { + new TimedEventInfo(new ProgramChangeEvent(programNumber) { Channel = PatternTestUtilities.Channel }, eventTime) + }); + } + + [Test] + public void ProgramChange_GeneralMidiProgram() + { + var program1 = GeneralMidiProgram.Applause; + var program2 = GeneralMidiProgram.AltoSax; + var eventTime = MusicalTimeSpan.Quarter; + + var noteNumber = (SevenBitNumber)100; + var note = DryWetMidi.MusicTheory.Note.Get(noteNumber); + + var pattern = new PatternBuilder() + + .ProgramChange(program1) + .Note(note, eventTime) + .ProgramChange(program2) + + .Build(); + + PatternTestUtilities.TestTimedEventsWithExactOrder(pattern, new[] + { + new TimedEventInfo(new ProgramChangeEvent(program1.AsSevenBitNumber()) { Channel = PatternTestUtilities.Channel }, new MidiTimeSpan()), + new TimedEventInfo(new NoteOnEvent(noteNumber, DryWetMidi.Interaction.Note.DefaultVelocity) { Channel = PatternTestUtilities.Channel }, new MidiTimeSpan()), + new TimedEventInfo(new ProgramChangeEvent(program2.AsSevenBitNumber()) { Channel = PatternTestUtilities.Channel }, eventTime), + new TimedEventInfo(new NoteOffEvent(noteNumber, SevenBitNumber.MinValue) { Channel = PatternTestUtilities.Channel }, eventTime) + }); + } + + [Test] + public void ProgramChange_GeneralMidi2Program() + { + var eventsTime = MusicalTimeSpan.Quarter; + + var bankMsbControlNumber = ControlName.BankSelect.AsSevenBitNumber(); + var bankMsb = (SevenBitNumber)0x79; + + var bankLsbControlNumber = ControlName.LsbForBankSelect.AsSevenBitNumber(); + var bankLsb = (SevenBitNumber)0x03; + + var generalMidiProgram = GeneralMidiProgram.BirdTweet; + var generalMidi2Program = GeneralMidi2Program.BirdTweet2; + + var pattern = new PatternBuilder() + + .Note(NoteName.A, eventsTime) + .ProgramChange(generalMidi2Program) + + .Build(); + + PatternTestUtilities.TestTimedEvents(pattern, new[] + { + new TimedEventInfo(new ControlChangeEvent(bankMsbControlNumber, bankMsb) { Channel = PatternTestUtilities.Channel }, eventsTime), + new TimedEventInfo(new ControlChangeEvent(bankLsbControlNumber, bankLsb) { Channel = PatternTestUtilities.Channel }, eventsTime), + new TimedEventInfo(new ProgramChangeEvent(generalMidiProgram.AsSevenBitNumber()) { Channel = PatternTestUtilities.Channel }, eventsTime), + }); + } + + #endregion + } +} diff --git a/DryWetMidi/Composing/Actions/AddControlChangeEventAction.cs b/DryWetMidi/Composing/Actions/AddControlChangeEventAction.cs new file mode 100644 index 000000000..0bfacd1c7 --- /dev/null +++ b/DryWetMidi/Composing/Actions/AddControlChangeEventAction.cs @@ -0,0 +1,47 @@ +using Melanchall.DryWetMidi.Common; +using Melanchall.DryWetMidi.Core; +using Melanchall.DryWetMidi.Interaction; + +namespace Melanchall.DryWetMidi.Composing +{ + internal sealed class AddControlChangeEventAction : PatternAction + { + #region Constructor + + public AddControlChangeEventAction(SevenBitNumber controlNumber, SevenBitNumber controlValue) + { + ControlNumber = controlNumber; + ControlValue = controlValue; + } + + #endregion + + #region Properties + + public SevenBitNumber ControlNumber { get; } + + public SevenBitNumber ControlValue { get; } + + #endregion + + #region Overrides + + public override PatternActionResult Invoke(long time, PatternContext context) + { + if (State != PatternActionState.Enabled) + return PatternActionResult.DoNothing; + + var controlChangeEvent = new ControlChangeEvent(ControlNumber, ControlValue) { Channel = context.Channel }; + var timedEvent = new TimedEvent(controlChangeEvent, time); + + return new PatternActionResult(time, new[] { timedEvent }); + } + + public override PatternAction Clone() + { + return new AddControlChangeEventAction(ControlNumber, ControlValue); + } + + #endregion + } +} diff --git a/DryWetMidi/Composing/PatternBuilder.ControlChange.cs b/DryWetMidi/Composing/PatternBuilder.ControlChange.cs new file mode 100644 index 000000000..19b255c6e --- /dev/null +++ b/DryWetMidi/Composing/PatternBuilder.ControlChange.cs @@ -0,0 +1,16 @@ +using Melanchall.DryWetMidi.Common; + +namespace Melanchall.DryWetMidi.Composing +{ + public sealed partial class PatternBuilder + { + #region Methods + + public PatternBuilder ControlChange(SevenBitNumber controlNumber, SevenBitNumber controlValue) + { + return AddAction(new AddControlChangeEventAction(controlNumber, controlValue)); + } + + #endregion + } +}