/// <summary> /// Creates a midi playback file by copying an existing midi file or stream /// and removing it's melody track, if such exists (according to the specified /// track number in the <paramref name="MelodyTrackNumber"/> parameter. /// <para> If the given midi is already a pure playback with no melody tracks, /// then a new copy of this midi would be returned. /// </para> /// </summary> /// <param name="midiStream"> The midi input that the playback should be created from. </param> /// <param name="MelodyTrackNumber">Number of the melody track in the given midi, if such a track exists.</param> /// <returns></returns> public static IMidiFile CreateMidiPlayback(Stream midiStream, MelodyTrackIndex?melodyTrackNumber) { // create a new midi file instance IMidiFile midifile = MidiFactory.CreateMidiFile(midiStream); // remove melody track is such exists on this midi file if (melodyTrackNumber.HasValue && melodyTrackNumber != MelodyTrackIndex.NoMelodyTrackInFile) { midifile.ExtractMelodyTrack((byte)melodyTrackNumber); } // return the resulted playback midi file return(midifile); }
/// <summary> /// Composes a solo-melody over this composition's midi playback file and chord progression, /// using the additional preferences and constraints parameters. /// </summary> /// <returns> A new midi file containing the composed solo-melody. </returns> /// <param name="strategy">Requested composition strategy algorithm for composing the melody. </param> /// <param name="overallNoteDurationFeel">Requested generic density overall feeling of the outcome melody.</param> /// <param name="musicalInstrument">Requested virtual music instrumen tha would play the output melody.</param> /// <param name="pitchRangeSource">Determins whether to set the pitch range from the original melody in the /// midi file, or from the custom values mentiones in <paramref name="minPitch"/> /// and <paramref name="maxPitch"/>.</param> /// <param name="minPitch">Requested lowest available pitch that would can be used for the composition. /// This is relevant only if <paramref name="pitchRangeSource"/> was set to /// <see cref="PitchRangeSource.Custom"/>.</param> /// <param name="maxPitch">Requested highest available pitch that would can be used for the composition. /// This is relevant only if <paramref name="pitchRangeSource"/> was set to /// <see cref="PitchRangeSource.Custom"/>.</param> /// <param name="useExistingMelodyAsSeed">If set, then the original melody might be used in initialization phase /// of the compositon strategy algorithm. This is relevant only if the /// midi file contains an existing melody to begin with. /// Moreover, this feature is implementation dependent: some strategies /// might make high use of original melody as the main initialization seed /// and some may not use it at all.</param> /// <param name="customParams"> Currently unused. This a placeholder for additional parameters that could be used /// in the future to extened existing implementations and developing new ones without /// breaking the existing interface. </param> /// <returns></returns> public IMidiFile[] Compose( CompositionStrategy strategy = CompositionStrategy.GeneticAlgorithmStrategy, OverallNoteDurationFeel overallNoteDurationFeel = OverallNoteDurationFeel.Medium, MusicalInstrument musicalInstrument = MusicalInstrument.AcousticGrandPiano, PitchRangeSource pitchRangeSource = PitchRangeSource.Custom, NotePitch minPitch = DefaultMinPitch, NotePitch maxPitch = DefaultMaxPitch, bool useExistingMelodyAsSeed = true, params object[] customParams) { // set compositor according to composition strategy _composer = ComposerFactory.CreateComposer(strategy); // make a copy of the input midi file for the output file MidiOutputFile = MidiFactory.CreateMidiFile(_midiInputFilePath); /* if the midi file already contains a melody track, * extract it out of the intended midi output file * and if requested, save it in a separate bar sequence for further usage * as a melody initialization seed for the composition if needed. */ if (_melodyTrackIndex.HasValue && _melodyTrackIndex.Value != MelodyTrackIndex.NoMelodyTrackInFile) { /* if the existing melody should serve as a seed, * initialize a bar-sequence place-holder for it, * based on the chord progression structure */ _melodySeed = useExistingMelodyAsSeed ? CompositionContext.CloneChordProgressionBars(ChordProgression) : null; /* extract/remove the existing melody from midi file and * save it in the place holder if it was initialized */ MidiOutputFile.ExtractMelodyTrack((byte)_melodyTrackIndex, _melodySeed); } // initialize pitch range from midi file if requested if (pitchRangeSource == PitchRangeSource.MidiFile && _melodyTrackIndex.HasValue) { NotePitch?lowestPitch, highestPitch; MidiInputFile.GetPitchRangeForTrack((int)_melodyTrackIndex, out lowestPitch, out highestPitch); if (lowestPitch.HasValue && highestPitch.HasValue) { minPitch = (NotePitch)lowestPitch; maxPitch = (NotePitch)highestPitch; } } // validate pitch range is at least one octave long (12 semi-tones) if (!IsPitchRangeValid((int)minPitch, (int)maxPitch, out string errorMessage)) { throw new ArgumentException(errorMessage); } // compose a new melody IList <IBar>[] composedMelodies = _composer.Compose( chordProgression: ChordProgression, melodyInitializationSeed: _melodySeed, overallNoteDurationFeel: overallNoteDurationFeel, minPitch: minPitch, maxPitch: maxPitch, customParams: customParams) .ToArray(); // Embed each new generated melody into a new separate midi file IMidiFile[] midiOutputs = new IMidiFile[composedMelodies.Length]; for (int i = 0; i < composedMelodies.Length; i++) { midiOutputs[i] = MidiFactory.CreateMidiFile(_midiInputFilePath); midiOutputs[i].ExtractMelodyTrack((byte)_melodyTrackIndex); midiOutputs[i].EmbedMelody(composedMelodies[i], musicalInstrument); midiOutputs[i].FadeOut(); } // save first output in dedicated placeholder MidiOutputFile = midiOutputs[0]; // return the first composed melody from the output return(midiOutputs); }
/// <summary> /// Reads the contents of a standard midi file and returns a high-level /// interface handler for the midi's content and metadata. /// </summary> /// <param name="filePath"> Path of the midi file.</param> /// <returns> IMidiFile which is handler for the midi's content and metadata. </returns> public static IMidiFile ReadMidiFile(string filePath) => MidiFactory.CreateMidiFile(filePath);
/// <summary> <inheritdoc cref="ReadMidiFile(string)"/></summary> /// <param name="filePath"> Stream containing the midi's file content.</param> /// <returns> IMidiFile which is handler for the midi's content and metadata. </returns> public static IMidiFile ReadMidiFile(Stream stream) => MidiFactory.CreateMidiFile(stream);
/// <summary> /// Construct a composition from a chord progression, midi file handler, /// and an indicator determinning if this midi file is a pure playback or whether /// it contains a melody track that should be replaced when composing a new melody. /// </summary> /// <remarks> Number of bars in midi file and chord progression must match exactly, and so do their durations. </remarks> /// <param name="chordProgressionFilePath"></param> /// <param name="midiFilePath"></param> /// <param name="melodyTrackIndex"> One-based Index of the existing melody track in the midi file, if one exists. /// <para> /// When composing, the track with this index would be replaced by the new composed /// melody track. If the current midi file is a pure playback that contains no existing /// melody track, then this property should be set to null. </para></param> /// <exception cref="InvalidDataException"> Thrown when there is a mismatch /// between the chord progression and the midi file, either in the total number /// of bars, or the duration of individual bars. </exception> /// <exception cref="FormatException"> Thrown when the chord progression file isn't /// constructed in the expected format syntax <see cref="ReadChordsFromFile(string)"/>. </exception> /// /// <exception cref="IndexOutOfRangeException"> Throw when <paramref name="melodyTrackIndex"/> /// has a value which equal or greater than the number of track in the given midi file. </exception> public CompositionContext(string chordProgressionFilePath, string midiFilePath, MelodyTrackIndex?melodyTrackIndex = null) : this(chordProgression : ReadChordsFromFile(chordProgressionFilePath), midiFile : MidiFactory.CreateMidiFile(midiFilePath), melodyTrackIndex) { }