示例#1
0
        public static ProcessResultArray <Clip> Apply(SetPitchOptions options, params Clip[] clips)
        {
            var resultClips = ClipUtilities.CreateEmptyPlaceholderClips(clips);

            int[] pitches;
            if (options.PitchValues.Length > 0)
            {
                pitches = options.PitchValues;
            }
            else
            {
                pitches = options.By.Notes.Select(x => x.Pitch).ToArray();
            }
            if (pitches.Length == 0)
            {
                return(new ProcessResultArray <Clip>(clips, "SetPitch did nothing, since neither pitches or -by clip was specified."));
            }

            for (var i = 0; i < clips.Length; i++)
            {
                var clip       = clips[i];
                var resultClip = resultClips[i];
                var pitchIx    = 0;
                foreach (var note in clip.Notes)
                {
                    var repitchedNote = new NoteEvent(note)
                    {
                        Pitch = pitches[pitchIx++ % pitches.Length]
                    };
                    ClipUtilities.AddNoteCutting(resultClip, repitchedNote);
                }
            }

            return(new ProcessResultArray <Clip>(resultClips));
        }
示例#2
0
        public static Clip CropClip(Clip clip, decimal start, decimal duration)
        {
            var processedClip = new Clip(duration, clip.IsLooping);

            processedClip.Notes.AddRange(ClipUtilities.GetSplitNotesInRangeAtPosition(start, start + duration, clip.Notes, 0));
            return(processedClip);
        }
示例#3
0
    public void TestMonophonize()
    {
        var clip1 = new Clip(1, true)
        {
            Notes = new SortedList <NoteEvent>()
            {
                new NoteEvent(60, 0, 1, 100),
                new NoteEvent(62, 0, 0.2m, 100),
                new NoteEvent(62, 0.3m, 0.2m, 100),
                new NoteEvent(62, 0.5m, 0.2m, 100),
                new NoteEvent(62, 0.8m, 0.2m, 100),
                new NoteEvent(60, 1, 1, 100),
            }
        };

        Assert.AreEqual(6, clip1.Notes.Count);
        ClipUtilities.Monophonize(clip1);
        Assert.AreEqual(2, clip1.Notes.Count);

        clip1 = new Clip(1, true)
        {
            Notes = new SortedList <NoteEvent>()
            {
                new NoteEvent(60, 0, .2m, 100),
                new NoteEvent(62, .1m, .2m, 100),
                new NoteEvent(62, .2m, .2m, 100)
            }
        };
        Assert.AreEqual(3, clip1.Notes.Count);
        ClipUtilities.Monophonize(clip1);
        Assert.AreEqual(2, clip1.Notes.Count);
    }
示例#4
0
        public static ProcessResultArray <Clip> Apply(ScaleOptions options, params Clip[] clips)
        {
            if (options.By != null)
            {
                clips = clips.Prepend(options.By).ToArray();
            }
            ClipUtilities.NormalizeClipLengths(clips);
            if (clips.Length < 2)
            {
                return(new ProcessResultArray <Clip>(clips));
            }
            var masterClip     = clips[0];
            var slaveClips     = clips.Skip(1).ToArray();
            var processedClips = slaveClips.Select(c => new Clip(c.Length, c.IsLooping)).ToArray();

            for (var i = 0; i < slaveClips.Length; i++)
            {
                var slaveClip = slaveClips[i];
                foreach (var note in slaveClip.Notes)
                {
                    var constrainedNote = new NoteEvent(note);
                    constrainedNote.Pitch = options.Strict ?
                                            ClipUtilities.FindNearestNotePitchInSet(note, masterClip.Notes) :
                                            ClipUtilities.FindNearestNotePitchInSetMusical(note, masterClip.Notes);
                    processedClips[i].Notes.Add(constrainedNote);
                }
            }
            return(new ProcessResultArray <Clip>(processedClips));
        }
