private Player <Playable> TCIPlayable(string filePath, Koioto.Support.FileReader.Courses course) { var info = GetOpenTaikoChartInfomation(filePath); var result = new Player <Playable>(); // とりあえず、難易度を入れる var neededCourse = info.Courses.Where(d => GetCoursesFromStirng(d.Difficulty) == course); if (!neededCourse.Any()) { // コースが無ければnullを返す。 return(null); } // コースが存在すれば処理を続行。 var diff = neededCourse.First(); var singleFile = File.ReadAllText(Path.Combine(Path.GetDirectoryName(filePath), diff.Single), Encoding.UTF8); var multipleFile = new string[0]; if (diff.Multiple != null) { multipleFile = new string[diff.Multiple.Length]; for (var i = 0; i < multipleFile.Length; i++) { multipleFile[i] = File.ReadAllText(Path.Combine(Path.GetDirectoryName(filePath), diff.Multiple[i]), Encoding.UTF8); } } var single = JsonConvert.DeserializeObject <OpenTaikoChartCourse>(singleFile); var multiple = new OpenTaikoChartCourse[multipleFile.Length]; for (var i = 0; i < multiple.Length; i++) { multiple[i] = JsonConvert.DeserializeObject <OpenTaikoChartCourse>(multipleFile[i]); } result.Single = CourseParser.Parse(info, single); result.Multiple = multiple.Select(m => CourseParser.Parse(info, m)).ToArray(); return(result); }
public static Playable Parse(OpenTaikoChartInfomation otci, OpenTaikoChartCourse otcc) { // メドレーではないので大きさは1。 var playable = new Playable { Sections = new List <Chip> [1] }; var sections = playable.Sections; sections[0] = new List <Chip>(); var balloonIndex = 0; var courseJson = otcc; // パースする。 var list = sections[0]; // 初めから1秒開けておく。 var nowTime = 0L; var nowBPM = otci.BPM ?? 120.0; var nowMeasure = new Measure(4, 4); var nowScroll = 1.0; var isGoGoTime = false; var measureCount = 0; var isFirstNoteInMeasure = true; var movieOffset = otci.Movieoffset; var barVisible = true; Chip rollstartChip = null; // オフセットする。 var offset = otci.Offset ?? 0; // オフセットが0未満だったらBGMの開始を0にしてそれ以降をずらす。 // そうでないならそのままオフセットする。 if (offset < 0) { // //var oneMeasure = GetMeasureDuration(nowMeasure, nowBPM); var bgmStartChip = new Chip { ChipType = Chips.BGMStart, Time = nowTime - (long)(Math.Abs(offset) * 1000.0 * 1000.0), BPM = nowBPM }; list.Add(bgmStartChip); } else { var bgmStartChip = new Chip { ChipType = Chips.BGMStart, Time = nowTime, BPM = nowBPM }; list.Add(bgmStartChip); // その時間分ずらす nowTime += (long)(offset * 1000.0 * 1000.0); } var origin = list.Where(c => c.ChipType == Chips.BGMStart).First(); foreach (var measure in courseJson.Measures) { // その小節にいくつ音符があるかカウントする var notesCount = 0; var notesElementCount = 0; foreach (var line in measure) { if (!line.Trim().StartsWith("#") && !string.IsNullOrWhiteSpace(line.Trim())) { // 命令行ではない notesElementCount++; foreach (var digit in line) { if (digit >= '0' && digit <= '9') { // 数字だ notesCount++; } } } } foreach (var line in measure) { // 行 // ひとつの音符あたりの数 var timePerNotes = (long)(GetMeasureDuration(nowMeasure, nowBPM) / notesCount); if (!line.Trim().StartsWith("#")) { // 命令行ではない if (isFirstNoteInMeasure) { // 小節 var measureChip = new Chip { ChipType = Chips.Measure, CanShow = barVisible, Scroll = nowScroll, BPM = nowBPM, IsGoGoTime = isGoGoTime, Measure = nowMeasure, MeasureCount = measureCount, Time = nowTime }; list.Add(measureChip); isFirstNoteInMeasure = false; } foreach (var digit in line) { if (digit >= '0' && digit <= '9') { // 数字だ var note = NotesConverter.GetNotesFromChar(digit); // 音符の追加 var noteChip = new Chip { ChipType = Chips.Note, NoteType = note, Scroll = nowScroll, BPM = nowBPM, CanShow = true, IsGoGoTime = isGoGoTime, Measure = nowMeasure, MeasureCount = measureCount, Time = nowTime }; if (note == Notes.RollStart || note == Notes.ROLLStart || note == Notes.Balloon) { // 連打開始なので記憶しておく。 rollstartChip = noteChip; if (note == Notes.Balloon) { if (courseJson.Balloon.Length > balloonIndex) { noteChip.RollObjective = courseJson.Balloon[balloonIndex] ?? 5; balloonIndex++; } else { noteChip.RollObjective = 5; } } } else if (note == Notes.RollEnd) { // 連打開始なので記憶した連打をrollendにり当てる。 if (rollstartChip != null) { rollstartChip.RollEnd = noteChip; rollstartChip = null; } } list.Add(noteChip); // 時間をひとつすすめる nowTime += timePerNotes; } } } else { // 命令行 var eventChip = new Chip(); var command = line.Trim(); var param = command.IndexOf(' ') >= 0 ? command.Substring(command.IndexOf(' ')) : ""; bool commandMatch(string name) { return(command.StartsWith(name, StringComparison.InvariantCultureIgnoreCase)); } if (commandMatch("#bpm")) { // #bpm n1 if (double.TryParse(param, out var bpm)) { eventChip.ChipType = Chips.BPMChange; nowBPM = bpm; } else { continue; } } else if (commandMatch("#scroll")) { // #scroll n1 if (double.TryParse(param, out var scroll)) { eventChip.ChipType = Chips.ScrollChange; nowScroll = scroll; } else { continue; } } else if (commandMatch("#tsign")) { // #tsign n1/n2 var n1 = param.Substring(0, param.IndexOf('/')); var n2 = param.Substring(param.IndexOf('/') + 1); if (double.TryParse(n1, out var part) && double.TryParse(n2, out var beat)) { eventChip.ChipType = Chips.MeasureChange; nowMeasure = new Measure(part, beat); } else { continue; } } else if (commandMatch("#gogobegin")) { eventChip.ChipType = Chips.GoGoStart; isGoGoTime = true; } else if (commandMatch("#gogoend")) { eventChip.ChipType = Chips.GoGoEnd; isGoGoTime = false; } else if (commandMatch("#delay")) { // #delay n1 if (double.TryParse(param, out var delay)) { nowTime += (long)(delay * 1000.0 * 1000.0); } else { continue; } } else if (commandMatch("#bar")) { // #bar show/hide var visible = param.Trim(); if (visible == "show") { barVisible = true; } else if (visible == "hide") { barVisible = false; } else { continue; } } else { continue; } eventChip.Scroll = nowScroll; eventChip.BPM = nowBPM; eventChip.IsGoGoTime = isGoGoTime; eventChip.Measure = nowMeasure; eventChip.MeasureCount = measureCount; eventChip.Time = nowTime; list.Add(eventChip); } } // ノーツが空だったときの処理 if (notesElementCount <= 0) { // 小節 var measureChip = new Chip { ChipType = Chips.Measure, CanShow = barVisible, Scroll = nowScroll, BPM = nowBPM, IsGoGoTime = isGoGoTime, Measure = nowMeasure, MeasureCount = measureCount, Time = nowTime }; list.Add(measureChip); nowTime += (long)GetMeasureDuration(nowMeasure, nowBPM); } measureCount++; isFirstNoteInMeasure = true; } // 後処理。 // 動画の開始時間を表すチップを挿入する。 if (movieOffset.HasValue) { var offsetValue = movieOffset.Value; // 0未満だったら、全体をずらす必要がある。 if (offsetValue < 0) { // すべてのチップの時間をずらす list.ForEach(c => c.Time += (long)(Math.Abs(offsetValue) * 1000.0 * 1000.0)); var offsetChip = new Chip { ChipType = Chips.MovieStart, Time = origin.Time - (long)(Math.Abs(offsetValue) * 1000.0 * 1000.0) }; //list.Add(offsetChip); var nearestChip = list.Where(c => c.Time <= offsetChip.Time); list.Insert(nearestChip.Count() > 0 ? list.IndexOf(nearestChip.Last()) + 1 : 0, offsetChip); } else { // そのまま入れることができる。 var offsetChip = new Chip { ChipType = Chips.MovieStart, Time = origin.Time + (long)(Math.Abs(offsetValue) * 1000.0 * 1000.0) }; var nearestChip = list.Where(c => c.Time <= offsetChip.Time); list.Insert(nearestChip.Count() > 0 ? list.IndexOf(nearestChip.Last()) + 1 : 0, offsetChip); } } // オフセットを追加する。前に1小節、後ろに1小節。 { // 3秒。 var offsetTime = 3L * 1000 * 1000; { // 前 var origBPM = origin.BPM; list.ForEach(c => c.Time += offsetTime); } { // 後 var last = list.Last(); var lastChip = new Chip { BPM = last.BPM, Scroll = last.Scroll, CanShow = false, ChipType = Chips.Measure, Time = last.Time + offsetTime }; list.Add(lastChip); } } return(playable); }