public void GetTempoChanges_MultipleChanges() { var microsecondsPerQuarterNote1 = 100000; var time1 = 1000; var microsecondsPerQuarterNote2 = 700000; var time2 = 1500; using (var tempoMapManager = new TempoMapManager()) { tempoMapManager.SetTempo(time2, new Tempo(microsecondsPerQuarterNote2)); tempoMapManager.SetTempo(time1, new Tempo(microsecondsPerQuarterNote1)); var tempoMap = tempoMapManager.TempoMap; var changes = tempoMap.GetTempoChanges(); Assert.AreEqual(2, changes.Count(), "Count of tempo changes is invalid."); var change1 = changes.First(); Assert.AreEqual(time1, change1.Time, "Time of first change is invalid."); Assert.AreEqual(new Tempo(microsecondsPerQuarterNote1), change1.Value, "Tempo of first change is invalid."); var change2 = changes.Last(); Assert.AreEqual(time2, change2.Time, "Time of second change is invalid."); Assert.AreEqual(new Tempo(microsecondsPerQuarterNote2), change2.Value, "Tempo of second change is invalid."); } }
private static TempoMap GenerateComplexTempoMap() { // 4/4 5/8 5/16 5/8 // |----+----+----+----|----+----+----+----|--+--+--+--+--|-+-+-+-+-|-+-+-+-+-|-+-+-+-+-|--+--+--+--+--| // 0 1 2 3 4 5 6 7 var steps = new[] { Tuple.Create(2 * MusicalTimeSpan.Whole, new TimeSignature(5, 8)), Tuple.Create(5 * MusicalTimeSpan.Eighth, new TimeSignature(5, 16)), Tuple.Create(15 * MusicalTimeSpan.Sixteenth, new TimeSignature(5, 8)), }; using (var tempoMapManager = new TempoMapManager(new TicksPerQuarterNoteTimeDivision(TicksPerQuarterNote))) { var time = new MusicalTimeSpan(); foreach (var step in steps) { time += step.Item1; tempoMapManager.SetTimeSignature(time, step.Item2); } tempoMapManager.SetTempo(new MetricTimeSpan(0, 0, 10), Tempo.FromMillisecondsPerQuarterNote(300)); tempoMapManager.SetTempo(new MetricTimeSpan(0, 1, 30), Tempo.FromMillisecondsPerQuarterNote(600)); tempoMapManager.SetTempo(new MetricTimeSpan(0, 1, 31), Tempo.FromMillisecondsPerQuarterNote(640)); return(tempoMapManager.TempoMap); } }
private static TempoMap GetTempoMap() { using (var tempoMapManager = new TempoMapManager()) { tempoMapManager.SetTempo(new MetricTimeSpan(0, 0, 1), Tempo.FromBeatsPerMinute(60)); tempoMapManager.SetTempo(new MetricTimeSpan(0, 0, 10), Tempo.FromBeatsPerMinute(150)); tempoMapManager.SetTempo(new MetricTimeSpan(0, 0, 50), Tempo.FromBeatsPerMinute(100)); return(tempoMapManager.TempoMap); } }
public void GetTempoAtTime_NonDefaultTempoMap_MultipleChangesAtMiddle_GetAfterSecondChange() { var microsecondsPerQuarterNote1 = 100000; var microsecondsPerQuarterNote2 = 700000; using (var tempoMapManager = new TempoMapManager()) { tempoMapManager.SetTempo(100, new Tempo(microsecondsPerQuarterNote1)); tempoMapManager.SetTempo(1000, new Tempo(microsecondsPerQuarterNote2)); var tempoMap = tempoMapManager.TempoMap; var tempo = tempoMap.GetTempoAtTime(new MidiTimeSpan(5000)); Assert.AreEqual(new Tempo(microsecondsPerQuarterNote2), tempo, "Tempo is invalid."); } }
public static void TestTimedEvents( Pattern pattern, ICollection <TimedEventInfo> expectedTimedEventsInfos, params Tuple <long, Tempo>[] tempoChanges) { TempoMap tempoMap; using (var tempoMapManager = new TempoMapManager()) { foreach (var tempoChange in tempoChanges) { tempoMapManager.SetTempo(tempoChange.Item1, tempoChange.Item2); } tempoMap = tempoMapManager.TempoMap; } var midiFile = pattern.ToFile(tempoMap, Channel); var expectedTimedEvents = expectedTimedEventsInfos.Select(i => new TimedEvent(i.Event, TimeConverter.ConvertFrom(i.Time ?? new MidiTimeSpan(), tempoMap))); var actualTimedEvents = midiFile.GetTimedEvents(); MidiAsserts.AreEqual( expectedTimedEvents, actualTimedEvents.Where(e => expectedTimedEvents.Any(ee => ee.Event.EventType == e.Event.EventType)), false, 0, "Events are invalid."); }
private static MidiFile TestNotes(Pattern pattern, ICollection <NoteInfo> expectedNotesInfos, params Tuple <long, Tempo>[] tempoChanges) { var channel = (FourBitNumber)2; TempoMap tempoMap = null; using (var tempoMapManager = new TempoMapManager()) { foreach (var tempoChange in tempoChanges) { tempoMapManager.SetTempo(tempoChange.Item1, tempoChange.Item2); } tempoMap = tempoMapManager.TempoMap; } var midiFile = pattern.ToFile(tempoMap, channel); var expectedNotes = expectedNotesInfos.Select(i => { var expectedTime = TimeConverter.ConvertFrom(i.Time ?? new MetricTime(), tempoMap); var expectedLength = LengthConverter.ConvertFrom(i.Length, expectedTime, tempoMap); return(new Note(i.NoteNumber, expectedLength, expectedTime) { Velocity = i.Velocity, Channel = channel }); }); Assert.IsTrue(NoteEquality.Equals(expectedNotes, midiFile.GetNotes())); return(midiFile); }
private static void TestTimedEventsWithExactOrder( Pattern pattern, ICollection <TimedEventInfo> expectedTimedEventsInfos, params Tuple <long, Tempo>[] tempoChanges) { TempoMap tempoMap; using (var tempoMapManager = new TempoMapManager()) { foreach (var tempoChange in tempoChanges) { tempoMapManager.SetTempo(tempoChange.Item1, tempoChange.Item2); } tempoMap = tempoMapManager.TempoMap; } var midiFile = pattern.ToFile(tempoMap, Channel); var expectedTimedEvents = expectedTimedEventsInfos.Select(i => new TimedEvent(i.Event, TimeConverter.ConvertFrom(i.Time ?? new MidiTimeSpan(), tempoMap))); var actualTimedEvents = midiFile.GetTimedEvents(); Assert.IsTrue(TimedEventEquality.AreEqual(expectedTimedEvents, actualTimedEvents, false), "Events have invalid order."); }
private static void TestTimedEvents( Pattern pattern, ICollection <TimedEventInfo> expectedTimedEventsInfos, params Tuple <long, Tempo>[] tempoChanges) { TempoMap tempoMap; using (var tempoMapManager = new TempoMapManager()) { foreach (var tempoChange in tempoChanges) { tempoMapManager.SetTempo(tempoChange.Item1, tempoChange.Item2); } tempoMap = tempoMapManager.TempoMap; } var midiFile = pattern.ToFile(tempoMap, Channel); var expectedTimedEvents = expectedTimedEventsInfos.Select(i => new TimedEvent(i.Event, TimeConverter.ConvertFrom(i.Time ?? new MidiTimeSpan(), tempoMap))); var actualTimedEvents = midiFile.GetTimedEvents(); foreach (var expectedEvent in expectedTimedEvents) { Assert.IsTrue(actualTimedEvents.Any(actual => TimedEventEquality.AreEqual(expectedEvent, actual, false)), $"There are no event: {expectedEvent}"); } }
private static MidiFile TestTimedEvents(Pattern pattern, ICollection <TimedEventInfo> expectedTimedEventsInfos, params Tuple <long, Tempo>[] tempoChanges) { var channel = (FourBitNumber)2; TempoMap tempoMap = null; using (var tempoMapManager = new TempoMapManager()) { foreach (var tempoChange in tempoChanges) { tempoMapManager.SetTempo(tempoChange.Item1, tempoChange.Item2); } tempoMap = tempoMapManager.TempoMap; } var midiFile = pattern.ToFile(tempoMap, channel); var expectedTimedEvents = expectedTimedEventsInfos.Select(i => new TimedEvent(i.Event, TimeConverter.ConvertFrom(i.Time ?? new MetricTimeSpan(), tempoMap))); var actualTimedEvents = midiFile.GetTimedEvents(); Assert.IsTrue(expectedTimedEvents.All(expected => actualTimedEvents.Any(actual => TimedEventEquality.Equals(expected, actual)))); return(midiFile); }
private static TempoMap GetTempoMap(IEnumerable <TimedMidiEvent> timedMidiEvents, TimeDivision timeDivision) { using (var tempoMapManager = new TempoMapManager(timeDivision)) { var setTempoEvents = timedMidiEvents.Where(e => e.Event is SetTempoEvent) .OrderBy(e => e.Time, new TimeSpanComparer()); foreach (var timedMidiEvent in setTempoEvents) { var setTempoEvent = (SetTempoEvent)timedMidiEvent.Event; tempoMapManager.SetTempo(timedMidiEvent.Time, new Tempo(setTempoEvent.MicrosecondsPerQuarterNote)); } var timeSignatureEvents = timedMidiEvents.Where(e => e.Event is TimeSignatureEvent) .OrderBy(e => e.Time, new TimeSpanComparer()); foreach (var timedMidiEvent in timeSignatureEvents) { var timeSignatureEvent = (TimeSignatureEvent)timedMidiEvent.Event; tempoMapManager.SetTimeSignature(timedMidiEvent.Time, new TimeSignature(timeSignatureEvent.Numerator, timeSignatureEvent.Denominator)); } return(tempoMapManager.TempoMap); } }
public void ReplaceTempoMap_ByDefault() { using (var tempoMapManager = new TempoMapManager()) { tempoMapManager.SetTempo(100, new Tempo(10)); tempoMapManager.SetTempo(300, new Tempo(100)); tempoMapManager.ReplaceTempoMap(TempoMap.Default); var tempoMap = tempoMapManager.TempoMap; Assert.AreEqual(TicksPerQuarterNoteTimeDivision.DefaultTicksPerQuarterNote, ((TicksPerQuarterNoteTimeDivision)tempoMap.TimeDivision).TicksPerQuarterNote); Assert.IsFalse(tempoMap.Tempo.Values.Any()); Assert.IsFalse(tempoMap.TimeSignature.Values.Any()); } }
private static TempoMap GenerateSimpleTempoMap() { // 4/4 5/8 5/16 // |----+----+----+----|--+--+--+--+--|-+-+-+-+-| // 0 1 2 3 using (var tempoMapManager = new TempoMapManager(new TicksPerQuarterNoteTimeDivision(TicksPerQuarterNote))) { tempoMapManager.SetTimeSignature(MusicalTimeSpan.Whole, new TimeSignature(5, 8)); tempoMapManager.SetTimeSignature(MusicalTimeSpan.Whole + 5 * MusicalTimeSpan.Eighth, new TimeSignature(5, 16)); tempoMapManager.SetTempo(new MetricTimeSpan(0, 0, 10), Tempo.FromMillisecondsPerQuarterNote(300)); tempoMapManager.SetTempo(new MetricTimeSpan(0, 1, 30), Tempo.FromMillisecondsPerQuarterNote(600)); return(tempoMapManager.TempoMap); } }
public void ConvertCsvToMidiFile_SingleTrackChunk_MetricTimes(MidiFileCsvLayout layout, bool orderEvents, string[] csvLines) { if (!orderEvents) { var tmp = csvLines[2]; csvLines[2] = csvLines[5]; csvLines[5] = tmp; } var midiFile = ConvertCsvToMidiFile(layout, TimeSpanType.Metric, csvLines); TempoMap expectedTempoMap; using (var tempoMapManager = new TempoMapManager(new TicksPerQuarterNoteTimeDivision(500))) { tempoMapManager.SetTempo(new MetricTimeSpan(0, 1, 3), new Tempo(300000)); expectedTempoMap = tempoMapManager.TempoMap; } var expectedEvents = new[] { new TimeAndMidiEvent(new MetricTimeSpan(), new NoteOnEvent((SevenBitNumber)50, (SevenBitNumber)120) { Channel = (FourBitNumber)10 }), new TimeAndMidiEvent(new MetricTimeSpan(), new TextEvent("Test")), new TimeAndMidiEvent(new MetricTimeSpan(0, 1, 0), new NoteOnEvent((SevenBitNumber)50, (SevenBitNumber)110) { Channel = (FourBitNumber)7 }), new TimeAndMidiEvent(new MetricTimeSpan(0, 1, 3), new SetTempoEvent(300000)), new TimeAndMidiEvent(new MetricTimeSpan(0, 1, 10), new NoteOffEvent((SevenBitNumber)50, (SevenBitNumber)70) { Channel = (FourBitNumber)10 }), new TimeAndMidiEvent(new MetricTimeSpan(0, 10, 3), new NoteOffEvent((SevenBitNumber)50, (SevenBitNumber)80) { Channel = (FourBitNumber)7 }) } .Select(te => new TimedEvent(te.Event, TimeConverter.ConvertFrom(te.Time, expectedTempoMap))) .ToArray(); Assert.AreEqual(1, midiFile.GetTrackChunks().Count(), "Track chunks count is invalid."); CollectionAssert.AreEqual(midiFile.GetTempoMap().GetTempoChanges(), expectedTempoMap.GetTempoChanges(), "Invalid tempo map."); Assert.AreEqual(new TicksPerQuarterNoteTimeDivision(500), midiFile.TimeDivision, "Invalid time division."); Assert.IsTrue(TimedEventEquality.AreEqual(expectedEvents, midiFile.GetTimedEvents(), false), "Invalid events."); }
public void Conversion_Metric_Math_Subtract_TempoChanged() { TempoMap tempoMap = null; using (var tempoMapManager = new TempoMapManager()) { tempoMapManager.SetTempo(new MetricTime(0, 1, 5), new Tempo(20000)); tempoMapManager.SetTempo(new MetricTime(0, 1, 50), new Tempo(2400)); tempoMap = tempoMapManager.TempoMap; } var mathLength = new MathLength(new MetricLength(0, 0, 20), new MetricLength(0, 0, 50)); var mathTime = new MathTime(new MetricTime(0, 2, 0), mathLength, MathOperation.Subtract); Assert.AreEqual(TimeConverter.ConvertFrom(new MetricTime(0, 0, 50), tempoMap), TimeConverter.ConvertFrom(mathTime, tempoMap)); }
public void GetTempoAtTime_NonDefaultTempoMap_SingleChangeAtMiddle_GetAtChange() { var microsecondsPerQuarterNote = 100000; using (var tempoMapManager = new TempoMapManager()) { tempoMapManager.SetTempo(1000, new Tempo(microsecondsPerQuarterNote)); var tempoMap = tempoMapManager.TempoMap; var tempo = tempoMap.GetTempoAtTime(new MidiTimeSpan(1000)); Assert.AreEqual(new Tempo(microsecondsPerQuarterNote), tempo, "Tempo is invalid."); } }
public static MidiFile TestNotes(Pattern pattern, ICollection <NoteInfo> expectedNotesInfos, params Tuple <long, Tempo>[] tempoChanges) { TempoMap tempoMap; using (var tempoMapManager = new TempoMapManager()) { foreach (var tempoChange in tempoChanges) { tempoMapManager.SetTempo(tempoChange.Item1, tempoChange.Item2); } tempoMap = tempoMapManager.TempoMap; } var midiFile = pattern.ToFile(tempoMap, Channel); var expectedNotes = expectedNotesInfos.Select(i => { var expectedTime = TimeConverter.ConvertFrom(i.Time ?? new MetricTimeSpan(), tempoMap); var expectedLength = LengthConverter.ConvertFrom(i.Length, expectedTime, tempoMap); return(new DryWetMidi.Interaction.Note(i.NoteNumber, expectedLength, expectedTime) { Velocity = i.Velocity, Channel = Channel }); }) .OrderBy(n => n.Time) .ToArray(); var actualNotes = midiFile.GetNotes().ToArray(); Assert.AreEqual(expectedNotes.Length, actualNotes.Length, "Notes count is invalid."); var j = 0; foreach (var expectedActual in expectedNotes.Zip(actualNotes, (e, a) => new { Expected = e, Actual = a })) { var expectedNote = expectedActual.Expected; var actualNote = expectedActual.Actual; Assert.IsTrue(NoteEquality.AreEqual(expectedNote, actualNote), $"Note {j} is invalid. Expected: {expectedNote}; actual: {actualNote}."); j++; } return(midiFile); }
public void GetTempoChanges_SingleChange_AtMiddle() { var microsecondsPerQuarterNote = 100000; var time = 1000; using (var tempoMapManager = new TempoMapManager()) { tempoMapManager.SetTempo(time, new Tempo(microsecondsPerQuarterNote)); var tempoMap = tempoMapManager.TempoMap; var changes = tempoMap.GetTempoChanges(); Assert.AreEqual(1, changes.Count(), "Count of tempo changes is invalid."); var change = changes.First(); Assert.AreEqual(time, change.Time, "Time of change is invalid."); Assert.AreEqual(new Tempo(microsecondsPerQuarterNote), change.Value, "Tempo of change is invalid."); } }
private static TempoMap GetTempoMap() { var changeTempoOffset = 5 * TimeOffset - 1; var maxTime = Math.Max((TimesCount - 1) * TimeOffset, (TimesCount - 1) * TimeOffset + Length); bool firstTempo = true; using (var tempoMapManager = new TempoMapManager()) { var time = 0L; while (time < maxTime) { tempoMapManager.SetTempo(time, firstTempo ? FirstTempo : SecondTempo); firstTempo = !firstTempo; time += changeTempoOffset; } return(tempoMapManager.TempoMap); } }
public static MidiFile TestNotes(Pattern pattern, ICollection <NoteInfo> expectedNotesInfos, params Tuple <long, Tempo>[] tempoChanges) { TempoMap tempoMap; using (var tempoMapManager = new TempoMapManager()) { foreach (var tempoChange in tempoChanges) { tempoMapManager.SetTempo(tempoChange.Item1, tempoChange.Item2); } tempoMap = tempoMapManager.TempoMap; } var midiFile = pattern.ToFile(tempoMap, Channel); var expectedNotes = expectedNotesInfos.Select(i => { var expectedTime = TimeConverter.ConvertFrom(i.Time ?? new MetricTimeSpan(), tempoMap); var expectedLength = LengthConverter.ConvertFrom(i.Length, expectedTime, tempoMap); return(new DryWetMidi.Interaction.Note(i.NoteNumber, expectedLength, expectedTime) { Velocity = i.Velocity, Channel = Channel }); }) .OrderBy(n => n.Time) .ToArray(); var actualNotes = midiFile.GetNotes().ToArray(); MidiAsserts.AreEqual(expectedNotes, actualNotes, "Notes are invalid."); return(midiFile); }
internal static MidiFile Load(string filePath) { try { MMSong mmSong = JsonExtensions.DeserializeFromFileCompressed <MMSong>(filePath); if (mmSong.schemaVersion < 1 && mmSong.schemaVersion > 2) { throw new FileFormatException("Error: This mmsong file format is not understood."); } // For now, just play the first available song. // if (mmSong.songs.Count != 1) throw new FileFormatException("Error: BMP currently only supports mmsong files with 1 song in them."); MMSong.Song song = mmSong.songs[0]; MidiFile sequence = new MidiFile(); sequence.Chunks.Add(new TrackChunk()); sequence.TimeDivision = new TicksPerQuarterNoteTimeDivision(600); using (TempoMapManager tempoMapManager = sequence.ManageTempoMap()) tempoMapManager.SetTempo(0, Tempo.FromBeatsPerMinute(100)); foreach (MMSong.Bard bard in song.bards) { List <Note> notes = new List <Note>(); bool failure = false; switch (bard.instrument) { case Instrument.Cymbal: case Instrument.Trumpet: case Instrument.Trombone: case Instrument.Horn: case Instrument.Tuba: case Instrument.Saxophone: case Instrument.Violin: case Instrument.Viola: case Instrument.Cello: case Instrument.DoubleBass: bard.sequence = bard.sequence.ToDictionary( x => x.Key + 2, x => x.Value); break; default: break; } if (bard.sequence.Count % 2 == 0) { long lastTime = 0; int lastNote = 254; foreach (KeyValuePair <long, int> sEvent in bard.sequence) { if (!failure) { if (lastNote == 254) { if (sEvent.Value <= 60 && sEvent.Value >= 24 && ((sEvent.Key * 25 % 100) == 50 || (sEvent.Key * 25) % 100 == 0)) { lastNote = sEvent.Value + 24; lastTime = sEvent.Key * 25; } else { failure = true; } } else { if (sEvent.Value == 254) { long dur = (sEvent.Key * 25) - lastTime; notes.Add(new Note((SevenBitNumber)lastNote, dur, lastTime) { Channel = (FourBitNumber)14, Velocity = (SevenBitNumber)(int)127, OffVelocity = (SevenBitNumber)(int)0 }); lastNote = 254; lastTime = sEvent.Key * 25; } else { failure = true; } } } } } else { failure = true; } if (failure) { throw new FileFormatException("Error: This mmsong file is corrupted"); } TrackChunk currentChunk = new TrackChunk(new SequenceTrackNameEvent(bard.instrument.ToString())); currentChunk.AddNotes(notes); notes = null; sequence.Chunks.Add(currentChunk); currentChunk = null; } using (var manager = new TimedEventsManager(sequence.GetTrackChunks().First().Events)) manager.Events.Add(new TimedEvent(new MarkerEvent(), (sequence.GetDuration <MetricTimeSpan>().TotalMicroseconds / 1000) + 100)); return(sequence); } catch (Exception ex) { throw ex; } }
public static void ExportToAudicaFile(AudicaFile audicaFile) { if (!File.Exists(audicaFile.filepath)) { Debug.Log("Save file is gone... :("); return; } Encoding encoding = Encoding.GetEncoding(437); using (var archive = ZipArchive.Open(audicaFile.filepath)) { HandleCache.CheckCacheFolderValid(); HandleCache.CheckSaveFolderValid(); bool expert = false, advanced = false, standard = false, easy = false; //Write the cues files to disk so we can add them to the audica file. if (audicaFile.diffs.expert.cues != null) { File.WriteAllText($"{Application.dataPath}/.cache/expert-new.cues", CuesToJson(audicaFile.diffs.expert)); expert = true; } if (audicaFile.diffs.advanced.cues != null) { File.WriteAllText($"{Application.dataPath}/.cache/advanced-new.cues", CuesToJson(audicaFile.diffs.advanced)); advanced = true; } if (audicaFile.diffs.moderate.cues != null) { File.WriteAllText($"{Application.dataPath}/.cache/moderate-new.cues", CuesToJson(audicaFile.diffs.moderate)); standard = true; } if (audicaFile.diffs.beginner.cues != null) { File.WriteAllText($"{Application.dataPath}/.cache/beginner-new.cues", CuesToJson(audicaFile.diffs.beginner)); easy = true; } File.WriteAllText($"{Application.dataPath}/.cache/song-new.desc", JsonUtility.ToJson(audicaFile.desc)); var workFolder = Path.Combine(Application.streamingAssetsPath, "Ogg2Audica"); MidiFile songMidi = MidiFile.Read(Path.Combine(workFolder, "songtemplate.mid")); using (var tempoMapManager = new TempoMapManager(new TicksPerQuarterNoteTimeDivision(480))) { float oneMinuteInMicroseconds = 60000000f; TempoMap tempoMap = tempoMapManager.TempoMap; foreach (var tempo in audicaFile.desc.tempoList) { tempoMapManager.SetTempo(new MetricTimeSpan((long)(tempo.time * 1000000)), new Tempo((long)(oneMinuteInMicroseconds / tempo.bpm))); } songMidi.ReplaceTempoMap(tempoMap); } songMidi.Write(Path.Combine(workFolder, $"{Application.dataPath}/.cache/song.mid"), true, MidiFileFormat.MultiTrack); //Remove any files we'll be replacing foreach (ZipArchiveEntry entry in archive.Entries) { if (entry.ToString() == "expert.cues") { archive.RemoveEntry(entry); } else if (entry.ToString() == "song.desc") { archive.RemoveEntry(entry); } else if (entry.ToString() == "song.mid") { archive.RemoveEntry(entry); } else if (entry.ToString() == "advanced.cues") { archive.RemoveEntry(entry); } else if (entry.ToString() == "moderate.cues") { archive.RemoveEntry(entry); } else if (entry.ToString() == "beginner.cues") { archive.RemoveEntry(entry); } } if (expert) { archive.AddEntry("expert.cues", $"{Application.dataPath}/.cache/expert-new.cues"); } if (advanced) { archive.AddEntry("advanced.cues", $"{Application.dataPath}/.cache/advanced-new.cues"); } if (standard) { archive.AddEntry("moderate.cues", $"{Application.dataPath}/.cache/moderate-new.cues"); } if (easy) { archive.AddEntry("beginner.cues", $"{Application.dataPath}/.cache/beginner-new.cues"); } archive.AddEntry("song.desc", $"{Application.dataPath}/.cache/song-new.desc"); archive.AddEntry("song.mid", $"{Application.dataPath}/.cache/song.mid"); archive.SaveTo(audicaFile.filepath + ".temp", SharpCompress.Common.CompressionType.None); archive.Dispose(); } File.Delete(audicaFile.filepath); File.Move(audicaFile.filepath + ".temp", audicaFile.filepath); Debug.Log("Export finished."); }