示例#5
0
    public static Clip AddEchoes(Clip clip, decimal[] lengths, int[] echoes)
    {
        var lengthIx = 0;
        var echoIx   = 0;
        var newNotes = new List <NoteEvent>();

        foreach (var noteEvent in clip.Notes)
        {
            var delayTime       = lengths[lengthIx++ % lengths.Length];
            var echoCount       = Math.Max(echoes[echoIx++ % echoes.Length], 2);
            var velocityFalloff = (int)Math.Round((noteEvent.Velocity - 10) / (echoCount - 1));
            if (noteEvent.Duration > delayTime)
            {
                noteEvent.Duration = delayTime;
            }
            for (var i = 0; i < echoCount; i++)
            {
                var echoedNote = noteEvent with {
                };
                echoedNote.Start    += delayTime * i;
                echoedNote.Velocity -= velocityFalloff * i;
                newNotes.Add(echoedNote);
            }
        }
        foreach (var newNote in newNotes)
        {
            ClipUtilities.AddNoteCutting(clip, newNote);
        }
        // todo: handle wrapping echoes outside the length of the clip
        return(clip);
    }
示例#6
0
 //[TestMethod]
 public void MyTestMethod()
 {
     for (var i = 10; i < 36; i += 5)
     {
         Console.WriteLine($"pitch {i} nearest: {ClipUtilities.FindNearestNotePitchInSet(i, new int[] { 0,12,24,32 })}");
     }
 }
示例#7
0
文件: Scan.cs 项目: twobob/mutateful
        public static ProcessResultArray <Clip> Apply(Command command, params Clip[] clips)
        {
            (var success, var msg) = OptionParser.TryParseOptions(command, out ScanOptions options);
            if (!success)
            {
                return(new ProcessResultArray <Clip>(msg));
            }
            var processedClips = new Clip[clips.Length];

            for (var c = 0; c < clips.Length; c++)
            {
                var     clip          = clips[c];
                var     processedClip = new Clip(options.Window * options.Count, clip.IsLooping);
                decimal delta         = clip.Length / options.Count,
                        curPos        = 0;

                for (int i = 0; i < options.Count; i++)
                {
                    processedClip.Notes.AddRange(ClipUtilities.GetSplitNotesInRangeAtPosition(curPos, curPos + options.Window, clip.Notes, options.Window * i));
                    curPos += delta;
                }
                processedClips[c] = processedClip;
            }

            return(new ProcessResultArray <Clip>(processedClips));
        }
示例#8
0
 public static ProcessResult <Clip[]> Apply(LoopOptions options, params Clip[] clips)
 {
     foreach (var clip in clips)
     {
         ClipUtilities.EnlargeClipByLooping(clip, options.Length * clip.Length);
     }
     return(new ProcessResult <Clip[]>(clips));
 }
示例#9
0
        // TODO: Add option to cut overlapping events, so that more of the original clip is preserved

        public static ProcessResultArray <Clip> Apply(params Clip[] clips)
        {
            var processedClips = new List <Clip>();

            foreach (var clip in clips)
            {
                processedClips.Add(ClipUtilities.Monophonize(clip));
            }
            return(new ProcessResultArray <Clip>(processedClips.ToArray()));
        }
示例#10
0
    public static ProcessResult <Clip[]> Apply(SliceOptions options, params Clip[] clips)
    {
        var processedClips = new List <Clip>();

        foreach (var clip in clips)
        {
            processedClips.Add(ClipUtilities.SplitNotesAtEvery(clip, options.Lengths));
        }
        return(new ProcessResult <Clip[]>(processedClips.ToArray()));
    }
示例#11
0
        // Add option to dynamically set # of events that should be rescaled to another note, probably via velocity.
        public static ProcessResultArray <Clip> Apply(ArpeggiateOptions options, params Clip[] clips)
        {
            Clip arpSequence = ClipUtilities.Monophonize(options.By ?? clips[0]);

            foreach (var clip in clips)
            {
                ClipUtilities.Monophonize(clip);
            }
            var processedClips = new List <Clip>(clips.Length);

            // If arp sequence doesn't start at zero and remove offset is specified, make it start at zero
            if (arpSequence.Notes[0].Start != 0 && options.RemoveOffset)
            {
                foreach (var arpNote in arpSequence.Notes)
                {
                    arpNote.Start -= arpSequence.Notes[0].Start;
                }
            }

            var count        = Math.Min(arpSequence.Notes.Count, options.Rescale);
            var arpNotes     = arpSequence.Notes.Take(count);
            var actualLength = arpNotes.Last().Start + arpNotes.Last().Duration;

            // Rescale arp events to the range 0-1
            foreach (var arpNote in arpNotes)
            {
                arpNote.Start    = arpNote.Start / actualLength;
                arpNote.Duration = arpNote.Duration / actualLength;
            }

            foreach (var clip in clips)
            {
                var resultClip = new Clip(clip.Length, clip.IsLooping);
                for (var i = 0; i < clip.Notes.Count; i++)
                {
                    var note           = clip.Notes[i];
                    var processedNotes = new List <NoteEvent>(count);

                    int ix = 0;
                    foreach (var currentArpNote in arpNotes)
                    {
                        NoteEvent processedNote = new NoteEvent(currentArpNote);
                        processedNote.Start     = note.Start + (processedNote.Start * note.Duration);
                        processedNote.Duration *= note.Duration;
                        processedNote.Pitch     = note.Pitch + arpSequence.RelativePitch(ix);
                        processedNotes.Add(processedNote);
                        ix++;
                    }
                    resultClip.Notes.AddRange(processedNotes);
                }
                processedClips.Add(resultClip);
            }
            return(new ProcessResultArray <Clip>(processedClips.ToArray()));
        }
