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)); }
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)); }
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); }
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)); }
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)); }