public MOD(Data.Buffer data) { data.SetEndianess(Endian.Endianess.Big); Log.Info.Write(ErrorSystemType.Data, $"Loading MOD song: {System.Text.Encoding.ASCII.GetString(data.Pop(20).ReinterpretAsArray(20))}"); // songname // add dummy sample 0 samples.Add(new Sample()); for (int i = 1; i < 32; ++i) // samples 1-31 { Log.Info.Write(ErrorSystemType.Data, $"Sample {i} name: {System.Text.Encoding.ASCII.GetString(data.Pop(22).ReinterpretAsArray(22))}"); // samplename samples.Add(new Sample() { Length = data.Pop <UInt16>() * 2, FineTune = ConvertFineTune(data.Pop <byte>() & 0xf), Volume = data.Pop <byte>(), // 0-64, change in dB = 20*log10(Volume/64) RepeatPointOffset = data.Pop <UInt16>() * 2, RepeatLength = Math.Max(0, data.Pop <UInt16>() - 1) * 2 }); } int songLength = data.Pop <byte>(); if (songLength < 1 || songLength > 128) { throw new ExceptionAudio("Invalid MOD format."); } data.Skip(1); byte[] songPatterns = data.Pop(128).ReinterpretAsArray(128); var magic = data.Pop(4).ToString(); if (magic != "M!K!" && magic != "M.K.") { throw new ExceptionAudio("Invalid or unsupported MOD format."); } int numPatterns = songPatterns.Max() + 1; List <Pattern> patterns = new List <Pattern>(numPatterns); for (int i = 0; i < numPatterns; ++i) { var pattern = new Pattern(); for (int div = 0; div < 64; ++div) { for (int chan = 0; chan < 4; ++chan) { pattern.AddNote(chan, new Note(data)); } } patterns.Add(pattern); } for (int i = 0; i < samples.Count; ++i) { if (samples[i].Length > 0) { data.Skip(2); // ignore tracker word samples[i].Length -= 2; uint length = (uint)samples[i].Length; if (length > 0) { samples[i].SetData(data.Pop(length).ReinterpretAsArray(length)); } } } for (int i = 0; i < songLength; ++i) { song.Add(patterns[songPatterns[i]]); } }
public XMI(Data.Buffer data) { // Note: Chunk length and so on are encoded as big endian. // But as we don't use them we use little endian because // the XMI data is encoded in little endian. data.SetEndianess(Endian.Endianess.Little); // Form chunk if (data.ToString(4) != "FORM") { return; } data.Pop(4); // FORM data.Pop <uint>(); // FORM chunk length // format XDIR if (data.ToString(4) != "XDIR") { return; } data.Pop(4); // XDIR if (data.ToString(4) != "INFO") { return; } data.Pop(4); // INFO data.Pop <uint>(); // INFO chunk length int numTracks = data.Pop <ushort>(); if (numTracks != 1) { return; // we only support one track per file } if (data.ToString(4) != "CAT ") { return; } data.Pop(4); // CAT_ data.Pop <uint>(); // CAT chunk length // format XMID if (data.ToString(4) != "XMID") { return; } data.Pop(4); // XMID // load the one track // Form chunk if (data.ToString(4) != "FORM") { return; } data.Pop(4); // FORM data.Pop <uint>(); // FORM chunk length // format XMID if (data.ToString(4) != "XMID") { return; } data.Pop(4); // XMID // TIMB chunk if (data.ToString(4) != "TIMB") { return; } data.Pop(4); // TIMB data.Pop <uint>(); // TIMB chunk length int count = data.Pop <ushort>(); for (int j = 0; j < count; ++j) { // we don't need the TIMB information, just skip it data.Pop(2); } // EVNT chunk if (data.ToString(4) != "EVNT") { return; } data.Pop(4); // EVNT data.Pop <uint>(); // EVNT chunk length // read xmi/midi events while (data.Readable()) { ParseEvent(data); } var eventIndices = new Dictionary <Event, int>(); for (int i = 0; i < events.Count; ++i) { eventIndices.Add(events[i], i); } events.Sort((a, b) => { int result = a.StartTime.CompareTo(b.StartTime); if (result == 0) { return(eventIndices[a].CompareTo(eventIndices[b])); } return(result); }); }