示例#12
0
    // # desc: Concatenates two or more clips together.
    public static ProcessResult <Clip[]> Apply(params Clip[] clips)
    {
        Clip    resultClip = new Clip(clips.Select(c => c.Length).Sum(), true);
        decimal pos        = 0;

        foreach (var clip in clips)
        {
            resultClip.Notes.AddRange(ClipUtilities.GetNotesInRangeAtPosition(0, clip.Length, clip.Notes, pos));
            pos += clip.Length;
        }
        return(new ProcessResult <Clip[]>(new[] { resultClip }));
    }
示例#13
0
    // TODO: Add option to cut overlapping events, so that more of the original clip is preserved

    public static ProcessResult <Clip[]> Apply(params Clip[] clips)
    {
        var resultClips = ClipUtilities.CreateEmptyPlaceholderClips(clips);

        for (var i = 0; i < clips.Length; i++)
        {
            var clip       = clips[i];
            var resultClip = resultClips[i];
            foreach (var note in clip.Notes)
            {
                AddNoteCutting(resultClip, note with {
                });
示例#14
0
        public static ProcessResultArray <Clip> Apply(SetRhythmOptions options, params Clip[] clips)
        {
            if (options.By != null)
            {
                clips = clips.Prepend(options.By).ToArray();
            }

            if (clips.Length < 2)
            {
                return(new ProcessResultArray <Clip>(clips, $"SetRhythm: Skipped command because it needs 2 clips, and {clips.Length} were passed in."));
            }
            ClipUtilities.NormalizeClipLengths(clips);

            var resultClips  = new Clip[clips.Length - 1];
            var byClip       = clips[0];
            var byIndex      = 0;
            var resultClipIx = 0;

            for (var i = 1; i < clips.Length; i++)
            {
                var clip       = clips[i];
                var resultClip = new Clip(0, clip.IsLooping);

                foreach (var note in clip.Notes)
                {
                    var byNote = byClip.Notes[byIndex % byClip.Count];

                    // special case: add silence between start of clip and first note, but only the first time, since subsequent silences are handled by DurationUntilNextNote
                    if (resultClip.Length == 0 && byIndex == 0)
                    {
                        resultClip.Length = byNote.Start;
                    }

                    resultClip.Add(new NoteEvent(note.Pitch, resultClip.Length, byNote.Duration, note.Velocity));
                    resultClip.Length += byClip.DurationUntilNextNote(byIndex % byClip.Count);
                    byIndex++;
                }

                // stacked/overlapping notes will lead to incorrect final length of clip, so check if this is the case
                var latestNoteEnd = resultClip.Notes.Max(x => x.End);
                if (latestNoteEnd > resultClip.Length)
                {
                    resultClip.Length = latestNoteEnd;
                }

                resultClip.Length           = Utilities.RoundUpToNearestSixteenth(resultClip.Length); // quantize clip length to nearest 1/16, or Live won't accept it
                resultClips[resultClipIx++] = resultClip;
            }

            return(new ProcessResultArray <Clip>(resultClips));
        }
示例#15
0
    public void TestNormalizeClipLengths()
    {
        var clip1 = new Clip(1, true)
        {
            Notes = new SortedList <NoteEvent>()
            {
                new NoteEvent(60, 0, .25m, 100),
                new NoteEvent(60, .4m, .1m, 100),
            }
        };
        var clip2 = new Clip(4, true)
        {
            Notes = new SortedList <NoteEvent>()
            {
                new NoteEvent(62, 0, 1, 100),
                new NoteEvent(62, 1, 1, 100),
                new NoteEvent(62, 2, 1, 100),
                new NoteEvent(62, 3, 1, 100)
            }
        };
        var clip3 = new Clip(2, true)
        {
            Notes = new SortedList <NoteEvent>()
            {
                new NoteEvent(64, .5m, 1, 100),
                new NoteEvent(64, 1.5m, .5m, 100),
            }
        };

        ClipUtilities.NormalizeClipLengths(new[] { clip1, clip2, clip3 });
        Assert.AreEqual(clip1.Notes[0].Start, 0);
        Assert.AreEqual(clip1.Notes[1].Start, .4m);
        Assert.AreEqual(clip1.Notes[2].Start, 1);
        Assert.AreEqual(clip1.Notes[3].Start, 1.4m);
        Assert.AreEqual(clip1.Notes[4].Start, 2);
        Assert.AreEqual(clip1.Notes[5].Start, 2.4m);
        Assert.AreEqual(clip1.Notes[6].Start, 3);
        Assert.AreEqual(clip1.Notes[7].Start, 3.4m);
        Assert.AreEqual(clip1.Notes.Count, 8);
        Assert.AreEqual(clip2.Notes[0].Start, 0);
        Assert.AreEqual(clip2.Notes[1].Start, 1);
        Assert.AreEqual(clip2.Notes[2].Start, 2);
        Assert.AreEqual(clip2.Notes[3].Start, 3);
        Assert.AreEqual(clip2.Notes.Count, 4);
        Assert.AreEqual(clip3.Notes[0].Start, .5m);
        Assert.AreEqual(clip3.Notes[1].Start, 1.5m);
        Assert.AreEqual(clip3.Notes[2].Start, 2.5m);
        Assert.AreEqual(clip3.Notes[3].Start, 3.5m);
        Assert.AreEqual(clip3.Notes.Count, 4);
    }
示例#16
0
    public static ProcessResult <Clip[]> Apply(SetLengthOptions options, params Clip[] clips)
    {
        var resultClips = ClipUtilities.CreateEmptyPlaceholderClips(clips);

        for (var index = 0; index < clips.Length; index++)
        {
            var clip          = clips[index];
            var resultClip    = resultClips[index];
            var lengthCounter = 0;
            foreach (var note in clip.Notes)
            {
                ClipUtilities.AddNoteCutting(resultClip, note with
                {
                    Duration = options.Lengths[lengthCounter++ % options.Lengths.Length]
                });
示例#17
0
文件: Crop.cs 项目: rinckd/mutateful
        public static ProcessResultArray <Clip> Apply(CropOptions options, params Clip[] clips)
        {
            var processedClips = new Clip[clips.Length];
            var start          = options.Lengths.Length > 1 ? options.Lengths[0] : 0;
            var duration       = options.Lengths.Length > 1 ? options.Lengths[1] : options.Lengths[0];
            var i = 0;

            foreach (var clip in clips)
            {
                var processedClip = new Clip(duration, clip.IsLooping);
                processedClip.Notes.AddRange(ClipUtilities.GetSplitNotesInRangeAtPosition(start, start + duration, clip.Notes, 0));
                processedClips[i++] = processedClip;
            }
            return(new ProcessResultArray <Clip>(processedClips));
        }
示例#18
0
        // TODO: Add option to cut overlapping events, so that more of the original clip is preserved

        public static ProcessResultArray <Clip> Apply(params Clip[] clips)
        {
            var resultClips = ClipUtilities.CreateEmptyPlaceholderClips(clips);

            for (var i = 0; i < clips.Length; i++)
            {
                var clip       = clips[i];
                var resultClip = resultClips[i];
                foreach (var note in clip.Notes)
                {
                    var newNote = new NoteEvent(note);
                    AddNoteCutting(resultClip, newNote);
                }
            }

            return(Filter.Apply(new FilterOptions(), resultClips));
        }
示例#19
0
    public static ProcessResult <Clip[]> Apply(QuantizeOptions options, params Clip[] clips)
    {
        var maxLen = clips.Max(x => x.Length);

        if (options.By != null)
        {
            if (options.By.Length < maxLen)
            {
                ClipUtilities.EnlargeClipByLooping(options.By, maxLen);
            }
            options.Divisions = options.By.Notes.Select(x => x.Start).Distinct().ToArray();
        }
        else
        {
            var currentPos        = 0m;
            var quantizePositions = new List <decimal>();
            var i = 0;
            while (currentPos <= maxLen)
            {
                quantizePositions.Add(currentPos);
                currentPos += options.Divisions[i % options.Divisions.Length];
                i++;
            }
            options.Divisions = quantizePositions.ToArray();
        }
        options.Amount = Math.Clamp(options.Amount, 0, 1);
        var resultClips = new Clip[clips.Length];

        for (var i = 0; i < clips.Length; i++)
        {
            var clip       = clips[i];
            var resultClip = new Clip(clip.Length, clip.IsLooping);

            foreach (var note in clip.Notes)
            {
                var constrainedNote = note with {
                };
                var newStart        = ClipUtilities.FindNearestNoteStartInDecimalSet(note, options.Divisions);
                constrainedNote.Start += (newStart - constrainedNote.Start) * options.Amount;
                resultClip.Add(constrainedNote);
            }
            resultClips[i] = resultClip;
        }
        return(new ProcessResult <Clip[]>(resultClips));
    }
示例#20
0
        public static ProcessResultArray <Clip> Apply(VelocityScaleOptions options, params Clip[] clips)
        {
            var processedClips = new Clip[clips.Length];

            var i = 0;

            foreach (var clip in clips)
            {
                var processedClip = new Clip(clip.Length, clip.IsLooping);

                processedClip.Notes.AddRange(ClipUtilities.GetSplitNotesInRangeAtPosition(0, clip.Length, clip.Notes, 0));

                foreach (var item in processedClip.Notes)
                {
                    item.Velocity = System.Math.Min(FullMidiVelocityRange, System.Math.Abs((int)System.Math.Floor(item.Velocity * options.Strength)));
                }
                processedClips[i++] = processedClip;
            }
            return(new ProcessResultArray <Clip>(processedClips));
        }
示例#21
0
    public static ProcessResult <Clip[]> Apply(ScaleOptions options, params Clip[] clips)
    {
        if (options.By != null)
        {
            clips = clips.Prepend(options.By).ToArray();
        }
        ClipUtilities.NormalizeClipLengths(clips);
        if (clips.Length < 2)
        {
            return(new ProcessResult <Clip[]>(clips));
        }
        var masterClip     = clips[0];
        var slaveClips     = clips.Skip(1).ToArray();
        var processedClips = slaveClips.Select(c => new Clip(c.Length, c.IsLooping)).ToArray();

        for (var i = 0; i < slaveClips.Length; i++)
        {
            var slaveClip = slaveClips[i];
            foreach (var note in slaveClip.Notes)
            {
                var masterNotes = SortedList <NoteEvent> .Empty;
                if (options.PositionAware)
                {
                    masterNotes = masterClip.Notes.Where(x => x.StartsInsideIntervalInclusive(note.Start, note.End) || x.CoversInterval(note.Start, note.End)).ToSortedList();
                }
                if (masterNotes.Count == 0)
                {
                    masterNotes = masterClip.Notes;
                }

                var constrainedNote   = note with {
                };
                constrainedNote.Pitch = options.Strict ?
                                        ClipUtilities.FindNearestNotePitchInSet(note, masterNotes) :
                                        ClipUtilities.FindNearestNotePitchInSetMusical(note, masterNotes);
                processedClips[i].Notes.Add(constrainedNote);
            }
        }
        return(new ProcessResult <Clip[]>(processedClips));
    }
示例#22
0
    public static ProcessResult <Clip[]> Apply(RemapOptions options, params Clip[] clips)
    {
        var resultClips = ClipUtilities.CreateEmptyPlaceholderClips(clips);

        for (var i = 0; i < clips.Length; i++)
        {
            var clip          = clips[i];
            var resultClip    = resultClips[i];
            var sourcePitches = clip.Notes.Select(x => x.Pitch).Distinct().OrderBy(x => x).ToList();
            var destPitches   = options.To.Count > 0
                ? options.To.Notes.Select(x => x.Pitch).Distinct().OrderBy(x => x).ToList()
                : Enumerable.Range(36, Math.Min(sourcePitches.Count, 128 - 36)).ToList();
            var inc = 1f;

            if (destPitches.Count < sourcePitches.Count)
            {
                inc = (float)destPitches.Count / sourcePitches.Count;
            }

            var map    = new Dictionary <int, int>();
            var destIx = 0f;
            foreach (var sourcePitch in sourcePitches)
            {
                map[sourcePitch] = destPitches[(int)Math.Floor(destIx)];
                destIx          += inc;
            }

            foreach (var note in clip.Notes)
            {
                var remappedNote = note with {
                    Pitch = map[note.Pitch]
                };
                ClipUtilities.AddNoteCutting(resultClip, remappedNote);
            }
        }
        return(new ProcessResult <Clip[]>(resultClips));
    }
}
示例#23
0
        public static ProcessResultArray <Clip> Apply(RatchetOptions options, params Clip[] clips)
        {
            options.Strength = Math.Clamp(options.Strength, 0, 1);
            if (options.By != null)
            {
                clips = clips.Prepend(options.By).ToArray();
            }
            ClipUtilities.NormalizeClipLengths(clips);
            if (clips.Length < 2)
            {
                clips = new[] { clips[0], clips[0] };
            }
            Clip controlSequence = new Clip(clips[0]);

            Clip[] targetSequences = clips.Skip(1).Select(x => new Clip(x)).ToArray();
            Clip[] resultSequences = new Clip[targetSequences.Length];

            if (options.RatchetValues.Length > 0)
            {
                for (var i = 0; i < options.RatchetValues.Length; i++)
                {
                    if (options.RatchetValues[i] < 1)
                    {
                        options.RatchetValues[i] = 1;                               // todo: should be handled by optionparser and min max values in optioninfo
                    }
                }
                for (var i = 0; i < targetSequences.Length; i++)
                {
                    var targetSequence = targetSequences[i];
                    resultSequences[i] = DoManualRatchet(options.RatchetValues, targetSequence, options.Strength, options.VelocityToStrength, options.Shape,
                                                         options.Mode);
                }
            }
            else
            {
                var(controlMin, controlMax, targetRange) = options.Mode == RatchetMode.Pitch
                    ? GetControlValuesFromPitch(options, controlSequence)
                    : GetControlValuesFromVelocity(options, controlSequence);

                float controlRange = Math.Max(controlMax - controlMin, 1);

                // set pitch for each note in control sequence
                if (options.Mode == RatchetMode.Pitch)
                {
                    foreach (var note in controlSequence.Notes)
                    {
                        note.Pitch = (int)Math.Round((note.Pitch - controlMin) / controlRange * targetRange) + 1;
                    }
                }
                else
                {
                    foreach (var note in controlSequence.Notes)
                    {
                        note.Velocity = (int)Math.Round((Math.Clamp(note.Velocity, controlMin, controlMax) - controlMin) / controlRange * targetRange) + 1;
                    }
                }
                for (var i = 0; i < targetSequences.Length; i++)
                {
                    var targetSequence = targetSequences[i];
                    resultSequences[i] = DoRatchet(controlSequence, targetSequence, (float)options.Strength, options.VelocityToStrength, options.Shape,
                                                   options.Mode);
                }
            }


            return(new ProcessResultArray <Clip>(resultSequences));
        }
示例#24
0
    public static ProcessResult <Clip[]> Apply(ShuffleOptions options, params Clip[] clips)
    {
        if (options.By.Notes.Count == 0)
        {
            options.By = clips[0];
        }
        if (options.By.Count == 0 && options.ShuffleValues.Length == 0)
        {
            return(new ProcessResult <Clip[]>("No -by clip or shuffle values specified."));
        }

        ClipUtilities.Monophonize(options.By);
        var targetClips = new Clip[clips.Length];

        int[] shuffleValues;
        if (options.ShuffleValues.Length == 0)
        {
            int minPitch = options.By.Notes.Min(x => x.Pitch);
            shuffleValues = options.By.Notes.Select(x => x.Pitch - minPitch).ToArray();
        }
        else
        {
            shuffleValues = options.ShuffleValues.Select(x => Math.Clamp(x, 1, 100) - 1).ToArray();
        }

        var c = 0;

        foreach (var clip in clips) // we only support one generated clip since these are tied to a specific clip slot. Maybe support multiple clips under the hood, but discard any additional clips when sending the output is the most flexible approach.
        {
            clip.GroupSimultaneousNotes();
            targetClips[c] = new Clip(clip.Length, clip.IsLooping);

            var numShuffleIndexes = shuffleValues.Length;
            if (numShuffleIndexes < clip.Notes.Count)
            {
                numShuffleIndexes = clip.Notes.Count;
            }
            var indexes = new int[numShuffleIndexes];

            for (var i = 0; i < numShuffleIndexes; i++)
            {
                // Calc shuffle indexes as long as there are notes in the source clip. If the clip to be shuffled contains more events than the source, add zero-indexes so that the rest of the sequence is produced sequentially.
                if (i < shuffleValues.Length)
                {
                    indexes[i] = (int)Math.Floor(((float)shuffleValues[i] / clip.Notes.Count) * clip.Notes.Count);
                }
                else
                {
                    indexes[i] = 0;
                }
            }

            // preserve original durations until next note
            var durationUntilNextNote = new List <decimal>(clip.Notes.Count);
            for (var i = 0; i < clip.Notes.Count; i++)
            {
                durationUntilNextNote.Add(clip.DurationUntilNextNote(i));
            }

            // do shuffle
            var     j   = 0;
            decimal pos = 0m;
            while (clip.Notes.Count > 0)
            {
                int currentIx = indexes[j++] % clip.Notes.Count;
                targetClips[c].Notes.Add(
                    clip.Notes[currentIx] with {
                    Start = pos
                }
示例#25
0
        public static ProcessResultArray <Clip> Apply(InterleaveOptions options, ClipMetaData metadata, params Clip[] clips)
        {
            if (clips.Length < 2)
            {
                clips = new[] { clips[0], clips[0] };
            }
            decimal position     = 0;
            int     repeatsIndex = 0;
            Clip    resultClip   = new Clip(4, true);

            switch (options.Mode)
            {
            case Event:
                if (options.ChunkChords)
                {
                    foreach (var clip in clips)
                    {
                        clip.GroupSimultaneousNotes();
                    }
                }
                var noteCounters = clips.Select(c => new IntCounter(c.Notes.Count)).ToArray();
                position = clips[0].Notes[0].Start;

                while (noteCounters.Any(nc => !nc.Overflow))
                {
                    for (var clipIndex = 0; clipIndex < clips.Length; clipIndex++)
                    {
                        var clip = clips[clipIndex];
                        var currentNoteCounter = noteCounters[clipIndex];

                        for (var repeats = 0; repeats < options.Repeats[repeatsIndex % options.Repeats.Length]; repeats++)
                        {
                            var note = clip.Notes[currentNoteCounter.Value];

                            if (!options.Solo || clip.ClipReference.Track == metadata.TrackNumber)
                            {
                                var newNote = new NoteEvent(note);
                                newNote.Start = position;
                                resultClip.Notes.Add(newNote);
                            }
                            position += clip.DurationUntilNextNote(currentNoteCounter.Value);
                        }
                        if (options.Skip)
                        {
                            foreach (var noteCounter in noteCounters)
                            {
                                noteCounter.Inc();
                            }
                        }
                        else
                        {
                            currentNoteCounter.Inc();
                        }
                        repeatsIndex++;
                    }
                }
                if (options.ChunkChords)
                {
                    resultClip.Flatten();
                }
                break;

            case Time:
                var srcPositions   = clips.Select(c => new DecimalCounter(c.Length)).ToArray();
                int timeRangeIndex = 0;

                while (srcPositions.Any(c => !c.Overflow))
                {
                    for (var clipIndex = 0; clipIndex < clips.Length; clipIndex++)
                    {
                        var clip             = clips[clipIndex];
                        var currentTimeRange = options.Ranges[timeRangeIndex];
                        for (var repeats = 0; repeats < options.Repeats[repeatsIndex % options.Repeats.Length]; repeats++)
                        {
                            if (!options.Solo || clip.ClipReference.Track == metadata.TrackNumber)
                            {
                                resultClip.Notes.AddRange(
                                    ClipUtilities.GetSplitNotesInRangeAtPosition(
                                        srcPositions[clipIndex].Value,
                                        srcPositions[clipIndex].Value + currentTimeRange,
                                        clips[clipIndex].Notes,
                                        position
                                        )
                                    );
                            }
                            position += currentTimeRange;
                        }
                        if (options.Skip)
                        {
                            foreach (var srcPosition in srcPositions)
                            {
                                srcPosition.Inc(currentTimeRange);
                            }
                        }
                        else
                        {
                            srcPositions[clipIndex].Inc(currentTimeRange);
                        }
                        repeatsIndex++;
                        timeRangeIndex = (timeRangeIndex + 1) % options.Ranges.Length;     // this means that you cannot use the Counts parameter to have varying time ranges for each repeat
                    }
                }
                break;
            }
            resultClip.Length = position;
            return(new ProcessResultArray <Clip>(new[] { resultClip }));
        }