private RenderItem BuildRenderItem(UPhoneme phoneme, UVoicePart part, UProject project) { USinger singer = project.Tracks[part.TrackNo].Singer; string rawfile = Lib.EncodingUtil.ConvertEncoding(singer.FileEncoding, singer.PathEncoding, phoneme.Oto.File); rawfile = Path.Combine(singer.Path, rawfile); double strechRatio = Math.Pow(2, 1.0 - (double)(int)phoneme.Parent.Expressions["velocity"].Data / 100); double length = phoneme.Oto.Preutter * strechRatio + phoneme.Envelope.Points[4].X; double requiredLength = Math.Ceiling(length / 50 + 1) * 50; double lengthAdjustment = phoneme.TailIntrude == 0 ? phoneme.Preutter : phoneme.Preutter - phoneme.TailIntrude + phoneme.TailOverlap; RenderItem item = new RenderItem() { // For resampler RawFile = rawfile, NoteNum = phoneme.Parent.NoteNum, Velocity = (int)phoneme.Parent.Expressions["velocity"].Data, Volume = (int)phoneme.Parent.Expressions["volume"].Data, StrFlags = phoneme.Parent.GetResamplerFlags(), PitchData = BuildPitchData(phoneme, part, project), RequiredLength = (int)requiredLength, Oto = phoneme.Oto, Tempo = project.BPM, // For connector SkipOver = phoneme.Oto.Preutter * strechRatio - phoneme.Preutter, PosMs = project.TickToMillisecond(part.PosTick + phoneme.Parent.PosTick + phoneme.PosTick) - phoneme.Preutter, DurMs = project.TickToMillisecond(phoneme.DurTick) + lengthAdjustment, Envelope = phoneme.Envelope.Points }; return(item); }
public RenderItem(UPhoneme phoneme, UVoicePart part, UProject project) { var singer = project.Tracks[part.TrackNo].Singer; SourceFile = phoneme.Oto.File; SourceFile = Path.Combine(PathManager.Inst.InstalledSingersPath, SourceFile); var strechRatio = Math.Pow(2, 1.0 - (double)(int)phoneme.Parent.Expressions["velocity"].Data / 100); var length = phoneme.Oto.Preutter * strechRatio + phoneme.Envelope.Points[4].X; var requiredLength = Math.Ceiling(length / 50 + 1) * 50; var lengthAdjustment = phoneme.TailIntrude == 0 ? phoneme.Preutter : phoneme.Preutter - phoneme.TailIntrude + phoneme.TailOverlap; NoteNum = phoneme.Parent.NoteNum; Velocity = (int)phoneme.Parent.Expressions["velocity"].Data; Volume = (int)phoneme.Parent.Expressions["volume"].Data; StrFlags = phoneme.Parent.GetResamplerFlags(); PitchData = BuildPitchData(phoneme, part, project); RequiredLength = (int)requiredLength; Oto = phoneme.Oto; Tempo = project.BPM; SkipOver = phoneme.Oto.Preutter * strechRatio - phoneme.Preutter; PosMs = project.TickToMillisecond(part.PosTick + phoneme.Parent.PosTick + phoneme.PosTick) - phoneme.Preutter; DurMs = project.TickToMillisecond(phoneme.DurTick) + lengthAdjustment; Envelope = phoneme.Envelope.Points; phonemeName = phoneme.Phoneme; }
public Tuple <MasterAdapter, List <Fader>, CancellationTokenSource, Task> RenderProject(int startTick) { var source = new CancellationTokenSource(); var items = new List <RenderItem>(); var faders = new List <Fader>(); foreach (var track in project.tracks) { var trackItems = PrepareTrack(track, project, startTick).ToArray(); var sources = trackItems.Select(item => { var waveSource = new WaveSource(item.PosMs, item.DurMs, item.Envelope, item.SkipOver); item.OnComplete = data => waveSource.SetWaveData(data); return(waveSource); }).ToList(); sources.AddRange(project.parts .Where(part => part is UWavePart && part.trackNo == track.TrackNo) .Select(part => part as UWavePart) .Select(part => { var waveSource = new WaveSource( project.TickToMillisecond(part.position), project.TickToMillisecond(part.Duration), null, part.skipMs); if (part.Samples != null) { waveSource.SetSamples(part.Samples); } return(waveSource); })); var trackMix = new WaveMix(sources); items.AddRange(trackItems); var fader = new Fader(trackMix); fader.Scale = PlaybackManager.DecibelToVolume(track.Volume); faders.Add(fader); } items = items.OrderBy(item => item.PosMs).ToList(); int threads = Util.Preferences.Default.PrerenderThreads; var progress = new Progress(items.Count); var task = Task.Run(() => { var progress = new Progress(items.Count); Parallel.ForEach(source: items, parallelOptions: new ParallelOptions() { MaxDegreeOfParallelism = threads }, body: item => { if (source.Token.IsCancellationRequested) { return; } item.progress = progress; Resample(item); }); ReleaseSourceTemp(); progress.Clear(); }); var master = new MasterAdapter(new WaveMix(faders)); master.SetPosition((int)(project.TickToMillisecond(startTick) * 44100 / 1000)); return(Tuple.Create(master, faders, source, task)); }
private void Render(UProject project, int tick) { IResamplerDriver driver = GetPreviewDriver(); if (driver == null) { return; } StopPreRender(); var scheduler = TaskScheduler.FromCurrentSynchronizationContext(); Task.Run(() => { RenderEngine engine = new RenderEngine(project, driver, cache, tick); var result = engine.RenderProject(tick); faders = result.Item2; var source = result.Item3; source = Interlocked.Exchange(ref playbakCancellationTokenSource, source); if (source != null) { source.Cancel(); Log.Information("Cancelling previous render"); } StartPlayback(project.TickToMillisecond(tick), result.Item1); }).ContinueWith((task) => { if (task.IsFaulted) { Log.Information($"{task.Exception}"); DocManager.Inst.ExecuteCmd(new UserMessageNotification(task.Exception.ToString())); throw task.Exception; } }, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, scheduler); }
public RenderItem(UPhoneme phoneme, UVoicePart part, UTrack track, UProject project, string resamplerName) { SourceFile = phoneme.oto.File; SourceFile = Path.Combine(PathManager.Inst.InstalledSingersPath, SourceFile); ResamplerName = resamplerName; if (project.expressions.TryGetValue("eng", out var descriptor)) { int index = (int)phoneme.GetExpression(project, "eng").Item1; string resampler = descriptor.options[index]; if (!string.IsNullOrEmpty(resampler)) { ResamplerName = resampler; } } string ext = Path.GetExtension(SourceFile); SourceTemp = Path.Combine(PathManager.Inst.GetCachePath(null), $"{HashHex(track.Singer.Id)}-{HashHex(phoneme.oto.Set)}-{HashHex(SourceFile)}{ext}"); Velocity = (int)phoneme.GetExpression(project, "vel").Item1; Volume = (int)phoneme.GetExpression(project, "vol").Item1; Modulation = (int)phoneme.GetExpression(project, "mod").Item1; var strechRatio = Math.Pow(2, 1.0 - Velocity / 100.0); var length = phoneme.oto.Preutter * strechRatio + phoneme.envelope.data[4].X; var requiredLength = Math.Ceiling(length / 50 + 1) * 50; var lengthAdjustment = phoneme.tailIntrude == 0 ? phoneme.preutter : phoneme.preutter - phoneme.tailIntrude + phoneme.tailOverlap; NoteNum = phoneme.Parent.tone; StrFlags = phoneme.GetResamplerFlags(project); PitchData = BuildPitchData(phoneme, part, project); RequiredLength = (int)requiredLength; Oto = phoneme.oto; Tempo = project.bpm; SkipOver = phoneme.oto.Preutter * strechRatio - phoneme.preutter; PosMs = project.TickToMillisecond(part.position + phoneme.Parent.position + phoneme.position) - phoneme.preutter; DurMs = project.TickToMillisecond(phoneme.Duration) + lengthAdjustment; Envelope = phoneme.envelope.data; phonemeName = phoneme.phoneme; }
public async Task <List <TrackSampleProvider> > RenderAsync() { List <TrackSampleProvider> trackSampleProviders = project.Tracks.Select( track => new TrackSampleProvider() { Volume = PlaybackManager.DecibelToVolume(track.Volume) }).ToList(); var cacheDir = PathManager.Inst.GetCachePath(project.FilePath); foreach (UPart part in project.Parts) { UVoicePart voicePart = part as UVoicePart; if (voicePart != null) { SequencingSampleProvider sampleProvider = await RenderPartAsync(voicePart, project, cacheDir); if (sampleProvider != null) { trackSampleProviders[voicePart.TrackNo].AddSource( sampleProvider, TimeSpan.FromMilliseconds(project.TickToMillisecond(voicePart.PosTick))); } } UWavePart wavePart = part as UWavePart; if (wavePart != null) { try { var stream = new AudioFileReader(wavePart.FilePath); trackSampleProviders[wavePart.TrackNo].AddSource( new WaveToSampleProvider(stream), TimeSpan.FromMilliseconds(project.TickToMillisecond(wavePart.PosTick))); } catch (Exception e) { Log.Error(e, "Failed to open audio file"); } } } return(trackSampleProviders); }
private void BuildVoicePartDone(SequencingSampleProvider source, UPart part, UProject project) { lock (lockObject) { trackSources[part.TrackNo].AddSource( source, TimeSpan.FromMilliseconds(project.TickToMillisecond(part.PosTick))); pendingParts--; } if (pendingParts == 0) { StartPlayback(); } }
private void BuildAudio(UProject project) { trackSources = new List <TrackSampleProvider>(); foreach (UTrack track in project.Tracks) { trackSources.Add(new TrackSampleProvider() { Volume = DecibelToVolume(track.Volume), Pan = track.Pan }); } pendingParts = project.Parts.Count; foreach (UPart part in project.Parts) { if (part is UWavePart) { lock (lockObject) { trackSources[part.TrackNo].AddSource( BuildWavePartAudio(part as UWavePart, project), TimeSpan.FromMilliseconds(project.TickToMillisecond(part.PosTick)) ); pendingParts--; } } else { var singer = project.Tracks[part.TrackNo].Singer; if (singer != null && singer.Loaded) { FileInfo ResamplerFile = new FileInfo(PathManager.Inst.GetPreviewEnginePath()); IResamplerDriver engine = ResamplerDriver.ResamplerDriver.LoadEngine(ResamplerFile.FullName); BuildVoicePartAudio(part as UVoicePart, project, engine); } else { lock (lockObject) { pendingParts--; } } } } if (pendingParts == 0) { StartPlayback(); } }
static public UProject Load(string file) { XmlDocument vsqx = new XmlDocument(); try { vsqx.Load(file); } catch (Exception e) { System.Windows.MessageBox.Show(e.GetType().ToString() + "\n" + e.Message); return(null); } XmlNamespaceManager nsmanager = new XmlNamespaceManager(vsqx.NameTable); nsmanager.AddNamespace("v3", vsq3NameSpace); nsmanager.AddNamespace("v4", vsq4NameSpace); XmlNode root; string nsPrefix; // Detect vsqx version root = vsqx.SelectSingleNode("v3:vsq3", nsmanager); if (root != null) { nsPrefix = "v3:"; } else { root = vsqx.SelectSingleNode("v4:vsq4", nsmanager); if (root != null) { nsPrefix = "v4:"; } else { System.Windows.MessageBox.Show("Unrecognizable VSQx file format."); return(null); } } UProject uproject = new UProject(); uproject.RegisterExpression(new IntExpression(null, "velocity", "VEL") { Data = 64, Min = 0, Max = 127 }); uproject.RegisterExpression(new IntExpression(null, "volume", "VOL") { Data = 100, Min = 0, Max = 200 }); uproject.RegisterExpression(new IntExpression(null, "opening", "OPE") { Data = 127, Min = 0, Max = 127 }); uproject.RegisterExpression(new IntExpression(null, "accent", "ACC") { Data = 50, Min = 0, Max = 100 }); uproject.RegisterExpression(new IntExpression(null, "decay", "DEC") { Data = 50, Min = 0, Max = 100 }); string bpmPath = string.Format("{0}masterTrack/{0}tempo/{0}{1}", nsPrefix, nsPrefix == "v3:" ? "bpm" : "v"); string beatperbarPath = string.Format("{0}masterTrack/{0}timeSig/{0}{1}", nsPrefix, nsPrefix == "v3:" ? "nume" : "nu"); string beatunitPath = string.Format("{0}masterTrack/{0}timeSig/{0}{1}", nsPrefix, nsPrefix == "v3:" ? "denomi" : "de"); string premeasurePath = string.Format("{0}masterTrack/{0}preMeasure", nsPrefix); string resolutionPath = string.Format("{0}masterTrack/{0}resolution", nsPrefix); string projectnamePath = string.Format("{0}masterTrack/{0}seqName", nsPrefix); string projectcommentPath = string.Format("{0}masterTrack/{0}comment", nsPrefix); string trackPath = string.Format("{0}vsTrack", nsPrefix); string tracknamePath = string.Format("{0}{1}", nsPrefix, nsPrefix == "v3:" ? "trackName" : "name"); string trackcommentPath = string.Format("{0}comment", nsPrefix); string tracknoPath = string.Format("{0}{1}", nsPrefix, nsPrefix == "v3:" ? "vsTrackNo" : "tNo"); string partPath = string.Format("{0}{1}", nsPrefix, nsPrefix == "v3:" ? "musicalPart" : "vsPart"); string partnamePath = string.Format("{0}{1}", nsPrefix, nsPrefix == "v3:" ? "partName" : "name"); string partcommentPath = string.Format("{0}comment", nsPrefix); string notePath = string.Format("{0}note", nsPrefix); string postickPath = string.Format("{0}{1}", nsPrefix, nsPrefix == "v3:" ? "posTick" : "t"); string durtickPath = string.Format("{0}{1}", nsPrefix, nsPrefix == "v3:" ? "durTick" : "dur"); string notenumPath = string.Format("{0}{1}", nsPrefix, nsPrefix == "v3:" ? "noteNum" : "n"); string velocityPath = string.Format("{0}{1}", nsPrefix, nsPrefix == "v3:" ? "velocity" : "v"); string lyricPath = string.Format("{0}{1}", nsPrefix, nsPrefix == "v3:" ? "lyric" : "y"); string phonemePath = string.Format("{0}{1}", nsPrefix, nsPrefix == "v3:" ? "phnms" : "p"); string playtimePath = string.Format("{0}playTime", nsPrefix); string partstyleattrPath = string.Format("{0}{1}/{0}{2}", nsPrefix, nsPrefix == "v3:" ? "partStyle" : "pStyle", nsPrefix == "v3:" ? "attr" : "v"); string notestyleattrPath = string.Format("{0}{1}/{0}{2}", nsPrefix, nsPrefix == "v3:" ? "noteStyle" : "nStyle", nsPrefix == "v3:" ? "attr" : "v"); uproject.BPM = Convert.ToDouble(root.SelectSingleNode(bpmPath, nsmanager).InnerText) / 100; uproject.BeatPerBar = int.Parse(root.SelectSingleNode(beatperbarPath, nsmanager).InnerText); uproject.BeatUnit = int.Parse(root.SelectSingleNode(beatunitPath, nsmanager).InnerText); uproject.Resolution = int.Parse(root.SelectSingleNode(resolutionPath, nsmanager).InnerText); uproject.FilePath = file; uproject.Name = root.SelectSingleNode(projectnamePath, nsmanager).InnerText; uproject.Comment = root.SelectSingleNode(projectcommentPath, nsmanager).InnerText; int preMeasure = int.Parse(root.SelectSingleNode(premeasurePath, nsmanager).InnerText); int partPosTickShift = -preMeasure * uproject.Resolution * uproject.BeatPerBar * 4 / uproject.BeatUnit; USinger usinger = new USinger(); uproject.Singers.Add(usinger); foreach (XmlNode track in root.SelectNodes(trackPath, nsmanager)) // track { UTrack utrack = new UTrack() { Singer = usinger, TrackNo = uproject.Tracks.Count }; uproject.Tracks.Add(utrack); utrack.Name = track.SelectSingleNode(tracknamePath, nsmanager).InnerText; utrack.Comment = track.SelectSingleNode(trackcommentPath, nsmanager).InnerText; utrack.TrackNo = int.Parse(track.SelectSingleNode(tracknoPath, nsmanager).InnerText); foreach (XmlNode part in track.SelectNodes(partPath, nsmanager)) // musical part { UVoicePart upart = new UVoicePart(); uproject.Parts.Add(upart); upart.Name = part.SelectSingleNode(partnamePath, nsmanager).InnerText; upart.Comment = part.SelectSingleNode(partcommentPath, nsmanager).InnerText; upart.PosTick = int.Parse(part.SelectSingleNode(postickPath, nsmanager).InnerText) + partPosTickShift; upart.DurTick = int.Parse(part.SelectSingleNode(playtimePath, nsmanager).InnerText); upart.TrackNo = utrack.TrackNo; foreach (XmlNode note in part.SelectNodes(notePath, nsmanager)) { UNote unote = uproject.CreateNote(); unote.PosTick = int.Parse(note.SelectSingleNode(postickPath, nsmanager).InnerText); unote.DurTick = int.Parse(note.SelectSingleNode(durtickPath, nsmanager).InnerText); unote.NoteNum = int.Parse(note.SelectSingleNode(notenumPath, nsmanager).InnerText); unote.Lyric = note.SelectSingleNode(lyricPath, nsmanager).InnerText; unote.Phonemes[0].Phoneme = note.SelectSingleNode(phonemePath, nsmanager).InnerText; unote.Expressions["velocity"].Data = int.Parse(note.SelectSingleNode(velocityPath, nsmanager).InnerText); foreach (XmlNode notestyle in note.SelectNodes(notestyleattrPath, nsmanager)) { if (notestyle.Attributes["id"].Value == "opening") { unote.Expressions["opening"].Data = int.Parse(notestyle.InnerText); } else if (notestyle.Attributes["id"].Value == "accent") { unote.Expressions["accent"].Data = int.Parse(notestyle.InnerText); } else if (notestyle.Attributes["id"].Value == "decay") { unote.Expressions["decay"].Data = int.Parse(notestyle.InnerText); } } unote.PitchBend.Points[0].X = -uproject.TickToMillisecond(Math.Min(15, unote.DurTick / 3)); unote.PitchBend.Points[1].X = -unote.PitchBend.Points[0].X; upart.Notes.Add(unote); } } } return(uproject); }
public static UProject Load(string file) { XmlDocument vsqx = new XmlDocument(); vsqx.Load(file); XmlNamespaceManager nsmanager = new XmlNamespaceManager(vsqx.NameTable); nsmanager.AddNamespace("v3", vsq3NameSpace); nsmanager.AddNamespace("v4", vsq4NameSpace); XmlNode root; string nsPrefix; // Detect vsqx version if ((root = vsqx.SelectSingleNode("v3:vsq3", nsmanager)) != null) { nsPrefix = "v3:"; } else if ((root = vsqx.SelectSingleNode("v4:vsq4", nsmanager)) != null) { nsPrefix = "v4:"; } else { throw new FileFormatException("Unrecognizable VSQx file format."); } UProject uproject = new UProject(); Ustx.AddDefaultExpressions(uproject); uproject.RegisterExpression(new UExpressionDescriptor("opening", "ope", 0, 100, 100)); string bpmPath = $"{nsPrefix}masterTrack/{nsPrefix}tempo/{nsPrefix}{(nsPrefix == "v3:" ? "bpm" : "v")}"; string beatperbarPath = $"{nsPrefix}masterTrack/{nsPrefix}timeSig/{nsPrefix}{(nsPrefix == "v3:" ? "nume" : "nu")}"; string beatunitPath = $"{nsPrefix}masterTrack/{nsPrefix}timeSig/{nsPrefix}{(nsPrefix == "v3:" ? "denomi" : "de")}"; string premeasurePath = $"{nsPrefix}masterTrack/{nsPrefix}preMeasure"; string resolutionPath = $"{nsPrefix}masterTrack/{nsPrefix}resolution"; string projectnamePath = $"{nsPrefix}masterTrack/{nsPrefix}seqName"; string projectcommentPath = $"{nsPrefix}masterTrack/{nsPrefix}comment"; string trackPath = $"{nsPrefix}vsTrack"; string tracknamePath = $"{nsPrefix}{(nsPrefix == "v3:" ? "trackName" : "name")}"; string trackcommentPath = $"{nsPrefix}comment"; string tracknoPath = $"{nsPrefix}{(nsPrefix == "v3:" ? "vsTrackNo" : "tNo")}"; string partPath = $"{nsPrefix}{(nsPrefix == "v3:" ? "musicalPart" : "vsPart")}"; string partnamePath = $"{nsPrefix}{(nsPrefix == "v3:" ? "partName" : "name")}"; string partcommentPath = $"{nsPrefix}comment"; string notePath = $"{nsPrefix}note"; string postickPath = $"{nsPrefix}{(nsPrefix == "v3:" ? "posTick" : "t")}"; string durtickPath = $"{nsPrefix}{(nsPrefix == "v3:" ? "durTick" : "dur")}"; string notenumPath = $"{nsPrefix}{(nsPrefix == "v3:" ? "noteNum" : "n")}"; string velocityPath = $"{nsPrefix}{(nsPrefix == "v3:" ? "velocity" : "v")}"; string lyricPath = $"{nsPrefix}{(nsPrefix == "v3:" ? "lyric" : "y")}"; string phonemePath = $"{nsPrefix}{(nsPrefix == "v3:" ? "phnms" : "p")}"; string playtimePath = $"{nsPrefix}playTime"; string partstyleattrPath = $"{nsPrefix}{(nsPrefix == "v3:" ? "partStyle" : "pStyle")}/{nsPrefix}{(nsPrefix == "v3:" ? "attr" : "v")}"; string notestyleattrPath = $"{nsPrefix}{(nsPrefix == "v3:" ? "noteStyle" : "nStyle")}/{nsPrefix}{(nsPrefix == "v3:" ? "attr" : "v")}"; uproject.bpm = Convert.ToDouble(root.SelectSingleNode(bpmPath, nsmanager).InnerText) / 100; uproject.beatPerBar = int.Parse(root.SelectSingleNode(beatperbarPath, nsmanager).InnerText); uproject.beatUnit = int.Parse(root.SelectSingleNode(beatunitPath, nsmanager).InnerText); uproject.resolution = int.Parse(root.SelectSingleNode(resolutionPath, nsmanager).InnerText); uproject.FilePath = file; uproject.name = root.SelectSingleNode(projectnamePath, nsmanager).InnerText; uproject.comment = root.SelectSingleNode(projectcommentPath, nsmanager).InnerText; int preMeasure = int.Parse(root.SelectSingleNode(premeasurePath, nsmanager).InnerText); int partPosTickShift = -preMeasure * uproject.resolution * uproject.beatPerBar * 4 / uproject.beatUnit; USinger usinger = new USinger(""); foreach (XmlNode track in root.SelectNodes(trackPath, nsmanager)) // track { UTrack utrack = new UTrack() { Singer = usinger, TrackNo = uproject.tracks.Count }; uproject.tracks.Add(utrack); //utrack.Name = track.SelectSingleNode(tracknamePath, nsmanager).InnerText; //utrack.Comment = track.SelectSingleNode(trackcommentPath, nsmanager).InnerText; utrack.TrackNo = int.Parse(track.SelectSingleNode(tracknoPath, nsmanager).InnerText); foreach (XmlNode part in track.SelectNodes(partPath, nsmanager)) // musical part { UVoicePart upart = new UVoicePart(); uproject.parts.Add(upart); upart.name = part.SelectSingleNode(partnamePath, nsmanager).InnerText; upart.comment = part.SelectSingleNode(partcommentPath, nsmanager).InnerText; upart.position = int.Parse(part.SelectSingleNode(postickPath, nsmanager).InnerText) + partPosTickShift; upart.Duration = int.Parse(part.SelectSingleNode(playtimePath, nsmanager).InnerText); upart.trackNo = utrack.TrackNo; foreach (XmlNode note in part.SelectNodes(notePath, nsmanager)) { UNote unote = uproject.CreateNote(); unote.position = int.Parse(note.SelectSingleNode(postickPath, nsmanager).InnerText); unote.duration = int.Parse(note.SelectSingleNode(durtickPath, nsmanager).InnerText); unote.tone = int.Parse(note.SelectSingleNode(notenumPath, nsmanager).InnerText); unote.lyric = note.SelectSingleNode(lyricPath, nsmanager).InnerText; if (unote.lyric == "-") { unote.lyric = "..."; } unote.phonemeExpressions.Add(new UExpression("vel") { index = 0, value = int.Parse(note.SelectSingleNode(velocityPath, nsmanager).InnerText) * 100 / 64, }); foreach (XmlNode notestyle in note.SelectNodes(notestyleattrPath, nsmanager)) { if (notestyle.Attributes["id"].Value == "opening") { unote.phonemeExpressions.Add(new UExpression("ope") { index = 0, value = int.Parse(notestyle.InnerText) * 100 / 127, }); } else if (notestyle.Attributes["id"].Value == "accent") { unote.phonemeExpressions.Add(new UExpression("atk") { index = 0, value = int.Parse(notestyle.InnerText) * 2, }); } else if (notestyle.Attributes["id"].Value == "decay") { unote.phonemeExpressions.Add(new UExpression("dec") { index = 0, // V4 default is 50. Translate it to no effect in OU. V4 dec 100 roughly maps to OU 50. value = Math.Max(0, int.Parse(notestyle.InnerText) - 50), }); } } unote.pitch.data[0].X = -(float)uproject.TickToMillisecond(Math.Min(15, unote.duration / 3)); unote.pitch.data[1].X = -unote.pitch.data[0].X; upart.notes.Add(unote); } } } uproject.AfterLoad(); uproject.Validate(); return(uproject); }
private List <int> BuildPitchData(UPhoneme phoneme, UVoicePart part, UProject project) { int leftBound = phoneme.Parent.position + phoneme.position - project.MillisecondToTick(phoneme.preutter); int rightBound = phoneme.Parent.position + phoneme.position + phoneme.Duration - project.MillisecondToTick(phoneme.tailIntrude - phoneme.tailOverlap); var leftNote = phoneme.Parent; var rightNote = phoneme.Parent; bool oneMore = true; while ((leftBound < leftNote.RightBound || oneMore) && leftNote.Prev != null && leftNote.Prev.End == leftNote.position) { leftNote = leftNote.Prev; if (leftBound >= leftNote.RightBound) { oneMore = false; } } oneMore = true; while ((rightBound > rightNote.LeftBound || oneMore) && rightNote.Next != null && rightNote.Next.position == rightNote.End) { rightNote = rightNote.Next; if (rightBound <= rightNote.LeftBound) { oneMore = false; } } // Collect pitch curve and vibratos. var points = new List <PitchPoint>(); var vibratos = new List <Tuple <double, double, UVibrato> >(); var note = leftNote; float vel = Velocity; var strechRatio = Math.Pow(2, 1.0 - vel / 100); float correction = (float)(phoneme.oto.Preutter * (strechRatio - 1)); while (true) { var offsetMs = (float)project.TickToMillisecond(note.position - phoneme.Parent.position); foreach (var point in note.pitch.data) { var newpp = point.Clone(); newpp.X += offsetMs + correction; newpp.Y -= (phoneme.Parent.tone - note.tone) * 10; points.Add(newpp); } if (note.vibrato.length != 0) { double vibratoStartMs = project.TickToMillisecond(note.position + note.duration * (1 - note.vibrato.length / 100) - phoneme.Parent.position); double vibratoEndMs = project.TickToMillisecond(note.End - phoneme.Parent.position); vibratos.Add(Tuple.Create(vibratoStartMs, vibratoEndMs, note.vibrato)); } if (note == rightNote) { break; } note = note.Next; } // Expand curve if necessary. float startMs = (float)(project.TickToMillisecond(phoneme.position) - phoneme.oto.Preutter); float endMs = (float)(project.TickToMillisecond(phoneme.End) - phoneme.tailIntrude + phoneme.tailOverlap); if (points.First().X > startMs) { points.Insert(0, new PitchPoint(startMs, points.First().Y)); } if (points.Last().X < endMs) { points.Add(new PitchPoint(endMs, points.Last().Y)); } // Interpolation. var pitches = new List <int>(); const int intervalTick = 5; float intervalMs = (float)project.TickToMillisecond(intervalTick); float currMs = startMs; int i = 0; int vibrato = 0; while (currMs < endMs) { while (points[i + 1].X < currMs) { i++; } var pit = MusicMath.InterpolateShape(points[i].X, points[i + 1].X, points[i].Y, points[i + 1].Y, currMs, points[i].shape) * 10; while (vibrato < vibratos.Count - 1 && vibratos[vibrato].Item2 < currMs) { vibrato++; } if (vibrato < vibratos.Count && vibratos[vibrato].Item1 <= currMs && currMs < vibratos[vibrato].Item2) { pit += InterpolateVibrato(vibratos[vibrato].Item3, currMs - vibratos[vibrato].Item1, vibratos[vibrato].Item2 - vibratos[vibrato].Item1, project); } pitches.Add((int)pit); currMs += intervalMs; } return(pitches); }