Ejemplo n.º 1
0
        /// <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);
        }
Ejemplo n.º 2
0
        /// <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);
        }
Ejemplo n.º 3
0
 /// <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);
Ejemplo n.º 4
0
 /// <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);
Ejemplo n.º 5
0
 /// <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)
 {
 }