public ITFHeader ToITF() { ITFHeader result = new ITFHeader(); result.BeatPerMinute = Header.DefaultBPM; result.TickPerRow = Header.DefaultTempo; foreach (var pat in Patterns) { ITFPattern itfp = new ITFPattern(); bool emptypattern = true; for (int row = 0; row < pat.NumberOfRows; row++) { ITFRow itfr = new ITFRow(); bool emptyrow = true; for (int channel = 0; channel < Header.NumberOfChannels; channel++) { XMNote xmnote = pat.PatArr[row, channel]; ITFNote itfn = null; if (xmnote != null) { emptyrow = false; emptypattern = false; itfn = new ITFNote(); if (xmnote.Instrument != 0) { itfn.Instrument = xmnote.Instrument; } if (xmnote.Note != 0) { itfn.Note = xmnote.Note; } itfn.Volume = xmnote.Volume; itfn.NoteOff = xmnote.noteoff; itfn.Effect = xmnote.Effect; itfn.EffectParam = xmnote.EffectParam; } itfr.Channels.Add(itfn); } if (emptyrow) { itfr = null; } itfp.Rows.Add(itfr); } if (emptypattern) { itfp = null; } result.Patterns.Add(itfp); } foreach (byte item in Header.PatternOrderTable) { result.PlayOrder.Add(result.Patterns[item]); } return(result); }
public static int ym2151octave(this ITFNote itfnote) { if (itfnote.actualnote == 0) { return(Math.Max(itfnote.octave.Value - 1, 0)); } return(itfnote.octave.Value); }
public static void NoteIntoBytes(this ITFNote cn, List <byte> bytes, int channel, Conversion conv, byte operatormask = 0b01111000) { if (channel > 7) { return; } byte register, value = 0; //Instrument setup for channel if (cn.Instrument != null) { if (conv.CYMS.LastInstrumentPerChannel[channel] != cn.Instrument) { ResetChannel(bytes, channel, conv.CYMS); YM2151Instrument ci = conv.DefinedInstruments[cn.Instrument.Value]; var cbs = ci.ToControlBytes(channel); foreach (var item in cbs) { addtobytes(bytes, item.Key, item.Value, conv.CYMS); } conv.CYMS.LastInstrumentPerChannel[channel] = (byte)cn.Instrument.Value; } } //Volume control if (!cn.NoteOff && cn.Note != 0 && cn.Volume == 0) { cn.Volume = 64; } if (cn.Effect == 0xC) { cn.Volume = cn.EffectParam; } if (cn.Volume != 0) { if (conv.CYMS.LastInstrumentPerChannel[channel] != 0) { YM2151Instrument ci = conv.DefinedInstruments[conv.CYMS.LastInstrumentPerChannel[channel]]; //Csak a hallható operátor hangerejét kéne módosítani. Elvileg. //De most még nem. Dictionary <byte, byte> tempd = new Dictionary <byte, byte>(); ci.SetVolumeToBytes(tempd, channel, (byte)cn.Volume.Value); foreach (var item in tempd) { addtobytes(bytes, item.Key, item.Value, conv.CYMS); } } } if (cn.NoteOff || cn.Effect == 0x14) { //$08 -SSSSCCC Key On(Play Sound) C = Channel S = Slot(C2 M2 C1 M1) //Minden operátor kuss. register = (byte)(0x08); value = 0b00000000; value += (byte)(channel & 0b00000111); addtobytes(bytes, register, value, conv.CYMS); } else { if (cn.Note.HasValue) { if (conv.Project.YM2151Instruments[(byte)cn.Instrument - 1].Detune.HasValue) { int newnote = cn.Note.Value + conv.Project.YM2151Instruments[(byte)cn.Instrument - 1].Detune.Value; if (newnote <= 0) { throw new Exception("Detune error. Out of range downwards."); } cn.Note = (byte)(newnote); if (cn.NoteOff) { throw new Exception("Detune error. Out of range upwards."); } } //$28 -$2F -OOONNNN Chn0 - 7 KeyCode O = Octive, N = Note register = (byte)(0x28 + channel); value = 0; value += (byte)((cn.ym2151octave() & 0b00000111) << 4); value += (byte)(cn.ym2151note() & 0b00001111); addtobytes(bytes, register, value, conv.CYMS); //$08 -SSSSCCC Key On(Play Sound) C = Channel S = Slot(C2 M2 C1 M1) //Most minden operátor szóljon. register = (byte)(0x08); value = operatormask; value += (byte)(channel & 0b00000111); addtobytes(bytes, register, value, conv.CYMS); } } }
public static List <byte> PatternToBytes(this ITFPattern itfpattern, Conversion conv, bool[] channelson = null, int?FromRow = null, int?ToRow = null) { int usedchannels = Math.Min(itfpattern.Rows.Max(x => x.Channels.Count), 8); if (channelson == null) { channelson = new bool[usedchannels]; for (int i = 0; i < channelson.Length; i++) { channelson[i] = true; } } List <byte> bytes = new List <byte>(); byte register, value = 0; for (int rows = 0; rows < itfpattern.Rows.Count; rows++) { if (FromRow.HasValue && FromRow.Value > rows) { continue; } if (ToRow.HasValue && ToRow.Value < rows) { continue; } //Is there any effects needs ticking here? bool tickingneeded = false; for (int channels = 0; channels < itfpattern.Rows[rows].Channels.Count; channels++) { ITFNote cn = itfpattern.Rows[rows].Channels[channels]; //Tempo determination. This can come from all available channels. if (cn.Effect == 0xF) { if (cn.EffectParam < 32) { conv.CurrentTickPerRow = cn.EffectParam.Value; } else { conv.CurrentBPM = cn.EffectParam.Value; } } //Ticking will apply only for the phisycally converted channels. if (channels > usedchannels) { continue; } int epu = ((byte)cn.EffectParam.Value >> 4) & 0xF; int epd = (byte)cn.EffectParam.Value & 0xF; tickingneeded = tickingneeded || (cn.Effect == 0xE && epu == 0xD) || (cn.Effect == 0xE && epu == 0xC) || (cn.Effect == 0xA && cn.EffectParam != 0); } if (tickingneeded) { ITFNote[,] tickarr = new ITFNote[conv.CurrentTickPerRow, usedchannels]; for (int channel = 0; channel < usedchannels; channel++) { if (!channelson[channel]) { continue; } ITFNote cn = itfpattern.Rows[rows].Channels[channel]; int epu = ((byte)cn.EffectParam.Value >> 4) & 0xF; int epd = (byte)cn.EffectParam.Value & 0xF; if (cn.Effect == 0xA && cn.EffectParam != 0) //Volume slide { tickarr[0, channel] = cn; int volume = cn.Volume.Value; int diff = -epd; if (epu != 0) { diff = epu; } for (int i = 1; i < conv.CurrentTickPerRow; i++) { volume += diff; if (volume < 0) { volume = 0; } if (volume > 64) { volume = 64; } tickarr[i, channel] = new ITFNote() { Volume = (byte)volume }; } } else if (cn.Effect == 0xE && epu == 0xD) { if (epd > 5) { epd = 5; } tickarr[epd, channel] = cn; } else if (cn.Effect == 0xE && epu == 0xC) //Note cut { if (epd > 5) { epd = 5; } tickarr[0, channel] = cn; tickarr[epd, channel] = new ITFNote() { NoteOff = true }; } else { tickarr[0, channel] = cn; } } for (int ticks = 0; ticks < conv.CurrentTickPerRow; ticks++) { for (int channel = 0; channel < usedchannels; channel++) { if (!channelson[channel]) { continue; } ITFNote cn = tickarr[ticks, channel]; if (cn != null) { NoteIntoBytes(cn, bytes, channel, conv); } } //Timing register = 0; value = (byte)bpmtoplayertick(conv); addtobytes(bytes, register, value, conv.CYMS); } } else { for (int channel = 0; channel < usedchannels; channel++) { if (!channelson[channel]) { continue; } ITFNote cn = itfpattern.Rows[rows].Channels[channel]; NoteIntoBytes(cn, bytes, channel, conv); } //Timing register = 0; value = (byte)bpmtoplayertickrow(conv); addtobytes(bytes, register, value, conv.CYMS); } } return(bytes); }
public static int ym2151note(this ITFNote itfnote) { return(Normal2YMNote[itfnote.actualnote.Value]); }
public static void ToDMF(Conversion conv) { List <byte> outfile = new List <byte>(); ////START OF DMF FORMAT ////FORMAT FLAGS //16 Bytes: Format String, must be ".DelekDefleMask." outfile.AddRange(Encoding.ASCII.GetBytes(".DelekDefleMask.")); //1 Byte: File Version, must be 24(0x18) for DefleMask v0.12.0 outfile.Add(0x18); ////SYSTEM SET //1 Byte: System: // SYSTEM_GENESIS 0x02(SYSTEM_TOTAL_CHANNELS 10) // SYSTEM_GENESIS(EXT.CH3) 0x12(SYSTEM_TOTAL_CHANNELS 13) // SYSTEM_SMS 0x03(SYSTEM_TOTAL_CHANNELS 4) // SYSTEM_GAMEBOY 0x04(SYSTEM_TOTAL_CHANNELS 4) // SYSTEM_PCENGINE 0x05(SYSTEM_TOTAL_CHANNELS 6) // SYSTEM_NES 0x06(SYSTEM_TOTAL_CHANNELS 5) // SYSTEM_C64(SID 8580) 0x07(SYSTEM_TOTAL_CHANNELS 3) // SYSTEM_C64(SID 6581) 0x17(SYSTEM_TOTAL_CHANNELS 3) // SYSTEM_YM2151 0x08(SYSTEM_TOTAL_CHANNELS 13) outfile.Add(0x08); //YM2151 for now. ////VISUAL INFORMATION //1 Byte: Song Name Chars Count(0 - 255) outfile.Add((byte)conv.XM.Header.Modulename.Length); //N Bytes: Song Name Chars outfile.AddRange(Encoding.ASCII.GetBytes(conv.XM.Header.Modulename)); //1 Byte: Song Author Chars Count(0 - 255) outfile.Add(9); //N Bytes: Song Author Chars outfile.AddRange(Encoding.ASCII.GetBytes("TrackConv")); //1 Byte: Highlight A in patterns outfile.Add(0x00); //1 Byte: Highlight B in patterns outfile.Add(0x00); ////MODULE INFORMATION //1 Byte: Time Base outfile.Add((byte)conv.CurrentBPM); //1 Byte: Tick Time 1 outfile.Add((byte)conv.CurrentRowPerBeat); //1 Byte: Tick Time 2 outfile.Add((byte)conv.CurrentTickPerRow); //1 Byte: Frames Mode(0 = PAL, 1 = NTSC) outfile.Add(1); //Probably //1 Byte: Using Custom HZ(If set to 1, NTSC or PAL is ignored) outfile.Add(1); //CX16 60 Hz //1 Byte: Custom HZ value 1 outfile.Add(60); //1 Byte: Custom HZ value 2 outfile.Add(0); //1 Byte: Custom HZ value 3 outfile.Add(0); //4 Bytes: TOTAL_ROWS_PER_PATTERN int totalrowsperpattern = conv.ITF.Patterns.Max(x => x == null ? 0 : x.Rows.Count); outfile.AddRange(BitConverter.GetBytes(totalrowsperpattern)); //1 Byte: TOTAL_ROWS_IN_PATTERN_MATRIX outfile.Add((byte)conv.XM.Header.SongLengthInPatterns); //(Arpeggio Tick speed was here in previous version(1 Byte), it was REMOVED since ver 11.1) ////PATTERN MATRIX VALUES (A matrix of SYSTEM_TOTAL_CHANNELS x TOTAL_ROWS_IN_PATTERN_MATRIX) //Repeat this SYSTEM_TOTAL_CHANNELS times for (int i = 0; i < 13; i++) { // Repeat this TOTAL_ROWS_IN_PATTERN_MATRIX times for (int j = 0; j < conv.XM.Header.SongLengthInPatterns; j++) { // 1 Byte: Pattern Matrix Value: (Index from SYSTEM_TOTAL_CHANNELS loop, Index from TOTAL_ROWS_IN_PATTERN_MATRIX loop) outfile.Add(conv.XM.Header.PatternOrderTable[j]); } } ////INSTRUMENTS DATA (.DMP format is similar to this part, but there are some discrepancies, please read DMP_Specs.txt for more details) // 1 Byte: TOTAL_INSTRUMENTS outfile.Add((byte)conv.DefinedInstruments.Count); //Repeat this TOTAL_INSTRUMENTS times for (int i = 1; i <= conv.DefinedInstruments.Count; i++) { // 1 Byte: Instrument Name Chars Count(0 - 255) outfile.Add((byte)conv.DefinedInstruments[i].NAME.Length); // N Bytes: Instrument Name Chars outfile.AddRange(Encoding.ASCII.GetBytes(conv.DefinedInstruments[i].NAME)); // 1 Byte: Instrument Mode(0 = STANDARD INS, 1 = FM INS) outfile.Add(1); // //PER INSTRUMENT MODE DATA // //IF INSTRUMENT MODE IS FM ( = 1) // 1 Byte: ALG outfile.Add(conv.DefinedInstruments[i].CON); // 1 Byte: FB outfile.Add(conv.DefinedInstruments[i].FB); // 1 Byte: LFO(FMS on YM2612, PMS on YM2151) outfile.Add(conv.DefinedInstruments[i].PMS); // 1 Byte: LFO2(AMS on YM2612, AMS on YM2151) outfile.Add(conv.DefinedInstruments[i].AMS); // Repeat this TOTAL_OPERATORS times for (int o = 0; o < 4; o++) { // 1 Byte: AM outfile.Add(conv.DefinedInstruments[i].OPS[o].AM); // 1 Byte: AR outfile.Add(conv.DefinedInstruments[i].OPS[o].AR); // 1 Byte: DR outfile.Add(conv.DefinedInstruments[i].OPS[o].D1R); // 1 Byte: MULT outfile.Add(conv.DefinedInstruments[i].OPS[o].MULT); // 1 Byte: RR outfile.Add(conv.DefinedInstruments[i].OPS[o].RR); // 1 Byte: SL outfile.Add(conv.DefinedInstruments[i].OPS[o].KS); // 1 Byte: TL outfile.Add(conv.DefinedInstruments[i].OPS[o].TL); // 1 Byte: DT2 outfile.Add(conv.DefinedInstruments[i].OPS[o].DT2); // 1 Byte: RS outfile.Add(conv.DefinedInstruments[i].OPS[o].D1L); // 1 Byte: DT outfile.Add(conv.DefinedInstruments[i].OPS[o].DT); // 1 Byte: D2R outfile.Add(conv.DefinedInstruments[i].OPS[o].D2R); // 1 Byte: SSGMODE(BIT 4 = 0 Disabled, 1 Enabled, BITS 0, 1, 2 SSG_MODE) outfile.Add(conv.DefinedInstruments[i].OPS[o].SSGEG_Enabled); } } ////END OF INSTRUMENTS DATA ////WAVETABLES DATA // 1 Byte: TOTAL_WAVETABLES outfile.Add(0); //Nope //Repeat this TOTAL_WAVETABLES times // 4 Bytes: WAVETABLE_SIZE // Repeat this WAVETABLE_SIZE times // 4 Bytes: Wavetable Data ////PATTERNS DATA // Repeat this SYSTEM_TOTAL_CHANNELS times for (int i = 0; i < 13; i++) { // 1 Byte: CHANNEL_EFFECTS_COLUMNS_COUNT outfile.Add(1); // Repeat this TOTAL_ROWS_IN_PATTERN_MATRIX times for (int j = 0; j < conv.ITF.PlayOrder.Count; j++) { // Repeat this TOTAL_ROWS_PER_PATTERN times for (int k = 0; k < totalrowsperpattern; k++) { ITFNote note; if (i >= conv.XM.Header.NumberOfChannels) { note = new ITFNote(); } else { if (k >= conv.ITF.PlayOrder[j].Rows.Count) { continue; } note = conv.ITF.PlayOrder[j].Rows[k].Channels[i]; } // 2 Bytes: Note for this index outfile.AddRange(BitConverter.GetBytes((Int16)(note.actualnote.HasValue ? note.actualnote.Value : 0))); // 2 Bytes: Octave for this index outfile.AddRange(BitConverter.GetBytes((Int16)(note.octave.HasValue ? note.octave.Value : 0))); // //Note values: // //01 C# // //02 D- // //03 D# // //04 E- // //05 F- // //06 F# // //07 G- // //08 G# // //09 A- // //10 A# // //11 B- // //12 C- // //Special cases: // //Note = 0 and octave = 0 means empty. // //Note = 100 means NOTE OFF, no matter what is inside the octave value. // 2 Bytes: Volume for this index(-1 = Empty) outfile.AddRange(BitConverter.GetBytes((Int16)(note.Volume.HasValue ? note.Volume.Value : 0))); // Repeat this CHANNEL_EFFECTS_COLUMNS_COUNT times // 2 Bytes: Effect Code for this index(-1 = Empty) outfile.AddRange(BitConverter.GetBytes((Int16)(note.Effect.HasValue ? note.Effect.Value : 0))); // 2 Bytes: Effect Value for this index(-1 = Empty) outfile.AddRange(BitConverter.GetBytes((Int16)(note.EffectParam.HasValue ? note.EffectParam.Value : 0))); // 2 Bytes: Instrument for this index(-1 = Empty) outfile.AddRange(BitConverter.GetBytes((Int16)(note.Instrument.HasValue ? note.Instrument.Value : 0))); } } } ////PCM SAMPLES DATA //1 Byte: TOTAL_SAMPLES outfile.Add(0); //Nope //Repeat this TOTAL_SAMPLES times // 4 Bytes: SAMPLE_SIZE // 1 Byte: Sample Name Chars Count(0 - 255) // N Bytes: Sample Name Chars // 1 Byte: Sample Rate // 1 Byte: Sample Pitch // 1 Byte: Sample Amp // 1 Byte: Sample Bits(8 or 16) // Repeat this SAMPLE_SIZE times // 2 Bytes: Actual Sample Data ////END OF DMF FORMAT /// //MemoryStream stream = new MemoryStream(outfile.ToArray()); //using (FileStream compressedFileStream = File.Create("music.dmf")) //{ // using (DeflateStream compressionStream = new DeflateStream(compressedFileStream, CompressionMode.Compress)) // { // stream.CopyTo(compressionStream); // } //} var output2 = CreateToMemoryStream(outfile.ToArray()); var arr2 = output2.ToArray(); File.WriteAllBytes("music.dmf", arr2); }