Beispiel #1
0
        private MasterTrackLineData[] GetMasterTrackDataFromPattern(Pattern renoisePattern, int numColumns)
        {
            int numRows = renoisePattern.NumberOfLines;
            int totalMasterTracksData = numRows * numColumns;

            MasterTrackLineData[] masterTrackStructsData = new MasterTrackLineData[totalMasterTracksData];

            if (renoisePattern.Tracks.PatternMasterTrack[0].Lines != null)
            {
                for (int cl = 0; cl < renoisePattern.Tracks.PatternMasterTrack[0].Lines.Length; cl++)
                {
                    for (int cc = 0; cc < renoisePattern.Tracks.PatternMasterTrack[0].Lines[cl].EffectColumns.EffectColumn.Length; cc++)
                    {
                        // row index starts from 0
                        int row = renoisePattern.Tracks.PatternMasterTrack[0].Lines[cl].index;

                        if (row >= numRows)
                        {
                            continue;
                        }

                        string effectnumber = null;
                        string effectvalue  = null;

                        effectnumber = renoisePattern.Tracks.PatternMasterTrack[0].Lines[cl].EffectColumns.EffectColumn[cc].Number;
                        effectvalue  = renoisePattern.Tracks.PatternMasterTrack[0].Lines[cl].EffectColumns.EffectColumn[cc].Value;

                        if ((effectnumber == null && effectvalue == null) == false)
                        {
                            int posInPattern = (numColumns * row) + cc;

                            masterTrackStructsData[posInPattern].IsSet = true;

                            masterTrackStructsData[posInPattern].EffectNumber = ConvertNullEffectToZero(effectnumber);
                            masterTrackStructsData[posInPattern].EffectValue  = ConvertNullEffectToZero(effectvalue);
                        }
                    }
                }
            }



            return(masterTrackStructsData);
        }
Beispiel #2
0
        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);
        }
Beispiel #3
0
        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());
        }