public void CleanUpManifestFile() { // read file string mp3ManifestFile = GetMp3ListFilePath(); List <string> lines = File.ReadAllText(mp3ManifestFile).Split(new[] { Environment.NewLine }, StringSplitOptions.None).ToList(); string pattern = @"(.+) \| (.+)"; string parseMp3(string line) => MatchGroup(line, pattern, 1); // filter out lines whose mp3s no longer exist List <string> keepLines = new List <string>(); foreach (string line in lines) { var relMp3 = parseMp3(line); var absMp3 = JunUtils.FullPathFromSongsFolder(relMp3); if (File.Exists(absMp3)) { keepLines.Add(line); } } // write to file File.WriteAllText(mp3ManifestFile, String.Join(Environment.NewLine, keepLines)); }
private void formAnimationTimer_Tick(object sender, EventArgs e) { Color startBackColor = Color.FromArgb(71, 115, 66); Color endBackColor = Color.FromArgb(45, 42, 63); var saveButtons = new List <Button>() { saveButton1, saveButton2, saveButton3, saveButton4 }; for (int i = 0; i < saveButtons.Count; i++) { // linearly interpolate colour // saveButtonHighlight 1.0 -> 0.0 if (saveButtonHighlight[i] > 0) { saveButtons[i].FlatAppearance.MouseOverBackColor = JunUtils.LerpColor(startBackColor, endBackColor, 1 - saveButtonHighlight[i]); saveButtons[i].ForeColor = JunUtils.LerpColor(Color.White, Color.FromArgb(90, 90, 134), 1 - saveButtonHighlight[i]); } // decay saveButtonHighlight[i] -= HIGHLIGHT_FADE; if (saveButtonHighlight[i] <= 0) { saveButtonHighlight[i] = 0M; saveButtons[i].ForeColor = Color.FromArgb(90, 90, 134); saveButtons[i].Text = "Save"; saveButtons[i].FlatAppearance.MouseOverBackColor = Color.Empty; } } }
// AR modifications: HR, DT, DTHR, BpmMultiplier public static decimal CalculateMultipliedAR(Beatmap map, decimal BpmMultiplier) { decimal newbpmMs = ApproachRateToMs(map.ApproachRate) / BpmMultiplier; decimal newbpmAR = MsToApproachRate(newbpmMs); return(JunUtils.Clamp(newbpmAR, 0, 11)); }
public async void GenerateBeatmap() { if (State != EditorState.READY) { return; } // pre SetState(EditorState.GENERATING_BEATMAP); // main phase Beatmap exportBeatmap = NewBeatmap.Copy(); ModifyBeatmapMetadata(exportBeatmap, BpmMultiplier, ChangePitch); if (!File.Exists(JunUtils.GetBeatmapDirectoryName(OriginalBeatmap) + "\\" + exportBeatmap.AudioFilename)) { string inFile = $"{Path.GetDirectoryName(OriginalBeatmap.Filename)}\\{OriginalBeatmap.AudioFilename}"; string outFile = $"{Path.GetDirectoryName(exportBeatmap.Filename)}\\{exportBeatmap.AudioFilename}"; await Task.Run(() => SongSpeedChanger.GenerateAudioFile(inFile, outFile, BpmMultiplier, ChangePitch)); // take note of this mp3 in a text file, so we can clean it up later string mp3ManifestFile = Properties.Settings.Default.SongsFolder + "\\modified_mp3_list.txt"; using (var writer = File.AppendText(mp3ManifestFile)) { string beatmapFolder = Path.GetDirectoryName(exportBeatmap.Filename).Replace(Properties.Settings.Default.SongsFolder + "\\", ""); string mp3RelativePath = beatmapFolder + "\\" + exportBeatmap.AudioFilename; writer.WriteLine(mp3RelativePath + " | " + exportBeatmap.Filename); } } exportBeatmap.Save(); // post form.PlayDoneSound(); SetState(EditorState.READY); }
public void GenerateBeatmap() { if (State != EditorState.READY) { return; } SetState(EditorState.GENERATING_BEATMAP); bool compensateForDT = (NewBeatmap.ApproachRate > 10 || NewBeatmap.OverallDifficulty > 10); // Set metadata Beatmap exportBeatmap = new Beatmap(NewBeatmap); ModifyBeatmapMetadata(exportBeatmap, BpmMultiplier, ChangePitch, compensateForDT); if (NoSpinners) { exportBeatmap.RemoveSpinners(); } // Slow down map by 1.5x if (compensateForDT) { exportBeatmap.ApproachRate = DifficultyCalculator.CalculateMultipliedAR(NewBeatmap, 1 / 1.5M); exportBeatmap.OverallDifficulty = DifficultyCalculator.CalculateMultipliedOD(NewBeatmap, 1 / 1.5M); decimal compensatedRate = (NewBeatmap.Bpm / OriginalBeatmap.Bpm) / 1.5M; exportBeatmap.SetRate(compensatedRate); } // Generate new mp3 var audioFilePath = Path.Combine(JunUtils.GetBeatmapDirectoryName(OriginalBeatmap), exportBeatmap.AudioFilename); var newMp3 = ""; if (!File.Exists(audioFilePath)) { string inFile = Path.Combine(Path.GetDirectoryName(OriginalBeatmap.Filename), OriginalBeatmap.AudioFilename); string outFile = Path.Combine(Path.GetTempPath(), exportBeatmap.AudioFilename); SongSpeedChanger.GenerateAudioFile(inFile, outFile, BpmMultiplier, mainform.BackgroundWorker, ChangePitch, compensateForDT); newMp3 = outFile; // take note of this mp3 in a text file, so we can clean it up later string mp3ManifestFile = GetMp3ListFilePath(); List <string> manifest = File.ReadAllLines(mp3ManifestFile).ToList(); string beatmapFolder = Path.GetDirectoryName(exportBeatmap.Filename).Replace(Properties.Settings.Default.SongsFolder + "\\", ""); string mp3RelativePath = Path.Combine(beatmapFolder, exportBeatmap.AudioFilename); manifest.Add(mp3RelativePath + " | " + exportBeatmap.Filename); File.WriteAllLines(mp3ManifestFile, manifest); } // save file to temp location (do not directly put into any song folder) exportBeatmap.Filename = Path.Combine(Path.GetTempPath(), Path.GetFileName(exportBeatmap.Filename)); exportBeatmap.Save(); // create and execute osz AddNewBeatmapToSongFolder(Path.GetDirectoryName(OriginalBeatmap.Filename), exportBeatmap.Filename, newMp3); // post SetState(EditorState.READY); }
public void SetBpm(int bpm) { float originalBpm = GetOriginalBpmData().Item1; float newMultiplier = bpm / originalBpm; newMultiplier = JunUtils.Clamp(newMultiplier, 0.1f, 5.0f); SetBpmMultiplier(newMultiplier); }
public static float CalculateMultipliedOD(Beatmap map, float BpmMultiplier) { float newbpmMs = OverallDifficultyToMs(map.OverallDifficulty) / BpmMultiplier; float newbpmOD = MsToOverallDifficulty(newbpmMs); newbpmOD = (float)Math.Round(newbpmOD * 10.0f) / 10.0f; newbpmOD = JunUtils.Clamp(newbpmOD, 0, 10); return(newbpmOD); }
public static decimal CalculateMultipliedOD(Beatmap map, decimal BpmMultiplier) { decimal newbpmMs = OverallDifficultyToMs(map.OverallDifficulty) / BpmMultiplier; decimal newbpmOD = MsToOverallDifficulty(newbpmMs); newbpmOD = (decimal)Math.Round(newbpmOD * 10.0M) / 10.0M; newbpmOD = JunUtils.Clamp(newbpmOD, 0, 11); return(newbpmOD); }
// return the new beatmap object if success // return null on failure private Beatmap LoadBeatmap(string beatmapPath) { // test if the beatmap is valid before committing to using it Beatmap retMap; try { retMap = new Beatmap(beatmapPath); } catch (FormatException e) { Console.WriteLine("Bad .osu file format"); OriginalBeatmap = null; NewBeatmap = null; return(null); } // Check if beatmap was loaded successfully if (retMap.Filename == null && retMap.Title == null) { Console.WriteLine("Bad .osu file format"); return(null); } // Check if this map was generated by osu-trainer if (retMap.Tags.Contains("osutrainer")) { string[] diffFiles = Directory.GetFiles(Path.GetDirectoryName(retMap.Filename), "*.osu"); int candidateSimilarity = int.MaxValue; Beatmap candidate = null; foreach (string diff in diffFiles) { Beatmap map = new Beatmap(diff); if (map.Tags.Contains("osutrainer")) { continue; } // lower value => more similar int similarity = JunUtils.LevenshteinDistance(retMap.Version, map.Version); if (similarity < candidateSimilarity) { candidate = map; candidateSimilarity = similarity; } } // just assume this shit is the original beatmap if (candidate != null) { retMap = candidate; } } return(retMap); }
public DeleteMp3sForm(List <string> filesToDelete) { InitializeComponent(); List <string> uniqueFilesToDelete = filesToDelete.Distinct().ToList(); // filter out files that don't exist Dictionary <string, string> pathMapping = new Dictionary <string, string>(); uniqueFilesToDelete.ForEach(file => pathMapping.Add(file, JunUtils.FullPathFromSongsFolder(file))); List <string> relativeFileList = uniqueFilesToDelete.Where(file => File.Exists(JunUtils.FullPathFromSongsFolder(file))).ToList(); // populate listview string formatFileSize(long len) { string[] sizes = { "B", "KB", "MB", "GB", "TB" }; int order = 0; decimal value = (decimal)len; while (value >= 1024 && order < sizes.Length - 1) { order++; value = value / 1024; } return(String.Format("{0:0.##} {1}", value, sizes[order])); } ListViewItem fileToListViewItem(string file) { string[] subitems = new string[3]; FileInfo fi = new FileInfo(JunUtils.FullPathFromSongsFolder(file)); subitems[0] = file; // path relative to songs folder subitems[1] = fi.CreationTime.ToString("d"); // date created subitems[2] = formatFileSize(fi.Length); // size return(new ListViewItem(subitems)); } fileListView.Items.Clear(); relativeFileList .Select(file => fileToListViewItem(file)) .ToList() .ForEach(item => fileListView.Items.Add(item)); // update total filesize label long totalSize = relativeFileList .Select(file => new FileInfo(JunUtils.FullPathFromSongsFolder(file)).Length) .Sum(); fileSizeLabel.Text = $"Total: {formatFileSize(totalSize)} to be deleted"; confirmButton.Focus(); }
public static Color LerpColor(Color StartColor, Color EndColor, decimal Progress) { var startSat = StartColor.GetSaturation(); var startHue = StartColor.GetHue(); var startBri = StartColor.GetBrightness(); var endSat = EndColor.GetSaturation(); var endHue = EndColor.GetHue(); var endBri = EndColor.GetBrightness(); var currentSat = startSat + (float)Progress * (endSat - startSat); var currentHue = startHue + (float)Progress * (endHue - startHue); var currentBri = startBri + (float)Progress * (endBri - startBri); return(JunUtils.ColorFromHSV(currentHue, currentSat, currentBri)); }
public List <string> GetUnusedMp3s() { // read manifest file List <string> lines = new List <string>(); string mp3ManifestFile = GetMp3ListFilePath(); if (!File.Exists(mp3ManifestFile)) { return(new List <string>()); } using (var reader = File.OpenText(mp3ManifestFile)) { string line = ""; while ((line = reader.ReadLine()) != null) { lines.Add(line); } } // convert that shit into a dictionary var mp3Dict = new Dictionary <string, List <string> >(); string pattern = @"(.+) \| (.+)"; string parseMp3(string line) => MatchGroup(line, pattern, 1); string parseOsu(string line) => MatchGroup(line, pattern, 2); // create dictionary keys lines .Select(line => parseMp3(line)).ToList() .Distinct() .ToList() .ForEach(mp3 => mp3Dict.Add(mp3, new List <string>())); // populate dictionary values foreach ((string mp3, string osu) in lines.Select(line => (parseMp3(line), parseOsu(line)))) { mp3Dict[mp3].Add(osu); } // find all keys where none of the associated beatmaps exist, but the mp3 still exists bool noFilesExist(bool acc, string file) => acc && !File.Exists(file); return(lines .Select(line => parseMp3(line)) .Where(mp3 => mp3Dict[mp3].Aggregate(true, noFilesExist)) .Where(mp3 => File.Exists(JunUtils.FullPathFromSongsFolder(mp3))) .ToList()); }
private void DeleteButton_Click(object sender, EventArgs e) { var mp3List = editor.GetUnusedMp3s(); if (new DeleteMp3sForm(mp3List).ShowDialog() == DialogResult.OK) { mp3List .Select(relativeMp3 => JunUtils.FullPathFromSongsFolder(relativeMp3)) .Select(absMp3 => new FileInfo(absMp3)) .ToList() .ForEach(file => file.Delete()); if (mp3List.Count > 0) { MessageBox.Show($"Deleted {mp3List.Count} file(s).", "Success"); } editor.CleanUpManifestFile(); } }
private void DeleteButton_Click(object sender, EventArgs e) { if (gameLoaded == true) { MessageBox.Show("Please close osu! first then try again.", "osu! is running"); return; } var mp3List = editor.GetUnusedMp3s(); if (new DeleteMp3sForm(mp3List).ShowDialog() == DialogResult.OK) { mp3List .Select(relativeMp3 => JunUtils.FullPathFromSongsFolder(relativeMp3)) .Select(absMp3 => new FileInfo(absMp3)) .ToList() .ForEach(file => file.Delete()); if (mp3List.Count > 0) { MessageBox.Show($"Deleted {mp3List.Count} file(s).", "Success"); } editor.CleanUpManifestFile(); } }
public static void GenerateAudioFile(string inFile, string outFile, decimal multiplier, BackgroundWorker worker, bool changePitch = false, bool preDT = false) { decimal compensatedMultiplier = multiplier / 1.5M; string temp1 = JunUtils.GetTempFilename("mp3"); // audio copy string temp2 = JunUtils.GetTempFilename("wav"); // decoded wav string temp3 = JunUtils.GetTempFilename("wav"); // stretched file string temp4 = JunUtils.GetTempFilename("mp3"); // encoded mp3 // TODO: try catch File.Copy(inFile, temp1); worker.ReportProgress(17); // mp3 => wav Process lame1 = new Process(); lame1.StartInfo.FileName = Path.Combine("Speed Changer Stuff", "lame.exe"); lame1.StartInfo.Arguments = $"-q 9 --priority 4 --decode \"{temp1}\" \"{temp2}\""; lame1.StartInfo.UseShellExecute = false; lame1.StartInfo.CreateNoWindow = true; lame1.Start(); lame1.WaitForExit(); worker.ReportProgress(33); // stretch (or speed up) wav decimal selectedMultiplier = (preDT ? compensatedMultiplier : multiplier); decimal tempo = (selectedMultiplier - 1) * 100; decimal cents = (decimal)(1200.0 * Math.Log((double)multiplier) / Math.Log(2)); decimal semitones = cents / 100.0M; Process soundstretch = new Process(); soundstretch.StartInfo.FileName = Path.Combine("Speed Changer Stuff", "soundstretch.exe"); if (changePitch) { soundstretch.StartInfo.Arguments = $"\"{temp2}\" \"{temp3}\" -quick -naa -tempo={tempo} -pitch={semitones}"; } else { soundstretch.StartInfo.Arguments = $"\"{temp2}\" \"{temp3}\" -quick -naa -tempo={tempo}"; } Console.WriteLine(soundstretch.StartInfo.Arguments); soundstretch.StartInfo.UseShellExecute = false; soundstretch.StartInfo.CreateNoWindow = true; soundstretch.Start(); soundstretch.WaitForExit(); worker.ReportProgress(50); // wav => mp3 Process lame2 = new Process(); lame2.StartInfo.FileName = Path.Combine("Speed Changer Stuff", "lame.exe"); lame2.StartInfo.Arguments = $"-q 9 --priority 4 \"{temp3}\" \"{temp4}\""; lame2.StartInfo.UseShellExecute = false; lame2.StartInfo.CreateNoWindow = true; lame2.Start(); lame2.WaitForExit(); worker.ReportProgress(67); if (File.Exists(outFile)) { File.Delete(outFile); } File.Copy(temp4, outFile); worker.ReportProgress(83); // Clean up File.Delete(temp1); File.Delete(temp2); File.Delete(temp3); File.Delete(temp4); worker.ReportProgress(100); }
// OUT: beatmap.Version // OUT: beatmap.Filename // OUT: beatmap.AudioFilename (if multiplier is not 1x) // OUT: beatmap.Tags private void ModifyBeatmapMetadata(Beatmap map, float multiplier, bool changePitch = false) { if (multiplier == 1) { string ARODCS = ""; if (NewBeatmap.ApproachRate != OriginalBeatmap.ApproachRate) { ARODCS += $" AR{NewBeatmap.ApproachRate}"; } if (NewBeatmap.OverallDifficulty != OriginalBeatmap.OverallDifficulty) { ARODCS += $" OD{NewBeatmap.OverallDifficulty}"; } if (NewBeatmap.CircleSize != OriginalBeatmap.CircleSize) { ARODCS += $" CS{NewBeatmap.CircleSize}"; } map.Version += ARODCS; } else { // If song has changed, no ARODCS in diff name var bpmsUnique = GetBpmList(map).Distinct().ToList(); if (bpmsUnique.Count >= 2) { map.Version += $" x{multiplier}"; } else { map.Version += $" {(bpmsUnique[0]).ToString("0")}bpm"; } map.AudioFilename = map.AudioFilename.Substring(0, map.AudioFilename.LastIndexOf(".", StringComparison.InvariantCulture)) + " " + GetBpmData(map).Item1 + "bpm.mp3"; if (changePitch) { map.AudioFilename = $"{Path.GetFileNameWithoutExtension(map.AudioFilename)} {multiplier:0.000}x.mp3"; } if (changePitch) { map.AudioFilename = $"{Path.GetFileNameWithoutExtension(map.AudioFilename)} {multiplier:0.000}x (pitch {(multiplier < 1 ? "lowered" : "raised")}).mp3"; } } map.Filename = map.Filename.Substring(0, map.Filename.LastIndexOf("\\", StringComparison.InvariantCulture) + 1) + JunUtils.NormalizeText(map.Artist) + " - " + JunUtils.NormalizeText(map.Title) + " (" + JunUtils.NormalizeText(map.Creator) + ")" + " [" + JunUtils.NormalizeText(map.Version) + "].osu"; // make this map searchable in the in-game menus map.Tags.Add("osutrainer"); }
private async void ServiceBeatmapChangeRequest() { // acquire mutually exclusive entry into this method serviceBeatmapRequestLocked = true; Beatmap candidateOriginalBeatmap = null, candidateNewBeatmap = null; while (completedBeatmapRequest == null || completedBeatmapRequest.RequestNumber != mapChangeRequests.Last().RequestNumber) { completedBeatmapRequest = mapChangeRequests.Last(); candidateOriginalBeatmap = await Task.Run(() => LoadBeatmap(mapChangeRequests.Last().Name)); if (candidateOriginalBeatmap != null) { candidateNewBeatmap = new Beatmap(candidateOriginalBeatmap); } // if a new request came in, invalidate candidate beatmap and service the new request } // no new requests, we can commit to using this beatmap OriginalBeatmap = candidateOriginalBeatmap; NewBeatmap = candidateNewBeatmap; if (OriginalBeatmap == null) { SetState(EditorState.NOT_READY); NotReadyReason = BadBeatmapReason.ERROR_LOADING_BEATMAP; BeatmapSwitched?.Invoke(this, EventArgs.Empty); } else { if (OriginalBeatmap.HitObjectCount == 0) { SetState(EditorState.NOT_READY); NotReadyReason = BadBeatmapReason.EMPTY_MAP; BeatmapSwitched?.Invoke(this, EventArgs.Empty); } else { // Apply multiplier NewBeatmap.SetRate(BpmMultiplier); // Apply bpm scaled settings if (ScaleAR) { NewBeatmap.ApproachRate = DifficultyCalculator.CalculateMultipliedAR(candidateOriginalBeatmap, BpmMultiplier); } if (ScaleOD) { NewBeatmap.OverallDifficulty = DifficultyCalculator.CalculateMultipliedOD(candidateOriginalBeatmap, BpmMultiplier); } // Apply locked settings if (HpIsLocked) { NewBeatmap.HPDrainRate = lockedHP; } if (CsIsLocked) { NewBeatmap.CircleSize = lockedCS; } if (ArIsLocked) { NewBeatmap.ApproachRate = lockedAR; } if (OdIsLocked) { NewBeatmap.OverallDifficulty = lockedOD; } if (BpmIsLocked) { SetBpm(lockedBpm); } // Apply Hardrock if (EmulateHardrock) { NewBeatmap.CircleSize = OriginalBeatmap.CircleSize * 1.3M; } if (EmulateHardrock) { NewBeatmap.OverallDifficulty = JunUtils.Clamp(GetScaledOD() * 1.4M, 0M, 10M); } SetState(EditorState.READY); RequestDiffCalc(); BeatmapSwitched?.Invoke(this, EventArgs.Empty); BeatmapModified?.Invoke(this, EventArgs.Empty); } } ControlsModified?.Invoke(this, EventArgs.Empty); serviceBeatmapRequestLocked = false; }
public static void GenerateAudioFile(string inFile, string outFile, double multiplier, bool changePitch = false) { if (multiplier == 1) { throw new ArgumentException("Don't call this function if multiplier is 1.0x"); } string temp1 = JunUtils.GetTempFilename("mp3"); // audio copy string temp2 = JunUtils.GetTempFilename("wav"); // decoded wav string temp3 = JunUtils.GetTempFilename("wav"); // stretched file string temp4 = JunUtils.GetTempFilename("mp3"); // encoded mp3 // TODO: try catch CopyFile(inFile, temp1); // mp3 => wav Process lame1 = new Process(); lame1.StartInfo.FileName = "Speed Changer Stuff\\lame.exe"; lame1.StartInfo.Arguments = $"-q 9 --priority 4 --decode \"{temp1}\" \"{temp2}\""; lame1.StartInfo.UseShellExecute = false; lame1.StartInfo.CreateNoWindow = true; lame1.Start(); lame1.WaitForExit(); // stretch (or speed up) wav float cents = (float)(1200.0f * Math.Log(multiplier) / Math.Log(2)); float semitones = cents / 100.0f; Process soundstretch = new Process(); soundstretch.StartInfo.FileName = "Speed Changer Stuff\\soundstretch.exe"; if (changePitch) { soundstretch.StartInfo.Arguments = $"\"{temp2}\" \"{temp3}\" -quick -naa -tempo={(multiplier - 1) * 100} -pitch={semitones}"; } else { soundstretch.StartInfo.Arguments = $"\"{temp2}\" \"{temp3}\" -quick -naa -tempo={(multiplier - 1) * 100}"; } soundstretch.StartInfo.UseShellExecute = false; soundstretch.StartInfo.CreateNoWindow = true; soundstretch.Start(); soundstretch.WaitForExit(); // wav => mp3 Process lame2 = new Process(); lame2.StartInfo.FileName = "Speed Changer Stuff\\lame.exe"; lame2.StartInfo.Arguments = $"-q 9 --priority 4 \"{temp3}\" \"{temp4}\""; lame2.StartInfo.UseShellExecute = false; lame2.StartInfo.CreateNoWindow = true; lame2.Start(); lame2.WaitForExit(); CopyFile(temp4, outFile); // Clean up File.Delete(temp1); File.Delete(temp2); File.Delete(temp3); File.Delete(temp4); }