public override void SerializeImpl(SerializerObject s) { NumChannels = s.Serialize <ushort>(NumChannels, name: nameof(NumChannels)); NumRowsPerPattern = s.Serialize <ushort>(NumRowsPerPattern, name: nameof(NumRowsPerPattern)); NumPatternsPerChannel = s.Serialize <ushort>(NumPatternsPerChannel, name: nameof(NumPatternsPerChannel)); UShort_06 = s.Serialize <ushort>(UShort_06, name: nameof(UShort_06)); Volume = s.Serialize <ushort>(Volume, name: nameof(Volume)); UShort_0A = s.Serialize <ushort>(UShort_0A, name: nameof(UShort_0A)); SequenceDataPointer = s.SerializePointer(SequenceDataPointer, name: nameof(SequenceDataPointer)); InstrumentSetPointer = s.SerializePointer(InstrumentSetPointer, name: nameof(InstrumentSetPointer)); SampleSetPointer = s.SerializePointer(SampleSetPointer, name: nameof(SampleSetPointer)); SampleRate = s.Serialize <ushort>(SampleRate, name: nameof(SampleRate)); UShort_1A = s.Serialize <ushort>(UShort_1A, name: nameof(UShort_1A)); UShort_1C = s.Serialize <ushort>(UShort_1C, name: nameof(UShort_1C)); UShort_1E = s.Serialize <ushort>(UShort_1E, name: nameof(UShort_1E)); PatternTablePointers = s.SerializePointerArray(PatternTablePointers, NumChannels, name: nameof(PatternTablePointers)); Reserved = s.SerializeArray <uint>(Reserved, NumChannels, name: nameof(Reserved)); List <int> instruments = new List <int>(); if (PatternTable == null) { int instrumentCount = 0; PatternTable = new GAX2_PatternHeader[PatternTablePointers.Length][]; Patterns = new GAX2_Pattern[PatternTablePointers.Length][]; for (int i = 0; i < PatternTablePointers.Length; i++) { s.DoAt(PatternTablePointers[i], () => { PatternTable[i] = s.SerializeObjectArray <GAX2_PatternHeader>(PatternTable[i], NumPatternsPerChannel, name: $"{nameof(PatternTable)}[{i}]"); if (Patterns[i] == null) { Patterns[i] = new GAX2_Pattern[PatternTable[i].Length]; for (int j = 0; j < Patterns[i].Length; j++) { s.DoAt(SequenceDataPointer + PatternTable[i][j].SequenceOffset, () => { Patterns[i][j] = s.SerializeObject <GAX2_Pattern>(Patterns[i][j], onPreSerialize: t => t.Duration = NumRowsPerPattern, name: $"{nameof(Patterns)}[{i}][{j}]"); instrumentCount = Math.Max(instrumentCount, Patterns[i][j].Rows.Max(cmd => cmd.Command == GAX2_PatternRow.Cmd.Note ? cmd.Instrument + 1 : 0)); instruments.AddRange(Patterns[i][j].Rows.Where(cmd => cmd.Command == GAX2_PatternRow.Cmd.Note).Select(cmd => (int)cmd.Instrument)); }); } } }); } InstrumentIndices = instruments.Distinct().Where(i => i != 250).ToArray(); s.Log("Instrument Count: " + InstrumentIndices.Length); Pointer endOffset = Patterns.Max(ta => ta.Max(t => t.EndOffset)); s.DoAt(endOffset, () => { Name = s.Serialize <string>(Name, name: nameof(Name)); string[] parse = Name.Split('"'); ParsedName = parse[1]; ParsedArtist = parse[2].Substring(3, 0xF); s.Log(ParsedName + " - " + ParsedArtist); }); s.DoAt(InstrumentSetPointer, () => { InstrumentSet = s.SerializePointerArray <GAX2_Instrument>(InstrumentSet, instrumentCount, resolve: true, name: nameof(InstrumentSet)); }); Samples = new GAX2_Sample[InstrumentIndices.Length]; for (int i = 0; i < InstrumentIndices.Length; i++) { int ind = InstrumentIndices[i]; var instr = InstrumentSet[ind].Value; if (instr != null) { s.DoAt(SampleSetPointer + (instr.Sample) * 8, () => { Samples[i] = s.SerializeObject <GAX2_Sample>(Samples[i], name: $"{nameof(Samples)}[{i}]"); }); } } } }
private XM_PatternRow[] GetPatternRows(GAX2_Song song, int patternIndex, int channelIndex, int numRows) { GAX2_Pattern gax = song.Patterns[channelIndex][patternIndex]; XM_PatternRow[] rows = new XM_PatternRow[numRows]; int curRow = 0; byte? vol = null; byte? eff = null; byte? effParam = null; foreach (var row in gax.Rows) { switch (row.Command) { case GAX2_PatternRow.Cmd.StartTrack: break; case GAX2_PatternRow.Cmd.EmptyTrack: while (curRow < rows.Length) { rows[curRow++] = new XM_PatternRow(); } break; // TODO: correct these case GAX2_PatternRow.Cmd.Unknown: break; case GAX2_PatternRow.Cmd.EffectOnly: eff = (row.Effect < 16 && row.Effect != 14 && row.Effect != 12) ? (byte?)row.Effect : null; effParam = eff.HasValue ? (byte?)(row.Velocity) : null; vol = (row.Effect == 12) ? (byte?)(0x10 + (row.Velocity >> 2)) : null; rows[curRow++] = new XM_PatternRow(volumeColumnByte: vol, effectType: eff, effectParameter: effParam); break; case GAX2_PatternRow.Cmd.Unknown81: rows[curRow++] = new XM_PatternRow(); break; case GAX2_PatternRow.Cmd.RestSingle: rows[curRow++] = new XM_PatternRow(); break; case GAX2_PatternRow.Cmd.RestMultiple: for (int i = 0; i < row.RestDuration; i++) { rows[curRow++] = new XM_PatternRow(); } break; case GAX2_PatternRow.Cmd.Note: eff = (row.Effect < 16 && row.Effect != 14 && row.Effect != 12) ? (byte?)row.Effect : null; effParam = eff.HasValue ? (byte?)(row.Velocity) : null; vol = (row.Effect == 12) ? (byte?)(0x10 + (row.Velocity >> 2)) : null; if (row.Instrument == 250) { rows[curRow++] = new XM_PatternRow(note: 97, volumeColumnByte: vol, effectType: eff, effectParameter: effParam); } else { int instr = 1 + Array.IndexOf(song.InstrumentIndices, row.Instrument); rows[curRow++] = new XM_PatternRow(note: row.Note, instrument: (byte)instr, volumeColumnByte: vol, effectType: eff, effectParameter: effParam); } break; } } return(rows); }
private Track CreateTrack(GAX2_Song song, int trackNum) { Track t = new Track(); TempoChangeBuilder b = new TempoChangeBuilder(); b.Tempo = 500000; b.Build(); t.Insert(0, b.Result); ChannelMessageBuilder builder = new ChannelMessageBuilder(); int?lastNoteOn = null; int currentTime = 0; int timeScale = 5; t.EndOfTrackOffset = (song.Patterns[trackNum].Length * song.NumRowsPerPattern) * timeScale; for (int trackPiece = 0; trackPiece < song.Patterns[trackNum].Length; trackPiece++) { GAX2_Pattern gaxTrack = song.Patterns[trackNum][trackPiece]; int baseTime = trackPiece * song.NumRowsPerPattern; currentTime = baseTime; for (int i = 0; i < gaxTrack.Rows.Length; i++) { GAX2_PatternRow cmd = gaxTrack.Rows[i]; switch (cmd.Command) { case GAX2_PatternRow.Cmd.Note: if (cmd.Instrument == 250) { continue; } if (exportSingleSoundfont) { if (song.InstrumentSet[cmd.Instrument]?.Value == null || song.InstrumentSet[cmd.Instrument].Value.Sample >= 128) { continue; } } else { if (song.InstrumentSet[cmd.Instrument]?.Value == null || Array.IndexOf(song.InstrumentIndices, cmd.Instrument) >= 128) { continue; } } // Note off if (lastNoteOn.HasValue) { builder.Command = ChannelCommand.NoteOff; builder.MidiChannel = 0; builder.Data1 = lastNoteOn.Value; builder.Data2 = 127; builder.Build(); t.Insert(currentTime * timeScale, builder.Result); lastNoteOn = null; } // Program change { int instrument = 0; if (exportSingleSoundfont) { instrument = song.InstrumentSet[cmd.Instrument].Value.Sample; } else { instrument = Array.IndexOf(song.InstrumentIndices, cmd.Instrument); } builder.MidiChannel = 0; builder.Command = ChannelCommand.ProgramChange; builder.Data1 = instrument; builder.Build(); t.Insert(currentTime * timeScale, builder.Result); } // Note on { builder.Command = ChannelCommand.NoteOn; int freq = cmd.Note; int vel = cmd.Velocity; builder.Data1 = freq; //GetMidiPitch(GetFrequency(freq)); float velf = (vel / 255f); // hack int veli = Mathf.RoundToInt(velf * 127f); builder.Data2 = veli; lastNoteOn = builder.Data1; builder.Build(); t.Insert(currentTime * timeScale, builder.Result); } break; } currentTime += cmd.Duration; } } if (lastNoteOn.HasValue) { builder.Command = ChannelCommand.NoteOff; builder.MidiChannel = 0; builder.Data1 = lastNoteOn.Value; builder.Data2 = 127; builder.Build(); t.Insert(currentTime * timeScale, builder.Result); lastNoteOn = null; } return(t); }