Esempio n. 1
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);
        }