public void ComputeTickPerRowForCurrentLine(TrackLineData[] trackLineData, int currentRow, int numChannels) { if (currentRow % numChannels == 0) { for (int i = 0; i < numChannels; i++) { int track = currentRow++; TrackLineData trackData = trackLineData[track]; if (trackData.IsSet) { if (trackData.EffectNumber != null) { char[] commandEffectSplitted = trackData.EffectNumber.ToCharArray(); char effType = commandEffectSplitted[0]; char effCom = commandEffectSplitted[1]; int effVal = Int16.Parse(trackData.EffectValue, System.Globalization.NumberStyles.HexNumber); char commandForTicks = playbackEngineVersion == Constants.MOD_VERSION_COMPATIBLE ? 'L' : 'K'; if (effCom.Equals(commandForTicks)) { ticksPerRow = effVal; } } } } } }
/* * Store all the row/track data in an array with size = (pattern.rows * totalChannels) * */ private TrackLineData[] GetLineTrackDataFromPatternOld(Pattern renoisePattern, int[] assignedTrackPosition) { int numRows = renoisePattern.NumberOfLines; int numChannels = assignedTrackPosition[assignedTrackPosition.Length - 1]; int patternDataSize = numRows * numChannels; TrackLineData[] patternStructsData = new TrackLineData[patternDataSize]; // scan each track on pattern for (int iCurrentTrack = 0; iCurrentTrack < renoisePattern.Tracks.PatternTrack.Length; iCurrentTrack++) { if (renoisePattern.Tracks.PatternTrack[iCurrentTrack].Lines != null) { // scan each lines filled on track for (int iCurrentLine = 0; iCurrentLine < renoisePattern.Tracks.PatternTrack[iCurrentTrack].Lines.Length; iCurrentLine++) { // row index starts from 0 int row = renoisePattern.Tracks.PatternTrack[iCurrentTrack].Lines[iCurrentLine].index; if (row >= numRows) { continue; } string note = null; string instrument = null; string volume = null; string panning = null; string effectNumber = null; string effectValue = null; // fill pattern values for note columns if (renoisePattern.Tracks.PatternTrack[iCurrentTrack].Lines[iCurrentLine].NoteColumns != null) { // avoids columns not visible to be written on pattern int trackTotalVisibleColumns = assignedTrackPosition[iCurrentTrack + 1] - assignedTrackPosition[iCurrentTrack]; int loopEnd = renoisePattern.Tracks.PatternTrack[iCurrentTrack].Lines[iCurrentLine].NoteColumns.NoteColumn.Length; if (trackTotalVisibleColumns < loopEnd) { loopEnd = trackTotalVisibleColumns; } // scan every columns on track for (int iCurrentColumn = 0; iCurrentColumn < loopEnd; iCurrentColumn++) { note = renoisePattern.Tracks.PatternTrack[iCurrentTrack].Lines[iCurrentLine].NoteColumns.NoteColumn[iCurrentColumn].Note; instrument = renoisePattern.Tracks.PatternTrack[iCurrentTrack].Lines[iCurrentLine].NoteColumns.NoteColumn[iCurrentColumn].Instrument; volume = renoisePattern.Tracks.PatternTrack[iCurrentTrack].Lines[iCurrentLine].NoteColumns.NoteColumn[iCurrentColumn].Volume; panning = renoisePattern.Tracks.PatternTrack[iCurrentTrack].Lines[iCurrentLine].NoteColumns.NoteColumn[iCurrentColumn].Panning; int trackAssigned = assignedTrackPosition[iCurrentTrack] + iCurrentColumn; int posInPattern = (row * numChannels) + trackAssigned; patternStructsData[posInPattern].IsSet = true; patternStructsData[posInPattern].Track = trackAssigned; patternStructsData[posInPattern].Row = row; patternStructsData[posInPattern].Note = note; patternStructsData[posInPattern].Instrument = instrument; patternStructsData[posInPattern].Volume = volume; patternStructsData[posInPattern].Panning = panning; } } // fill pattern values for effect columns if (renoisePattern.Tracks.PatternTrack[iCurrentTrack].Lines[iCurrentLine].EffectColumns != null) { effectNumber = renoisePattern.Tracks.PatternTrack[iCurrentTrack].Lines[iCurrentLine].EffectColumns.EffectColumn[0].Number; effectValue = renoisePattern.Tracks.PatternTrack[iCurrentTrack].Lines[iCurrentLine].EffectColumns.EffectColumn[0].Value; int trackAssigned = assignedTrackPosition[iCurrentTrack]; int totalNoteColumnsForThisTrack; if (iCurrentTrack == (renoisePattern.Tracks.PatternTrack.Length - 1)) { totalNoteColumnsForThisTrack = numChannels - trackAssigned; } else { totalNoteColumnsForThisTrack = assignedTrackPosition[iCurrentTrack + 1] - trackAssigned; } // spreads volume column effects for all columns in track for (int iCurrentColumn = 0; iCurrentColumn < totalNoteColumnsForThisTrack; iCurrentColumn++) { int posInPattern = (row * numChannels) + trackAssigned + iCurrentColumn; patternStructsData[posInPattern].IsSet = true; patternStructsData[posInPattern].Track = trackAssigned; patternStructsData[posInPattern].Row = row; patternStructsData[posInPattern].EffectNumber = ConvertNullEffectToZero(effectNumber); patternStructsData[posInPattern].EffectValue = ConvertNullEffectToZero(effectValue); } } } } } return(patternStructsData); }
/* * Store all the row/track data in an array with size = (pattern.rows * numChannels) * */ private TrackLineData[] GetLineTrackDataFromPattern(Pattern renoisePattern, SequencerTrack[] sequencerTracks) { int numRows = renoisePattern.NumberOfLines; int numChannels = GetNumChannels(sequencerTracks); int patternDataSize = numRows * numChannels; TrackLineData[] patternStructsData = new TrackLineData[patternDataSize]; // this is the channel index based to all renoise columns for track int channelIndex = 0; // iterate tracks foreach (var patternTrackIterator in renoisePattern.Tracks.PatternTrack.WithIndex()) { int track = patternTrackIterator.Index; PatternTrack patternTrack = patternTrackIterator.Value; int numVisibleColumnsTrack = sequencerTracks[track].NumberOfVisibleNoteColumns; int maxVisibleColumnIndex = numVisibleColumnsTrack - 1; if (patternTrack.Lines != null) { // iterate all lines (and subcolumns) for current track foreach (PatternLineNode line in patternTrack.Lines) { // row index starts from 0 int row = line.index; if (row >= numRows) { continue; } // note columns section if (line.NoteColumns != null) { // iterate note columns for current line foreach (var noteColumnIterator in line.NoteColumns.NoteColumn.WithIndex()) { int column = noteColumnIterator.Index; if (column > maxVisibleColumnIndex) { continue; } PatternLineNoteColumnNode noteColumn = noteColumnIterator.Value; string note = noteColumn.Note; string instrument = noteColumn.Instrument; string volume = noteColumn.Volume; string panning = noteColumn.Panning; string delay = noteColumn.Delay; int trackAssigned = channelIndex + column; int posInPattern = (row * numChannels) + trackAssigned; if (note != null || instrument != null || volume != null || panning != null || delay != null) { patternStructsData[posInPattern].IsSet = true; patternStructsData[posInPattern].Track = trackAssigned; patternStructsData[posInPattern].Row = row; patternStructsData[posInPattern].Note = note; patternStructsData[posInPattern].Instrument = instrument; patternStructsData[posInPattern].Volume = volume; patternStructsData[posInPattern].Panning = panning; patternStructsData[posInPattern].Delay = delay; System.Diagnostics.Debug.WriteLine(String.Format( "row: {0} column: {1} track: {2} note: {3} volume {4} panning {5} delay {6}", row, column, track, note, volume, panning, delay) ); } } } // effect columns section if (line.EffectColumns != null) { // iterate effect columns for current line foreach (var noteColumnIterator in line.EffectColumns.EffectColumn.WithIndex()) { int column = noteColumnIterator.Index; if (column > 0 || column > maxVisibleColumnIndex) { continue; } PatternLineEffectColumnNode noteColumn = noteColumnIterator.Value; string effectnumber = noteColumn.Number; string effectvalue = noteColumn.Value; int trackAssigned = channelIndex; if (effectnumber != null || effectvalue != null) { // spreads column effects for all the track sub-columns for (int iCol = 0; iCol < numVisibleColumnsTrack; iCol++) { int posInPattern = (row * numChannels) + trackAssigned + iCol; patternStructsData[posInPattern].IsSet = true; patternStructsData[posInPattern].Track = trackAssigned; patternStructsData[posInPattern].Row = row; patternStructsData[posInPattern].EffectNumber = ConvertNullEffectToZero(effectnumber); patternStructsData[posInPattern].EffectValue = ConvertNullEffectToZero(effectvalue); System.Diagnostics.Debug.WriteLine(String.Format( "row: {0} column: {1} track: {2} effect: {3} value: {4}", row, iCol, track, effectnumber, effectvalue) ); } } } } } } channelIndex += sequencerTracks[track].NumberOfVisibleNoteColumns; } return(patternStructsData); }
private static void SetPatternCell(List <TrackLineData> pattern, int columnCount, int row, int column, TrackLineData val) { pattern[GetPatternIndex(columnCount, row, column)] = val; }
private byte[] GetPatternData(PatternData patternData, int numChannels, int numMasterTrackColumns) { const int numRows4Pattern = 64; const int totalData4EachTrackLine = 4; int maxTrackLinesInPattern = numRows4Pattern * numChannels; byte[] data = new byte[numRows4Pattern * totalData4EachTrackLine * numChannels]; int cyclesToDo = patternData.NumRows * numChannels; if (cyclesToDo > maxTrackLinesInPattern) { cyclesToDo = maxTrackLinesInPattern; } int offset = 0; //int currentMasterTrackColumnToParse = 0; byte[] masterTrackCommand = new byte[2]; bool isMasterTrackCommandUsed = false; // force number of mastertrack columns to parse at x value; any column beyond the x value will be ignored const int maxMasterTrackColumnToParse = 0; int numMasterTrackColumnsToParse = maxMasterTrackColumnToParse; // numMasterTrackColumns -- count of mt columns in module // numMasterTrackColumnsToParse -- count of mt columns to parse if (numMasterTrackColumnsToParse > numMasterTrackColumns) { numMasterTrackColumnsToParse = numMasterTrackColumns; } int currentMasterTrackIndex = 0; int masterTrackIndexLimitForCurrentRow = 0; for (int i = 0; i < cyclesToDo; i++) { modUtils.ComputeTickPerRowForCurrentLine(patternData.TracksLineData, i, numChannels); int currentRow = i / numChannels; int currentChannel = i % numChannels + 1; if (currentChannel == 1) { if (isMasterTrackCommandUsed) { string errorMessage = string.Format("row {0}, channel {1}: {2}", currentRow, currentChannel, "Some MasterTrack command were not used due to missing free command effects slots"); OnReportProgress(new EventReportProgressArgs(errorMessage, MsgType.ERROR)); } //currentMasterTrackColumnToParse = 0; isMasterTrackCommandUsed = false; currentMasterTrackIndex = currentRow * numMasterTrackColumns; masterTrackIndexLimitForCurrentRow = currentMasterTrackIndex + numMasterTrackColumnsToParse; } while (currentMasterTrackIndex < masterTrackIndexLimitForCurrentRow && !isMasterTrackCommandUsed) { MasterTrackLineData masterTrackLineData = patternData.MasterTrackLineData[currentMasterTrackIndex]; if (masterTrackLineData.IsSet) { masterTrackCommand = modUtils.GetCommandsFromMasterTrack(masterTrackLineData.EffectNumber, masterTrackLineData.EffectValue); // test //masterTrackCommand = new byte[] { 1, 5 }; if (masterTrackCommand[0] + masterTrackCommand[1] > 0) { isMasterTrackCommandUsed = true; //break; } } //currentMasterTrackColumnToParse++; currentMasterTrackIndex++; } TrackLineData trackLineData = patternData.TracksLineData[i]; offset = i * totalData4EachTrackLine; int sampleNumber = 0; int period = 0; int effectNum = 0; int effectVal = 0; // check if any effect command used on this row; // otherwise, tries to transpose some renoise volume commands to mod effect command bool isEffectCommandUsed = false; if (trackLineData.IsSet || isMasterTrackCommandUsed) { if (trackLineData.Instrument != null) { sampleNumber = Int16.Parse(trackLineData.Instrument, System.Globalization.NumberStyles.HexNumber) + 1; } if (trackLineData.Note != null) { try { bool isTonePortamentoTriggered = false; isTonePortamentoTriggered = modUtils.IsTonePortamentoTriggered(trackLineData.EffectNumber, trackLineData.Volume, trackLineData.Panning); period = modUtils.GetModNote(trackLineData.Note, sampleNumber - 1, currentChannel - 1, isTonePortamentoTriggered); } catch (Exception e) { string errorMessage = string.Format("row {0}, instrument {1}, channel {2}: {3}", currentRow, (sampleNumber - 1), currentChannel, e.Message); OnReportProgress(new EventReportProgressArgs(errorMessage, MsgType.ERROR)); } } // means for effect value too if (trackLineData.EffectNumber != null) { try { byte[] values = modUtils.GetModEffect(trackLineData.EffectNumber, trackLineData.EffectValue, sampleNumber, currentChannel - 1, period != 0); if ((values[0] + values[1]) > 0) { isEffectCommandUsed = true; effectNum = values[0]; effectVal = values[1]; } } catch (ConversionException e) { string errorMessage = string.Format("row {0}, channel {1}: {2}", currentRow, currentChannel, e.Message); OnReportProgress(new EventReportProgressArgs(errorMessage, MsgType.ERROR)); } } // check for any volume / panning command to transpose if (isEffectCommandUsed == false) { // volume column if (trackLineData.Volume != null) { try { byte[] values = modUtils.TransposeVolumeToCommandEffect(trackLineData.Volume, sampleNumber, currentChannel - 1, period != 0); if ((values[0] + values[1]) > 0) { isEffectCommandUsed = true; effectNum = values[0]; effectVal = values[1]; } } catch (ConversionException e) { string errorMessage = string.Format("row {0}, channel {1}: {2}", currentRow, currentChannel, e.Message); OnReportProgress(new EventReportProgressArgs(errorMessage, MsgType.ERROR)); } } // delay column if (isEffectCommandUsed == false && trackLineData.Delay != null) { try { byte[] values = modUtils.TransposeDelayToCommandEffect(trackLineData.Delay); if ((values[0] + values[1]) > 0) { isEffectCommandUsed = true; effectNum = values[0]; effectVal = values[1]; } } catch (ConversionException e) { string errorMessage = string.Format("row {0}, channel {1}: {2}", currentRow, currentChannel, e.Message); OnReportProgress(new EventReportProgressArgs(errorMessage, MsgType.ERROR)); } } // panning column if (isEffectCommandUsed == false && trackLineData.Panning != null) { try { byte[] values = modUtils.TransposePanningToCommandEffect(trackLineData.Panning, sampleNumber, currentChannel - 1, period != 0); if ((values[0] + values[1]) > 0) { isEffectCommandUsed = true; effectNum = values[0]; effectVal = values[1]; } } catch (ConversionException e) { string errorMessage = string.Format("row {0}, channel {1}: {2}", currentRow, currentChannel, e.Message); OnReportProgress(new EventReportProgressArgs(errorMessage, MsgType.ERROR)); } } // apply global commands to the current effect column if (isMasterTrackCommandUsed && !isEffectCommandUsed) { isEffectCommandUsed = true; effectNum = masterTrackCommand[0]; effectVal = masterTrackCommand[1]; //currentMasterTrackColumnToParse++; //currentMasterTrackIndex++; isMasterTrackCommandUsed = false; } } } data[offset] = (byte)((sampleNumber & 0xf0) + ((period & 0xf00) >> 8)); data[offset + 1] = (byte)(System.Convert.ToByte(period & 0xff)); data[offset + 2] = (byte)(System.Convert.ToByte(((sampleNumber & 0xf) << 4) + (effectNum))); data[offset + 3] = (byte)(effectVal); } return(data); }
private byte[] GetPatternData(PatternData patternData, InstrumentData[] instruments, int numChannels, int numMasterTrackColumns) { byte noteBit = 1; byte instrumentBit = 2; byte volumeColBit = 4; byte effectTypeBit = 8; byte effectParamBit = 16; byte emptyBit = 128; byte allValuesFilledBit = (byte)(noteBit + instrumentBit + volumeColBit + effectTypeBit + effectParamBit + emptyBit); MemoryStream patternDataStream = new MemoryStream(); // A Carl Corcoran idea, useful to know which is the last sample played by an x channel System.Collections.Generic.Dictionary <int, SampleData?> playingSamplesMap = new System.Collections.Generic.Dictionary <int, SampleData?>(); byte[] masterTrackCommand = new byte[2]; bool isMasterTrackCommandUsed = false; // force number of mastertrack columns to parse at x value, therefore any column beyond the x value will be ignored // NOTE: if applied parseOnlyGlobalVolumeFromMT, other effect but Global Volume will be ignored const int maxMasterTrackColumnToParse = 1; int numMasterTrackColumnsToParse = maxMasterTrackColumnToParse; // numMasterTrackColumns -- count of MT columns in module // numMasterTrackColumnsToParse -- count of MT columns to parse if (numMasterTrackColumnsToParse > numMasterTrackColumns) { numMasterTrackColumnsToParse = numMasterTrackColumns; } // parse only global volume from MasterTrack const bool parseOnlyGlobalVolumeFromMT = false; int currentMasterTrackIndex = 0; int masterTrackIndexLimitForCurrentRow = 0; for (int i = 0; i < patternData.TracksLineData.Length; i++) { xmUtils.ComputeTickPerRowForCurrentLine(patternData.TracksLineData, i, numChannels); int currentRow = i / numChannels; int currentChannel = i % numChannels + 1; if (currentChannel == 1) { if (isMasterTrackCommandUsed) { string errorMessage = string.Format("row {0}, channel {1}: {2}", currentRow, currentChannel, "Some MasterTrack command were not used due to missing free command effects slots"); OnReportProgress(new EventReportProgressArgs(errorMessage, MsgType.ERROR)); } //currentMasterTrackColumnToParse = 0; isMasterTrackCommandUsed = false; currentMasterTrackIndex = currentRow * numMasterTrackColumns; masterTrackIndexLimitForCurrentRow = currentMasterTrackIndex + numMasterTrackColumnsToParse; } while (currentMasterTrackIndex < masterTrackIndexLimitForCurrentRow && !isMasterTrackCommandUsed) { MasterTrackLineData masterTrackLineData = patternData.MasterTrackLineData[currentMasterTrackIndex]; if (masterTrackLineData.IsSet) { masterTrackCommand = xmUtils.GetCommandsFromMasterTrack(masterTrackLineData.EffectNumber, masterTrackLineData.EffectValue, parseOnlyGlobalVolumeFromMT); if (masterTrackCommand[0] + masterTrackCommand[1] > 0) { isMasterTrackCommandUsed = true; //break; } } //currentMasterTrackColumnToParse++; currentMasterTrackIndex++; } TrackLineData trackLineData = patternData.TracksLineData[i]; if (trackLineData.IsSet || isMasterTrackCommandUsed) { byte compressionValue = emptyBit; byte xmNote = 0; byte xmInstrument = 0; byte xmVolume = 0; byte xmEffectNumber = 0; byte xmEffectValue = 0; bool isEffectCommandUsed = false; bool isVolumeCommandUsed = false; bool isPanningCommandUsed = false; if (trackLineData.Note != null) { try { xmNote = XMUtils.GetXMNote(trackLineData.Note); compressionValue = (byte)(compressionValue + noteBit); } catch (ConversionException e) { string errorMessage = string.Format("row {0}, channel {1}: {2}", currentRow, currentChannel, e.Message); OnReportProgress(new EventReportProgressArgs(errorMessage, MsgType.ERROR)); } } if (trackLineData.Instrument != null) { compressionValue = (byte)(compressionValue + instrumentBit); xmInstrument = (byte)(Int16.Parse(trackLineData.Instrument, System.Globalization.NumberStyles.HexNumber) + 1); if (xmNote != 0) { int xmSample = xmUtils.GetPlayedSampleFromKeymap(xmNote, xmInstrument); // figure out which sample will play for this. if (instruments[xmInstrument - 1].Samples.Length > xmSample) { playingSamplesMap[currentChannel] = instruments[xmInstrument - 1].Samples[xmSample]; } } } // the currently playing sample in the channel SampleData?currentlyPlayingSample = null; if (playingSamplesMap.ContainsKey(currentChannel)) { currentlyPlayingSample = playingSamplesMap[currentChannel]; } int sampleDefaultVolume = currentlyPlayingSample != null ? currentlyPlayingSample.Value.DefaultVolume : maxSampleVolume; float sampleVolume = 1.0f; if (currentlyPlayingSample != null) { sampleVolume = currentlyPlayingSample.Value.Volume; } if (trackLineData.EffectNumber != null) { try { byte[] values = xmUtils.GetXMEffect(trackLineData.EffectNumber, trackLineData.EffectValue, xmNote, xmInstrument); if ((values[0] + values[1]) > 0) { isEffectCommandUsed = true; xmEffectNumber = values[0]; xmEffectValue = values[1]; } } catch (ConversionException e) { string errorMessage = string.Format("row {0}, channel {1}: {2}", currentRow, currentChannel, e.Message); OnReportProgress(new EventReportProgressArgs(errorMessage, MsgType.ERROR)); } } // volume column (volume column got priority before panning) if (trackLineData.Volume != null) { xmVolume = xmUtils.GetVolumeColumnEffectFromVolume(trackLineData.Volume); isVolumeCommandUsed = xmVolume > 0; if (isVolumeCommandUsed == false && isEffectCommandUsed == false) { // transpose possible parseable command from volume to effect columns // G|U|D|I|O|B|Q|R|Y|C byte[] values = xmUtils.TransposeVolumeToCommandEffect(trackLineData.Volume); if ((values[0] + values[1]) > 0) { isEffectCommandUsed = true; xmEffectNumber = values[0]; xmEffectValue = values[1]; } } } // only with VOLUME_SCALING_MODE = COLUMN if (Settings.VolumeScalingMode == VOLUME_SCALING_MODE.COLUMN) { bool sampleNeedsVolumeScaling = currentlyPlayingSample != null && currentlyPlayingSample.Value.Volume != 1.0; bool doesTriggerSample = trackLineData.Note != null && trackLineData.Instrument != null; // if volume column command is used, then scale it if (sampleNeedsVolumeScaling && isVolumeCommandUsed && XMExtras.IsVolumeSetOnVolumeColumn(xmVolume)) { try { xmVolume = XMExtras.ScaleVolumeFromVolumeCommand(xmVolume, sampleVolume); } catch (ConversionException e) { string errorMessage = string.Format("row {0}, channel {1}: {2}", currentRow, currentChannel, e.Message); OnReportProgress(new EventReportProgressArgs(errorMessage, MsgType.ERROR)); } sampleNeedsVolumeScaling = false; } // if effect command is used, then scale it if (sampleNeedsVolumeScaling && isEffectCommandUsed && XMExtras.IsVolumeSetOnEffectColumn(xmEffectNumber)) { try { xmEffectValue = XMExtras.ScaleVolumeFromEffectCommand(xmEffectValue, sampleVolume); } catch (ConversionException e) { string errorMessage = string.Format("row {0}, channel {1}: {2}", currentRow, currentChannel, e.Message); OnReportProgress(new EventReportProgressArgs(errorMessage, MsgType.ERROR)); } sampleNeedsVolumeScaling = false; } // if sample is triggered and needs volume scaling, check for any free slot if (sampleNeedsVolumeScaling && doesTriggerSample) { // the real sample volume is relative with the default volume sampleVolume *= (float)currentlyPlayingSample.Value.DefaultVolume / (float)maxSampleVolume; // try to fill on volume column first if (isVolumeCommandUsed == false) { try { xmVolume = XMExtras.ScaleVolumeFromVolumeCommand(sampleVolume); isVolumeCommandUsed = true; } catch (ConversionException e) { string errorMessage = string.Format("row {0}, channel {1}: {2}", currentRow, currentChannel, e.Message); OnReportProgress(new EventReportProgressArgs(errorMessage, MsgType.ERROR)); } sampleNeedsVolumeScaling = false; } // try to fill on effect column if (sampleNeedsVolumeScaling && isEffectCommandUsed == false) { byte[] values = new byte[2]; // transpose possible parseable command from volume to effect columns // G|U|D|J|K|Q|B|R|Y|C try { values = XMExtras.ScaleVolumeFromEffectCommand(sampleVolume); } catch (Exception e) { string errorMessage = string.Format("row {0}, channel {1}: {2}", currentRow, currentChannel, e.Message); OnReportProgress(new EventReportProgressArgs(errorMessage, MsgType.ERROR)); } if ((values[0] + values[1]) > 0) { isEffectCommandUsed = true; xmEffectNumber = values[0]; xmEffectValue = values[1]; } sampleNeedsVolumeScaling = false; } } // if still sample needs scaling a conversion error is thrown if (sampleNeedsVolumeScaling) { // no empty slot free, a log error conversion is thrown string errorMessage = string.Format("row {0}, channel {1}: {2}", currentRow, currentChannel, "Cannot apply scaled volume for this channel due to missing free slots"); OnReportProgress(new EventReportProgressArgs(errorMessage, MsgType.ERROR)); } } // delay column if (trackLineData.Delay != null) { if (isEffectCommandUsed == false) { byte[] values = xmUtils.TransposeDelayToCommandEffect(trackLineData.Delay); isEffectCommandUsed = true; xmEffectNumber = values[0]; xmEffectValue = values[1]; } else { // no empty slot free, a log error conversion is thrown string errorMessage = string.Format("row {0}, channel {1}: {2}", currentRow, currentChannel, "Cannot apply delay for this channel due to missing free slots"); OnReportProgress(new EventReportProgressArgs(errorMessage, MsgType.ERROR)); } } // panning column if (trackLineData.Panning != null) { if (isVolumeCommandUsed == false) { xmVolume = xmUtils.GetVolumeColumnEffectFromPanning(trackLineData.Panning); isPanningCommandUsed = xmVolume > 0; } if (isPanningCommandUsed == false && isEffectCommandUsed == false) { byte[] values = xmUtils.TransposePanningToCommandEffect(trackLineData.Panning); if ((values[0] + values[1]) > 0) { isEffectCommandUsed = true; xmEffectNumber = values[0]; xmEffectValue = values[1]; } } } // apply global commands to the current effect column if (isMasterTrackCommandUsed && !isEffectCommandUsed) { isEffectCommandUsed = true; xmEffectNumber = masterTrackCommand[0]; xmEffectValue = masterTrackCommand[1]; //currentMasterTrackColumnToParse++; isMasterTrackCommandUsed = false; } // xm volume column binary switch if (isPanningCommandUsed || isVolumeCommandUsed) { compressionValue = (byte)(compressionValue + volumeColBit); } if (xmEffectNumber > 0) { compressionValue = (byte)(compressionValue + effectTypeBit); } if (xmEffectValue > 0) { compressionValue = (byte)(compressionValue + effectParamBit); } // this might require a little explanation. // row/track data, wherever is not completely filled with note, instrument, volume col, // effect type, effect value // is packaged in this way: // the first byte means the type of values, and from second the values to put. // an empty row/track data byte is byte value 128 // the values order, starting from the less significant bit are // // 1 1 1 1 1 // note instrument volume col effect type effect value // // so, for example, a value of 26 in bit is 11010 that means note, instrument and effect type filled. // Therefore the first byte will be 154 (128 + 26) // See the specs for a better idea // writes the package byte only if not all values are valorized if (compressionValue != allValuesFilledBit) { patternDataStream.WriteByte(compressionValue); } // checks for every bit of package byte to understand which values has to store in if (((compressionValue & 0x1)) > 0) { patternDataStream.WriteByte(xmNote); } if (((compressionValue & 0x3) >> 1) > 0) { patternDataStream.WriteByte(xmInstrument); } if (((compressionValue & 0x7) >> 2) > 0) { patternDataStream.WriteByte(xmVolume); } if (((compressionValue & 0xf) >> 3) > 0) { patternDataStream.WriteByte(xmEffectNumber); } if (((compressionValue & 0x1f) >> 4) > 0) { patternDataStream.WriteByte(xmEffectValue); } } else { patternDataStream.WriteByte(emptyBit); } } return(patternDataStream.ToArray()); }