public void Write(GAX2_Song song, string outPath) { #if ISWINDOWS Sequence s = new Sequence(); Track t = new Track(); for (int i = 0; i < song.NumChannels; i++) { s.Add(CreateTrack(song, i)); } // This plugin doesn't overwrite files if (File.Exists(outPath)) { File.Delete(outPath); } s.Save(outPath); #endif }
private XM_Pattern GetPattern(GAX2_Song song, int index) { XM_Pattern pat = new XM_Pattern(); pat.NumChannels = song.NumChannels; pat.NumRows = song.NumRowsPerPattern; XM_PatternRow[] rows = Enumerable.Range(0, song.NumChannels).SelectMany(c => GetPatternRows(song, index, c, song.NumRowsPerPattern)).ToArray(); pat.PatternRows = new XM_PatternRow[rows.Length]; for (int row = 0; row < song.NumRowsPerPattern; row++) { for (int ch = 0; ch < song.NumChannels; ch++) { pat.PatternRows[row * song.NumChannels + ch] = rows[ch * song.NumRowsPerPattern + row]; } } // pat.PackedPatternDataSize = (ushort)(song.NumChannels * song.NumRowsPerPattern); pat.PackedPatternDataSize = (ushort)pat.PatternRows.Sum(p => p.Size); return(pat); }
public XM_Instrument GetInstrument(GAX2_Song song, int index) { int ind = song.InstrumentIndices[index]; var gax_instr = song.InstrumentSet[ind].Value; XM_Sample smp = new XM_Sample(); smp.SampleName = "Sample " + gax_instr.Sample; smp.SampleData16 = ConvertSample(song.Samples[index].Sample); //song.Samples[index].Sample.Select(b => (sbyte)(b - 128)).ToArray(); smp.Type = 1 << 4; // 16 bit sample data smp.SampleLength = (uint)smp.SampleData16.Length * 2; //smp.FineTune = gax_instr.Pitch1; smp.RelativeNoteNumber = (sbyte)(gax_instr.Pitch2 * 10); XM_Instrument instr = new XM_Instrument(); instr.InstrumentName = "Instrument " + ind; instr.Samples = new XM_Sample[] { smp }; return(instr); }
public XM ConvertToXM(GAX2_Song song) { XM xm = new XM(); xm.ModuleName = song.ParsedName; xm.Instruments = song.InstrumentIndices.Select((s, i) => GetInstrument(song, i)).ToArray(); xm.NumInstruments = (ushort)xm.Instruments.Length; xm.NumChannels = song.NumChannels; xm.NumPatterns = song.NumPatternsPerChannel; xm.PatternOrderTable = Enumerable.Range(0, song.NumPatternsPerChannel).Select(i => (byte)i).ToArray(); xm.SongLength = (ushort)xm.PatternOrderTable.Length; xm.DefaultTempo = 6; xm.DefaultBPM = 150; xm.Flags = 1; if (xm.PatternOrderTable.Length < 256) { byte[] patOrderTable = xm.PatternOrderTable; Array.Resize(ref patOrderTable, 256); xm.PatternOrderTable = patOrderTable; } xm.HeaderSize = (uint)(20 + xm.PatternOrderTable.Length); xm.Patterns = Enumerable.Range(0, song.NumPatternsPerChannel).Select(i => GetPattern(song, i)).ToArray(); return(xm); }
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); }