From 631007059751cebf45b75027d97f754628d931fa Mon Sep 17 00:00:00 2001 From: p4o-a7o <94420131+p4o-a7o@users.noreply.github.com> Date: Mon, 9 Feb 2026 19:26:53 -0500 Subject: [PATCH 01/10] add SimplexNoise --- OpenUtau.Core/ThirdParty/SimplexNoise.cs | 455 +++++++++++++++++++++++ 1 file changed, 455 insertions(+) create mode 100644 OpenUtau.Core/ThirdParty/SimplexNoise.cs diff --git a/OpenUtau.Core/ThirdParty/SimplexNoise.cs b/OpenUtau.Core/ThirdParty/SimplexNoise.cs new file mode 100644 index 000000000..8258fabed --- /dev/null +++ b/OpenUtau.Core/ThirdParty/SimplexNoise.cs @@ -0,0 +1,455 @@ +/* +BSD 3-Clause License + +Copyright (c) 2019, Benjamin Ward +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Simplex Noise for C# +// Copyright © Benjamin Ward 2019 +// See LICENSE +// Simplex Noise implementation offering 1D, 2D, and 3D forms w/ values in the range of 0 to 255. +// Based on work by Heikki Törmälä (2012) and Stefan Gustavson (2006). + +using System; + +namespace SimplexNoise +{ + /// + /// Implementation of the Perlin simplex noise, an improved Perlin noise algorithm. + /// Based loosely on SimplexNoise1234 by Stefan Gustavson: http://staffwww.itn.liu.se/~stegu/aqsis/aqsis-newnoise/ + /// + public static class Noise + { + /// + /// Creates 1D Simplex noise + /// + /// The number of points to generate + /// The scale of the noise. The greater the scale, the denser the noise gets + /// An array containing 1D Simplex noise + public static float[] Calc1D(int width, float scale) + { + var values = new float[width]; + for (var i = 0; i < width; i++) + values[i] = Generate(i * scale) * 128 + 128; + return values; + } + + /// + /// Creates 2D Simplex noise + /// + /// The number of points to generate in the 1st dimension + /// The number of points to generate in the 2nd dimension + /// The scale of the noise. The greater the scale, the denser the noise gets + /// An array containing 2D Simplex noise + public static float[,] Calc2D(int width, int height, float scale) + { + var values = new float[width, height]; + for (var i = 0; i < width; i++) + for (var j = 0; j < height; j++) + values[i, j] = Generate(i * scale, j * scale) * 128 + 128; + return values; + } + + /// + /// Creates 3D Simplex noise + /// + /// The number of points to generate in the 1st dimension + /// The number of points to generate in the 2nd dimension + /// The number of points to generate in the 3nd dimension + /// The scale of the noise. The greater the scale, the denser the noise gets + /// An array containing 3D Simplex noise + public static float[, ,] Calc3D(int width, int height, int length, float scale) + { + var values = new float[width, height, length]; + for (var i = 0; i < width; i++) + for (var j = 0; j < height; j++) + for (var k = 0; k < length; k++) + values[i, j, k] = Generate(i * scale, j * scale, k * scale) * 128 + 128; + return values; + } + + /// + /// Gets the value of an index of 1D simplex noise + /// + /// Index + /// The scale of the noise. The greater the scale, the denser the noise gets + /// The value of an index of 1D simplex noise + public static float CalcPixel1D(int x, float scale) + { + return Generate(x * scale) * 128 + 128; + } + + /// + /// Gets the value of an index of 2D simplex noise + /// + /// 1st dimension index + /// 2st dimension index + /// The scale of the noise. The greater the scale, the denser the noise gets + /// The value of an index of 2D simplex noise + public static float CalcPixel2D(int x, int y, float scale) + { + return Generate(x * scale, y * scale) * 128 + 128; + } + + + /// + /// Gets the value of an index of 3D simplex noise + /// + /// 1st dimension index + /// 2nd dimension index + /// 3rd dimension index + /// The scale of the noise. The greater the scale, the denser the noise gets + /// The value of an index of 3D simplex noise + public static float CalcPixel3D(int x, int y, int z, float scale) + { + return Generate(x * scale, y * scale, z * scale) * 128 + 128; + } + + static Noise() + { + _perm = new byte[PermOriginal.Length]; + PermOriginal.CopyTo(_perm, 0); + } + + /// + /// Arbitrary integer seed used to generate lookup table used internally + /// + public static int Seed + { + get => _seed; + set + { + if (value == 0) + { + _perm = new byte[PermOriginal.Length]; + PermOriginal.CopyTo(_perm, 0); + } + else + { + // Begin discontinuity fix by @ntark // + + // Fixes issue #7 and #8 + // https://github.com/WardBenjamin/SimplexNoise/pull/13 + // permutation matrix is duplicated to handle rollovers + _perm = new byte[512]; + + var perm = new byte[256]; + new Random(value).NextBytes(perm); + + Array.Copy(perm, 0, _perm, 0, 256); + Array.Copy(perm, 0, _perm, 256, 256); + + // End discontinuity fix // + } + + _seed = value; + } + } + + private static int _seed; + + /// + /// 1D simplex noise + /// + /// + /// + public static float Generate(float x) // made public + { + var i0 = FastFloor(x); + var i1 = i0 + 1; + var x0 = x - i0; + var x1 = x0 - 1.0f; + + var t0 = 1.0f - x0 * x0; + t0 *= t0; + var n0 = t0 * t0 * Grad(_perm[i0 & 0xff], x0); + + var t1 = 1.0f - x1 * x1; + t1 *= t1; + var n1 = t1 * t1 * Grad(_perm[i1 & 0xff], x1); + // The maximum value of this noise is 8*(3/4)^4 = 2.53125 + // A factor of 0.395 scales to fit exactly within [-1,1] + return 0.395f * (n0 + n1); + } + + /// + /// 2D simplex noise + /// + /// + /// + /// + public static float Generate(float x, float y) // made public + { + const float F2 = 0.366025403f; // F2 = 0.5*(sqrt(3.0)-1.0) + const float G2 = 0.211324865f; // G2 = (3.0-Math.sqrt(3.0))/6.0 + + float n0, n1, n2; // Noise contributions from the three corners + + // Skew the input space to determine which simplex cell we're in + var s = (x + y) * F2; // Hairy factor for 2D + var xs = x + s; + var ys = y + s; + var i = FastFloor(xs); + var j = FastFloor(ys); + + var t = (i + j) * G2; + var X0 = i - t; // Unskew the cell origin back to (x,y) space + var Y0 = j - t; + var x0 = x - X0; // The x,y distances from the cell origin + var y0 = y - Y0; + + // For the 2D case, the simplex shape is an equilateral triangle. + // Determine which simplex we are in. + int i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords + if (x0 > y0) { i1 = 1; j1 = 0; } // lower triangle, XY order: (0,0)->(1,0)->(1,1) + else { i1 = 0; j1 = 1; } // upper triangle, YX order: (0,0)->(0,1)->(1,1) + + // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and + // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where + // c = (3-sqrt(3))/6 + + var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords + var y1 = y0 - j1 + G2; + var x2 = x0 - 1.0f + 2.0f * G2; // Offsets for last corner in (x,y) unskewed coords + var y2 = y0 - 1.0f + 2.0f * G2; + + // Wrap the integer indices at 256, to avoid indexing perm[] out of bounds + var ii = Mod(i, 256); + var jj = Mod(j, 256); + + // Calculate the contribution from the three corners + var t0 = 0.5f - x0 * x0 - y0 * y0; + if (t0 < 0.0f) n0 = 0.0f; + else + { + t0 *= t0; + n0 = t0 * t0 * Grad(_perm[ii + _perm[jj]], x0, y0); + } + + var t1 = 0.5f - x1 * x1 - y1 * y1; + if (t1 < 0.0f) n1 = 0.0f; + else + { + t1 *= t1; + n1 = t1 * t1 * Grad(_perm[ii + i1 + _perm[jj + j1]], x1, y1); + } + + var t2 = 0.5f - x2 * x2 - y2 * y2; + if (t2 < 0.0f) n2 = 0.0f; + else + { + t2 *= t2; + n2 = t2 * t2 * Grad(_perm[ii + 1 + _perm[jj + 1]], x2, y2); + } + + // Add contributions from each corner to get the final noise value. + // The result is scaled to return values in the interval [-1,1]. + return 40.0f * (n0 + n1 + n2); // TODO: The scale factor is preliminary! + } + + + public static float Generate(float x, float y, float z) // made public + { + // Simple skewing factors for the 3D case + const float F3 = 0.333333333f; + const float G3 = 0.166666667f; + + float n0, n1, n2, n3; // Noise contributions from the four corners + + // Skew the input space to determine which simplex cell we're in + var s = (x + y + z) * F3; // Very nice and simple skew factor for 3D + var xs = x + s; + var ys = y + s; + var zs = z + s; + var i = FastFloor(xs); + var j = FastFloor(ys); + var k = FastFloor(zs); + + var t = (i + j + k) * G3; + var X0 = i - t; // Unskew the cell origin back to (x,y,z) space + var Y0 = j - t; + var Z0 = k - t; + var x0 = x - X0; // The x,y,z distances from the cell origin + var y0 = y - Y0; + var z0 = z - Z0; + + // For the 3D case, the simplex shape is a slightly irregular tetrahedron. + // Determine which simplex we are in. + int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords + int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords + + /* This code would benefit from a backport from the GLSL version! */ + if (x0 >= y0) + { + if (y0 >= z0) + { i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 1; k2 = 0; } // X Y Z order + else if (x0 >= z0) { i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 0; k2 = 1; } // X Z Y order + else { i1 = 0; j1 = 0; k1 = 1; i2 = 1; j2 = 0; k2 = 1; } // Z X Y order + } + else + { // x0 0) ? ((int)x) : (((int)x) - 1); + } + + private static int Mod(int x, int m) + { + var a = x % m; + return a < 0 ? a + m : a; + } + + private static float Grad(int hash, float x) + { + var h = hash & 15; + var grad = 1.0f + (h & 7); // Gradient value 1.0, 2.0, ..., 8.0 + if ((h & 8) != 0) grad = -grad; // Set a random sign for the gradient + return (grad * x); // Multiply the gradient with the distance + } + + private static float Grad(int hash, float x, float y) + { + var h = hash & 7; // Convert low 3 bits of hash code + var u = h < 4 ? x : y; // into 8 simple gradient directions, + var v = h < 4 ? y : x; // and compute the dot product with (x,y). + return ((h & 1) != 0 ? -u : u) + ((h & 2) != 0 ? -2.0f * v : 2.0f * v); + } + + private static float Grad(int hash, float x, float y, float z) + { + var h = hash & 15; // Convert low 4 bits of hash code into 12 simple + var u = h < 8 ? x : y; // gradient directions, and compute dot product. + var v = h < 4 ? y : h == 12 || h == 14 ? x : z; // Fix repeats at h = 12 to 15 + return ((h & 1) != 0 ? -u : u) + ((h & 2) != 0 ? -v : v); + } + + private static float Grad(int hash, float x, float y, float z, float t) + { + var h = hash & 31; // Convert low 5 bits of hash code into 32 simple + var u = h < 24 ? x : y; // gradient directions, and compute dot product. + var v = h < 16 ? y : z; + var w = h < 8 ? z : t; + return ((h & 1) != 0 ? -u : u) + ((h & 2) != 0 ? -v : v) + ((h & 4) != 0 ? -w : w); + } + } +} From dee9b16e7bfcc55cd3bb800c01665dff22d47aa6 Mon Sep 17 00:00:00 2001 From: p4o-a7o <94420131+p4o-a7o@users.noreply.github.com> Date: Mon, 9 Feb 2026 19:29:04 -0500 Subject: [PATCH 02/10] add new vibrato variance fields & behavior --- OpenUtau.Core/Commands/NoteCommands.cs | 107 +++++++++++++++++++++++++ OpenUtau.Core/Ustx/UNote.cs | 46 ++++++++++- OpenUtau.Core/Util/NotePresets.cs | 22 +++-- 3 files changed, 166 insertions(+), 9 deletions(-) diff --git a/OpenUtau.Core/Commands/NoteCommands.cs b/OpenUtau.Core/Commands/NoteCommands.cs index 85bde011f..259607aab 100644 --- a/OpenUtau.Core/Commands/NoteCommands.cs +++ b/OpenUtau.Core/Commands/NoteCommands.cs @@ -454,6 +454,113 @@ public override void Unexecute() { } } + public class VibratoVarianceDepthCommand : VibratoCommand { + readonly UNote note; + readonly float newVariance; + readonly float oldVariance; + + public VibratoVarianceDepthCommand(UVoicePart part, UNote note, float variance) : base(part, note) { + this.note = note; + newVariance = variance; + oldVariance = note.vibrato.variance; + } + + public override string ToString() { + return "Change vibrato variance depth"; + } + + public override void Execute() { + lock (Part) { + note.vibrato.variance = newVariance; + } + } + public override void Unexecute() { + lock (Part) { + note.vibrato.variance = oldVariance; + } + } + } + + public class VibratoPitchVarianceCommand : VibratoCommand { + readonly UNote note; + readonly float newPitchVariance; + readonly float oldPitchVariance; + + public VibratoPitchVarianceCommand(UVoicePart part, UNote note, float pitchVariance) : base(part, note) { + this.note = note; + newPitchVariance = pitchVariance; + oldPitchVariance = note.vibrato.pitchVariance; + } + + public override string ToString() { + return "Change vibrato pitch variance depth"; + } + + public override void Execute() { + lock (Part) { + note.vibrato.pitchVariance = newPitchVariance; + } + } + public override void Unexecute() { + lock (Part) { + note.vibrato.pitchVariance = oldPitchVariance; + } + } + } + + public class VibratoVarianceFrequencyCommand : VibratoCommand { + readonly UNote note; + readonly float newVarianceFreq; + readonly float oldVarianceFreq; + + public VibratoVarianceFrequencyCommand(UVoicePart part, UNote note, float frequency) : base(part, note) { + this.note = note; + newVarianceFreq = frequency; + oldVarianceFreq = note.vibrato.varianceFreq; + } + + public override string ToString() { + return "Change vibrato variance frequency"; + } + + public override void Execute() { + lock (Part) { + note.vibrato.varianceFreq = newVarianceFreq; + } + } + public override void Unexecute() { + lock (Part) { + note.vibrato.varianceFreq = oldVarianceFreq; + } + } + } + + public class VibratoVarianceSeedCommand : VibratoCommand { + readonly UNote note; + readonly int newVarianceSeed; + readonly int oldVarianceSeed; + + public VibratoVarianceSeedCommand(UVoicePart part, UNote note, int seed) : base(part, note) { + this.note = note; + newVarianceSeed = seed; + oldVarianceSeed = note.vibrato.varianceSeed; + } + + public override string ToString() { + return "Change vibrato variance seed"; + } + + public override void Execute() { + lock (Part) { + note.vibrato.varianceSeed = newVarianceSeed; + } + } + public override void Unexecute() { + lock (Part) { + note.vibrato.varianceSeed = oldVarianceSeed; + } + } + } public class PhonemeOffsetCommand : NoteCommand { readonly UNote note; diff --git a/OpenUtau.Core/Ustx/UNote.cs b/OpenUtau.Core/Ustx/UNote.cs index e4c8bd144..c73a7c983 100644 --- a/OpenUtau.Core/Ustx/UNote.cs +++ b/OpenUtau.Core/Ustx/UNote.cs @@ -6,6 +6,7 @@ using OpenUtau.Api; using OpenUtau.Core.Util; using YamlDotNet.Serialization; +using SimplexNoise; namespace OpenUtau.Core.Ustx { public class UNote : IComparable { @@ -281,6 +282,14 @@ public class UVibrato { float _drift = NotePresets.Default.DefaultVibrato.VibratoDrift; // Percentage of volume reduction in linkage with vibrato. When this is 100%, volume will be 1.2 times to 0.2 times regardless of depth. float _volLink = NotePresets.Default.DefaultVibrato.VibratoVolLink; + // Amount of variance in the shape of the vibrato curve. + float _variance = NotePresets.Default.DefaultVibrato.VibratoVariance; + // Amount of variance in the intensity of the vibrato curve. + float _pitchVariance = NotePresets.Default.DefaultVibrato.VibratoVariance; + // Frequency of variance in the shape of the vibrato curve. + float _varianceFreq = NotePresets.Default.DefaultVibrato.VibratoVarianceFreq; + // Random seed used for vibrato variance + int _varianceSeed = NotePresets.Default.DefaultVibrato.VibratoVarianceSeed; public float length { get => _length; set => _length = Math.Max(0, Math.Min(100, value)); } public float period { get => _period; set => _period = Math.Max(5, Math.Min(500, value)); } @@ -305,6 +314,11 @@ public float @out { public float drift { get => _drift; set => _drift = Math.Max(-100, Math.Min(100, value)); } public float volLink { get => _volLink; set => _volLink = Math.Max(-100, Math.Min(100, value)); } + public float variance { get => _variance; set => _variance = Math.Max(0, Math.Min(2, value)); } + public float pitchVariance { get => _pitchVariance; set => _pitchVariance = Math.Max(0, Math.Min(2, value)); } + public float varianceFreq { get => _varianceFreq; set => _varianceFreq = Math.Max(0.1F, Math.Min(8, value)); } + public int varianceSeed { get => _varianceSeed; set => _varianceSeed = value; } + [YamlIgnore] public float NormalizedStart => 1f - length / 100f; public UVibrato Clone() { @@ -316,7 +330,11 @@ public UVibrato Clone() { @out = @out, shift = shift, drift = drift, - volLink = volLink + volLink = volLink, + variance = variance, + pitchVariance = pitchVariance, + varianceFreq = varianceFreq, + varianceSeed = varianceSeed }; return result; } @@ -338,7 +356,19 @@ public Vector2 Evaluate(float nPos, float nPeriod, UNote note) { float nOut = length / 100f * @out / 100f; float nOutPos = 1f - nOut; float t = (nPos - nStart) / nPeriod + shift / 100f; - float y = (float)Math.Sin(2 * Math.PI * t) * depth + (depth / 100 * drift); + float y; + if(variance > 0 || pitchVariance > 0) { + Noise.Seed = varianceSeed; // setting seed might need optimization + // * 0.33F so it has a reasonable base value, it's too much at unity + float variancePhaseShift = Noise.Generate(t * varianceFreq) * 0.33F; + Noise.Seed = varianceSeed + 1; + float pitchNoise = Noise.Generate(t * varianceFreq) * 100 * pitchVariance; + t += variancePhaseShift * variance; + float normalizedDepth = 200 * ((depth - 5) / (200 - 5)); + y = (float)Math.Sin(2 * Math.PI * t) * depth + (depth / 100 * drift) + (normalizedDepth / 100 * pitchNoise); + } else { + y = (float)Math.Sin(2 * Math.PI * t) * depth + (depth / 100 * drift); + } if (nPos < nStart) { y = 0; } else if (nPos < nInPos) { @@ -367,7 +397,17 @@ public float EvaluateVolume(float nPos, float nPeriod) { volLink *= -1; } float t = (nPos - nStart) / nPeriod + shift / 100f; - float reduction = (-(float)Math.Sin(2 * Math.PI * t) / 2 + 0.3f) * volLink / 100; + float reduction; + if(variance > 0) { + Noise.Seed = varianceSeed; // setting seed might need optimization + // * 0.33F so it has a reasonable base value, it's too much at unity + // TODO actually use pitch variance to influence volume link too?? + float variancePhaseShift = Noise.Generate(t * varianceFreq) * 0.33F; + t += variancePhaseShift * variance; + reduction = (-(float)Math.Sin(2 * Math.PI * t) / 2 + 0.3f) * volLink / 100; + } else { + reduction = (-(float)Math.Sin(2 * Math.PI * t) / 2 + 0.3f) * volLink / 100; + } if (nPos < nStart) { reduction = 0; } else if (nPos < nInPos) { diff --git a/OpenUtau.Core/Util/NotePresets.cs b/OpenUtau.Core/Util/NotePresets.cs index 7a46e715b..b8cefb802 100644 --- a/OpenUtau.Core/Util/NotePresets.cs +++ b/OpenUtau.Core/Util/NotePresets.cs @@ -46,10 +46,10 @@ public static void Reset() { new PortamentoPreset("Snap", 2, -1), }); Default.VibratoPresets.AddRange(new List { - new VibratoPreset("Standard", 75, 175, 25, 10, 10, 0, 0, 0), - new VibratoPreset("UTAU Default", 65, 180, 35, 20, 20, 0, 0, 0), - new VibratoPreset("UTAU Strong", 65, 210, 55, 25, 25, 0, 0, 0), - new VibratoPreset("UTAU Weak", 65, 165, 20, 25, 25, 0, 0, 0) + new VibratoPreset("Standard", 75, 175, 25, 10, 10, 0, 0, 0, 0, 0, 1, 0), + new VibratoPreset("UTAU Default", 65, 180, 35, 20, 20, 0, 0, 0, 0, 0, 1, 0), + new VibratoPreset("UTAU Strong", 65, 210, 55, 25, 25, 0, 0, 0, 0, 0, 1, 0), + new VibratoPreset("UTAU Weak", 65, 165, 20, 25, 25, 0, 0, 0, 0, 0, 1, 0) }); Save(); @@ -61,7 +61,7 @@ public class SerializableNotePresets { public string SplittedLyric = "+"; public PortamentoPreset DefaultPortamento = new PortamentoPreset("Standard", 80, -40); public List PortamentoPresets = new List { }; - public VibratoPreset DefaultVibrato = new VibratoPreset("Standard", 75, 175, 25, 10, 10, 0, 0, 0); + public VibratoPreset DefaultVibrato = new VibratoPreset(); public List VibratoPresets = new List { }; public bool AutoVibratoToggle = false; public int AutoVibratoNoteDuration = 481; @@ -91,8 +91,12 @@ public class VibratoPreset { public float VibratoShift = 0; public float VibratoDrift = 0; public float VibratoVolLink = 0; + public float VibratoVariance = 0; + public float VibratoPitchVariance = 0; + public float VibratoVarianceFreq = 1; + public int VibratoVarianceSeed = 0; - public VibratoPreset(string name, float length, float period, float depth, float fadein, float fadeout, float shift, float drift, float volLink) { + public VibratoPreset(string name, float length, float period, float depth, float fadein, float fadeout, float shift, float drift, float volLink, float variance, float pitchVariance, float varianceFrequency, int varianceSeed) { Name = name; VibratoLength = length; VibratoPeriod = period; @@ -102,8 +106,14 @@ public VibratoPreset(string name, float length, float period, float depth, float VibratoShift = shift; VibratoDrift = drift; VibratoVolLink = volLink; + VibratoVariance = variance; + VibratoPitchVariance = pitchVariance; + VibratoVarianceFreq = varianceFrequency; + VibratoVarianceSeed = varianceSeed; } + public VibratoPreset() {} + public override string ToString() => Name; } From 1275719dd9f5d34c94383f316db28c0af1189d8b Mon Sep 17 00:00:00 2001 From: p4o-a7o <94420131+p4o-a7o@users.noreply.github.com> Date: Mon, 9 Feb 2026 19:29:19 -0500 Subject: [PATCH 03/10] bump ustx format version --- OpenUtau.Core/Format/USTx.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenUtau.Core/Format/USTx.cs b/OpenUtau.Core/Format/USTx.cs index 4922d0918..c657c4dd7 100644 --- a/OpenUtau.Core/Format/USTx.cs +++ b/OpenUtau.Core/Format/USTx.cs @@ -10,7 +10,7 @@ namespace OpenUtau.Core.Format { public class Ustx { - public static readonly Version kUstxVersion = new Version(0, 9); + public static readonly Version kUstxVersion = new Version(0, 9, 1); public const string DYN = "dyn"; public const string PITD = "pitd"; From 414d3f219011a6e700ef3215af891682446d628e Mon Sep 17 00:00:00 2001 From: p4o-a7o <94420131+p4o-a7o@users.noreply.github.com> Date: Mon, 9 Feb 2026 19:30:56 -0500 Subject: [PATCH 04/10] vibrato variance UI controls --- OpenUtau/Controls/NotePropertiesControl.axaml | 29 +++++++ .../Controls/NotePropertiesControl.axaml.cs | 6 ++ OpenUtau/Strings/Strings.axaml | 8 ++ OpenUtau/ViewModels/NoteDefaultsViewModel.cs | 41 +++++++++- .../ViewModels/NotePropertiesViewModel.cs | 82 ++++++++++++++++++- OpenUtau/Views/NoteDefaultsDialog.axaml | 23 ++++++ 6 files changed, 187 insertions(+), 2 deletions(-) diff --git a/OpenUtau/Controls/NotePropertiesControl.axaml b/OpenUtau/Controls/NotePropertiesControl.axaml index 2d1921a3c..92d979e4b 100644 --- a/OpenUtau/Controls/NotePropertiesControl.axaml +++ b/OpenUtau/Controls/NotePropertiesControl.axaml @@ -146,6 +146,35 @@ TickPlacement="BottomRight" TickFrequency="0.1" IsSnapToTickEnabled="true" Tag="VibratoVolLink"/> + + + + + + + +