public static S3MFile Parse(string path) { FileStream stream = new FileStream(path, FileMode.Open); using (BinaryReader reader = new BinaryReader(stream)) { S3MFile file = new S3MFile(); file.Name = ReadFileName(reader); CheckStreamPosition(stream, 28); // skip one byte (it's always 1A) stream.ReadByte(); file.Type = reader.ReadBytesAsInt(1); CheckStreamPosition(stream, 30); // skip to next row stream.Seek(2, SeekOrigin.Current); CheckStreamPosition(stream, 0x20); //file.OrderCount = ReadByteAsInt(reader); file.OrderCount = reader.ReadInt16(); file.InstrumentCount = reader.ReadInt16(); file.PatternCount = reader.ReadInt16(); // skip flags reader.ReadInt16(); // skip version info reader.ReadInt16(); // skip file format info reader.ReadInt16(); ReadSCRM(reader); CheckStreamPosition(stream, 0x30); file.GlobalVolume = reader.ReadBytesAsInt(1); file.InitialSpeed = reader.ReadBytesAsInt(1); file.InitialTempo = reader.ReadBytesAsInt(1); file.MasterVolume = reader.ReadBytesAsInt(1); file.UltraClickRemoval = reader.ReadBytesAsInt(1); file.LoadChannelPanSettings = reader.ReadBytesAsInt(1); stream.Seek(0x40, SeekOrigin.Begin); file.ChannelSettings = ReadByteArrayAsIntArray(32, reader); file.Orders = ReadByteArrayAsIntArray(file.OrderCount, reader); file.InstrumentPointers = ReadParapointers(file.InstrumentCount, reader); file.PatternPointers = ReadParapointers(file.PatternCount, reader); file.Patterns.AddRange(ReadPatterns(file.PatternPointers, stream, reader)); return file; } }
public static List<List<Event>> Generate(S3MFile file) { Dictionary<int, Channel> channels = new Dictionary<int, Channel>(); int speed = file.InitialSpeed; int tick = 0; int rowSkip = 0; Channel firstChannel = GetChannel(channels, 1); TempoEvent tempoEvent = new TempoEvent(tick, file.InitialTempo); firstChannel.AddNoteEvent(tempoEvent); TimeSignatureEvent currentTimeSignatureEvent = new TimeSignatureEvent(tick, 4, 4); firstChannel.AddNoteEvent(currentTimeSignatureEvent); foreach (var order in file.Orders) { if (order == 255) { break; } var pattern = file.Patterns[order]; int patternStartTick = tick; int rowIndex = rowSkip; for (; rowIndex < pattern.Rows.Count; rowIndex++) { rowSkip = 0; bool breakPatternToRow = false; Row row = pattern.Rows[rowIndex]; foreach (var channelEvent in row.ChannelEvents) { Channel channel = GetChannel(channels, channelEvent.ChannelNumber); bool needNoteOff = channel.IsPlayingNote && channelEvent.NoteAction != NoteAction.None; if (needNoteOff) { channel.AddNoteEvent(GenerateNoteOffEvent(channel, tick)); } if (channelEvent.NoteAction == NoteAction.Start) { var delay = 0; if (pattern.PatternNumber == 8 && channelEvent.Command != CommandType.None) { Console.WriteLine("Pattern {0} Row {1} Channel {2} {3}", pattern.PatternNumber, row.RowNumber, channelEvent.ChannelNumber, channelEvent.Command); } if (channelEvent.Command == CommandType.Notedelay) { if (208 <= channelEvent.Data && channelEvent.Data <= 214) // HACK: 214 is a guess at the top range of SD commands { delay = channelEvent.Data - 208; Debug.Assert(delay >= 0); } } channel.AddNoteEvent(GenerateNoteOnEvent(channel, channelEvent, tick + delay)); } if (channelEvent.Data != 0) { channel.Data = channelEvent.Data; } if (channelEvent.Command == CommandType.BreakPatternToRow) { breakPatternToRow = true; rowSkip = channelEvent.Data; break; } else if (channelEvent.Command == CommandType.SetSpeed) { speed = channelEvent.Data; } } tick += speed; if (breakPatternToRow) { // just finished last row in this pattern // because we are jumping to a new patter var modulo = (rowIndex + 1) % 32; if (modulo != 0) { // sad to say, not sure why mod 8 but divide by 16 works var m8 = (rowIndex + 1) % 8; if (m8 == 0) { firstChannel.AddNoteEvent(new TimeSignatureEvent(patternStartTick, (rowIndex + 1) / 16, 4)); firstChannel.AddNoteEvent(new TimeSignatureEvent(tick, 4, 4)); } } // now go to next pattern break; } } } // finalize any leftover note on events foreach (var key in channels.Keys) { Channel channel = channels[key]; if (channel.CurrentNote != null) { channel.AddNoteEvent(GenerateNoteOffEvent(channel, tick)); } } List<List<Event>> allEvents = new List<List<Event>>(); foreach (var key in channels.Keys.OrderBy(i => i)) { allEvents.Add(channels[key].NoteEvents); } return allEvents; }
public static void SaveXml(S3MFile file, string path) { XmlDocument doc = new XmlDocument(); doc.LoadXml("<score-partwise version=\"1.1\"><part-list/></score-partwise>"); // add part for each instrument for (int instrumentIndex = 0; instrumentIndex < file.InstrumentCount; instrumentIndex++) { XmlElement part = doc.CreateElement("score-part"); XmlElement partList = (XmlElement)doc.DocumentElement.SelectSingleNode("part-list"); partList.AppendChild(part); part.SetAttribute("id", instrumentIndex.ToString()); XmlElement partName = doc.CreateElement("part-name"); partName.InnerText = String.Format("Part {0}", instrumentIndex); part.AppendChild(partName); } foreach (Pattern pattern in file.Patterns) { foreach (ChannelEvent evt in pattern.EventsByChannel) { //Console.Out.WriteLine(evt); } break; } Pattern p1 = file.Patterns[0]; var c = from evt in p1.EventsByChannel where evt.ChannelNumber == 1 && evt.Instrument == 2 && evt.Volume > 20 select evt; XmlElement part1 = doc.CreateElement("part"); part1.SetAttribute("id", "1"); doc.DocumentElement.AppendChild(part1); XmlElement measure = doc.CreateElement("measure"); part1.AppendChild(measure); measure.SetAttribute("number", "1"); measure.SetAttribute("width", "800"); measure.InnerXml = " <attributes> <divisions>32</divisions> <key> <fifths>0</fifths> <mode>major</mode> </key> <time> <beats>4</beats> <beat-type>4</beat-type> </time> <clef> <sign>G</sign> <line>2</line> </clef> <staff-details print-object=\"no\"/> </attributes>"; XmlElement beam = null; XmlElement lastNote = null; XmlElement firstNote = null; foreach (var evt in c) { Console.Out.WriteLine(evt); XmlElement note = doc.CreateElement("note"); measure.AppendChild(note); lastNote = note; if (firstNote == null) { firstNote = note; } XmlElement pitch = doc.CreateElement("pitch"); note.AppendChild(pitch); int step = evt.Note & 15; int octave = evt.Note >> 4; bool alter = Alter[step] != 0; pitch.InnerXml = String.Format("<step>{0}</step><alter>{2}</alter><octave>{1}</octave>", Notes[step], octave, Alter[step]); Console.Out.WriteLine(pitch.InnerXml); XmlElement duration = doc.CreateElement("duration"); note.AppendChild(duration); duration.InnerText = "1"; XmlElement type = doc.CreateElement("type"); note.AppendChild(type); type.InnerText = "eighth"; XmlElement stem = doc.CreateElement("stem"); note.AppendChild(stem); stem.InnerText = "up"; if (beam == null) { beam = doc.CreateElement("beam"); beam.SetAttribute("number", "1"); beam.InnerText = "begin"; note.AppendChild(beam); } else { beam = doc.CreateElement("beam"); beam.SetAttribute("number", "1"); beam.InnerText = "end"; } //break; } if (lastNote != firstNote) { lastNote.AppendChild(beam); } //Console.Out.WriteLine(doc.OuterXml); doc.Save(path); }