public ADPCMStream(RSTMHeader* pRSTM, VoidPtr dataAddr)
        {
            HEADHeader* pHeader = pRSTM->HEADData;
            StrmDataInfo* part1 = pHeader->Part1;
            bshort* ynCache = (bshort*)pRSTM->ADPCData->Data;
            byte* sPtr;
            short[][] coefs;
            ADPCMInfo* info;
            int loopBlock, loopChunk;
            short yn1 = 0, yn2 = 0;

            _numChannels = part1->_format._channels;
            _isLooped = part1->_format._looped != 0;
            _sampleRate = part1->_sampleRate;
            _numSamples = part1->_numSamples;
            _numBlocks = part1->_numBlocks;
            _blockLen = part1->_blockSize;
            _loopStartSample = part1->_loopStartSample;
            _lastBlockSamples = part1->_lastBlockSamples;
            _lastBlockSize = part1->_lastBlockTotal;
            _samplesPerBlock = part1->_samplesPerBlock;
            _loopEndSample = _numSamples;

            _blockStates = new ADPCMState[_numChannels, _numBlocks];
            _currentStates = new ADPCMState[_numChannels];
            _loopStates = new ADPCMState[_numChannels];
            coefs = new short[_numChannels][];

            loopBlock = _loopStartSample / _samplesPerBlock;
            loopChunk = (_loopStartSample - (loopBlock * _samplesPerBlock)) / 14;
            sPtr = (byte*)dataAddr + (loopBlock * _blockLen * _numChannels) + (loopChunk * 8);

            //Get channel info
            for (int i = 0; i < _numChannels; i++)
            {
                info = pHeader->GetChannelInfo(i);
                //Get channel coefs
                coefs[i] = info->Coefs;
                //Fill loop state
                _loopStates[i] = new ADPCMState(sPtr, info->_lps, info->_lyn1, info->_lyn2, coefs[i]);
                //Advance source pointer for next channel
                sPtr += _blockLen;
            }

            //Fill block states in a linear fashion
            sPtr = (byte*)dataAddr;
            for (int sIndex = 0, bIndex = 0; sIndex < _numSamples; sIndex += _samplesPerBlock, bIndex++)
                for (int cIndex = 0; cIndex < _numChannels; cIndex++)
                {
                    if (bIndex > 0) //yn values will be zero if first block
                    {
                        yn1 = *ynCache++;
                        yn2 = *ynCache++;
                    }
                    //Get block state
                    _blockStates[cIndex, bIndex] = new ADPCMState(sPtr, *sPtr, yn1, yn2, coefs[cIndex]); //Use ps from data stream
                    //Advance address
                    sPtr += (bIndex == _numBlocks - 1) ? _lastBlockSize : _blockLen;
                }
        }
        internal static unsafe byte[] ToRSTM(FSTMHeader *fstm)
        {
            FSTMDataInfo fstmDataInfo = fstm->INFOData->_dataInfo;
            int          channels     = fstmDataInfo._format._channels;

            // Get section sizes from the BRSTM - BFSTM is such a similar format that we can assume the sizes will match.
            int rstmSize = 0x40;
            int infoSize = fstm->_infoBlockSize;
            int seekSize = fstm->_seekBlockSize;
            int dataSize = fstm->_dataBlockSize;

            //Create byte array
            byte[] array = new byte[rstmSize + infoSize + seekSize + dataSize];

            fixed(byte *address = array)
            {
                //Get section pointers
                RSTMHeader *    rstm = (RSTMHeader *)address;
                HEADHeader *    info = (HEADHeader *)((byte *)rstm + rstmSize);
                ADPCHeader *    seek = (ADPCHeader *)((byte *)info + infoSize);
                RSTMDATAHeader *data = (RSTMDATAHeader *)((byte *)seek + seekSize);

                //Initialize sections
                rstm->Set(infoSize, seekSize, dataSize);
                info->Set(infoSize, channels, (WaveEncoding)fstm->INFOData->_dataInfo._format._encoding);
                seek->Set(seekSize);
                data->Set(dataSize);

                //Set HEAD data
                *info->Part1 = new StrmDataInfo(fstmDataInfo, rstmSize + infoSize + seekSize + 0x20);

                //Create one ADPCMInfo for each channel
                IntPtr *    adpcData = stackalloc IntPtr[channels];
                ADPCMInfo **pAdpcm   = (ADPCMInfo **)adpcData;

                for (int i = 0; i < channels; i++)
                {
                    *(pAdpcm[i] = info->GetChannelInfo(i)) = new ADPCMInfo(*fstm->INFOData->GetChannelInfo(i));
                }

                bshort *seekFrom = (bshort *)fstm->SEEKData->Data;
                bshort *seekTo   = (bshort *)seek->Data;

                for (int i = 0; i < seek->_length / 2 - 8; i++)
                {
                    *(seekTo++) = *(seekFrom++);
                }

                VoidPtr dataFrom = fstm->DATAData->Data;
                VoidPtr dataTo   = data->Data;

                Memory.Move(dataTo, dataFrom, (uint)data->_length - 8);
            }

            return(array);
        }
        public static ADPCMStream[] GetStreams(RSTMHeader* pRSTM, VoidPtr dataAddr)
        {
            HEADHeader* pHeader = pRSTM->HEADData;
            StrmDataInfo* part1 = pHeader->Part1;
            int c = part1->_format._channels;
            ADPCMStream[] streams = new ADPCMStream[c.RoundUpToEven() / 2];

            for (int i = 0; i < streams.Length; i++)
            {
                int x = (i + 1) * 2 <= c ? 2 : 1;
                streams[i] = new ADPCMStream(pRSTM, x, i * 2, dataAddr);
            }

            return streams;
        }
Esempio n. 4
0
        public static unsafe FileMap Encode(IAudioStream stream, IProgressTracker progress)
        {
            int    tmp;
            bool   looped   = stream.IsLooping;
            int    channels = stream.Channels;
            int    samples;
            int    blocks;
            int    sampleRate = stream.Frequency;
            int    lbSamples, lbSize, lbTotal;
            int    loopPadding, loopStart, totalSamples;
            short *tPtr;

            if (looped)
            {
                loopStart = stream.LoopStartSample;
                samples   = stream.LoopEndSample; //Set sample size to end sample. That way the audio gets cut off when encoding.

                //If loop point doesn't land on a block, pad the stream so that it does.
                if ((tmp = loopStart % 0x3800) != 0)
                {
                    loopPadding = 0x3800 - tmp;
                    loopStart  += loopPadding;
                }
                else
                {
                    loopPadding = 0;
                }

                totalSamples = loopPadding + samples;
            }
            else
            {
                loopPadding  = loopStart = 0;
                totalSamples = samples = stream.Samples;
            }

            if (progress != null)
            {
                progress.Begin(0, totalSamples * channels * 3, 0);
            }

            blocks = (totalSamples + 0x37FF) / 0x3800;

            //Initialize stream info
            if ((tmp = totalSamples % 0x3800) != 0)
            {
                lbSamples = tmp;
                lbSize    = (lbSamples + 13) / 14 * 8;
                lbTotal   = lbSize.Align(0x20);
            }
            else
            {
                lbSamples = 0x3800;
                lbTotal   = lbSize = 0x2000;
            }

            //Get section sizes
            int rstmSize = 0x40;
            int headSize = (0x68 + (channels * 0x40)).Align(0x20);
            int adpcSize = ((blocks - 1) * 4 * channels + 0x10).Align(0x20);
            int dataSize = ((blocks - 1) * 0x2000 + lbTotal) * channels + 0x20;

            //Create file map
            FileMap map = FileMap.FromTempFile(rstmSize + headSize + adpcSize + dataSize);

            //Get section pointers
            RSTMHeader *    rstm = (RSTMHeader *)map.Address;
            HEADHeader *    head = (HEADHeader *)((int)rstm + rstmSize);
            ADPCHeader *    adpc = (ADPCHeader *)((int)head + headSize);
            RSTMDATAHeader *data = (RSTMDATAHeader *)((int)adpc + adpcSize);

            //Initialize sections
            rstm->Set(headSize, adpcSize, dataSize);
            head->Set(headSize, channels);
            adpc->Set(adpcSize);
            data->Set(dataSize);

            //Set HEAD data
            StrmDataInfo *part1 = head->Part1;

            part1->_format            = new AudioFormatInfo(2, (byte)(looped ? 1 : 0), (byte)channels, 0);
            part1->_sampleRate        = (ushort)sampleRate;
            part1->_blockHeaderOffset = 0;
            part1->_loopStartSample   = loopStart;
            part1->_numSamples        = totalSamples;
            part1->_dataOffset        = rstmSize + headSize + adpcSize + 0x20;
            part1->_numBlocks         = blocks;
            part1->_blockSize         = 0x2000;
            part1->_samplesPerBlock   = 0x3800;
            part1->_lastBlockSize     = lbSize;
            part1->_lastBlockSamples  = lbSamples;
            part1->_lastBlockTotal    = lbTotal;
            part1->_dataInterval      = 0x3800;
            part1->_bitsPerSample     = 4;

            //Create one ADPCMInfo for each channel
            int *       adpcData = stackalloc int[channels];
            ADPCMInfo **pAdpcm   = (ADPCMInfo **)adpcData;

            for (int i = 0; i < channels; i++)
            {
                *(pAdpcm[i] = head->GetChannelInfo(i)) = new ADPCMInfo()
                {
                    _pad = 0
                }
            }
            ;

            //Create buffer for each channel
            int *   bufferData     = stackalloc int[channels];
            short **channelBuffers = (short **)bufferData;
            int     bufferSamples  = totalSamples + 2; //Add two samples for initial yn values

            for (int i = 0; i < channels; i++)
            {
                channelBuffers[i] = tPtr = (short *)Marshal.AllocHGlobal(bufferSamples * 2); //Two bytes per sample

                //Zero padding samples and initial yn values
                for (int x = 0; x < (loopPadding + 2); x++)
                {
                    *tPtr++ = 0;
                }
            }

            //Fill buffers
            stream.SamplePosition = 0;
            short *sampleBuffer = stackalloc short[channels];

            for (int i = 2; i < bufferSamples; i++)
            {
                if (stream.SamplePosition == stream.LoopEndSample && looped)
                {
                    stream.SamplePosition = stream.LoopStartSample;
                }

                stream.ReadSamples(sampleBuffer, 1);
                for (int x = 0; x < channels; x++)
                {
                    channelBuffers[x][i] = sampleBuffer[x];
                }
            }

            //Calculate coefs
            for (int i = 0; i < channels; i++)
            {
                AudioConverter.CalcCoefs(channelBuffers[i] + 2, totalSamples, (short *)pAdpcm[i], progress);
            }

            //Encode blocks
            byte *  dPtr = (byte *)data->Data;
            bshort *pyn  = (bshort *)adpc->Data;

            for (int sIndex = 0, bIndex = 1; sIndex < totalSamples; sIndex += 0x3800, bIndex++)
            {
                int blockSamples = Math.Min(totalSamples - sIndex, 0x3800);
                for (int x = 0; x < channels; x++)
                {
                    short *sPtr = channelBuffers[x] + sIndex;

                    //Set block yn values
                    if (bIndex != blocks)
                    {
                        *pyn++ = sPtr[0x3801];
                        *pyn++ = sPtr[0x3800];
                    }

                    //Encode block (include yn in sPtr)
                    AudioConverter.EncodeBlock(sPtr, blockSamples, dPtr, (short *)pAdpcm[x]);

                    //Set initial ps
                    if (bIndex == 1)
                    {
                        pAdpcm[x]->_ps = *dPtr;
                    }

                    //Advance output pointer
                    if (bIndex == blocks)
                    {
                        //Fill remaining
                        dPtr += lbSize;
                        for (int i = lbSize; i < lbTotal; i++)
                        {
                            *dPtr++ = 0;
                        }
                    }
                    else
                    {
                        dPtr += 0x2000;
                    }
                }

                if (progress != null)
                {
                    if ((sIndex % 0x3800) == 0)
                    {
                        progress.Update(progress.CurrentValue + (0x7000 * channels));
                    }
                }
            }

            //Reverse coefs
            for (int i = 0; i < channels; i++)
            {
                short *p = pAdpcm[i]->_coefs;
                for (int x = 0; x < 16; x++, p++)
                {
                    *p = p->Reverse();
                }
            }

            //Write loop states
            if (looped)
            {
                //Can't we just use block states?
                int loopBlock = loopStart / 0x3800;
                int loopChunk = (loopStart - (loopBlock * 0x3800)) / 14;
                dPtr = (byte *)data->Data + (loopBlock * 0x2000 * channels) + (loopChunk * 8);
                tmp  = (loopBlock == blocks - 1) ? lbTotal : 0x2000;

                for (int i = 0; i < channels; i++, dPtr += tmp)
                {
                    //Use adjusted samples for yn values
                    tPtr             = channelBuffers[i] + loopStart;
                    pAdpcm[i]->_lps  = *dPtr;
                    pAdpcm[i]->_lyn2 = *tPtr++;
                    pAdpcm[i]->_lyn1 = *tPtr;
                }
            }

            //Free memory
            for (int i = 0; i < channels; i++)
            {
                Marshal.FreeHGlobal((IntPtr)channelBuffers[i]);
            }

            if (progress != null)
            {
                progress.Finish();
            }

            return(map);
        }
    }
        internal static unsafe byte[] ToRSTM(CSTMHeader *cstm)
        {
            CSTMDataInfo cstmDataInfo = cstm->INFOData->_dataInfo;
            int          channels     = cstmDataInfo._format._channels;

            if (cstm->_seekBlockRef._type != CSTMReference.RefType.SeekBlock)
            {
                throw new Exception("BrawlLib does not recognize this type of CSTM file (the SEEK block is missing or in an unexpected location.)");
            }

            // Get section sizes from the BRSTM - BCSTM is such a similar format that we can assume the sizes will match.
            int rstmSize = 0x40;
            int infoSize = cstm->_infoBlockSize;
            int seekSize = cstm->_seekBlockSize;
            int dataSize = cstm->_dataBlockSize;

            //Create byte array
            byte[] array = new byte[rstmSize + infoSize + seekSize + dataSize];

            fixed(byte *address = array)
            {
                //Get section pointers
                RSTMHeader *    rstm = (RSTMHeader *)address;
                HEADHeader *    info = (HEADHeader *)((byte *)rstm + rstmSize);
                ADPCHeader *    seek = (ADPCHeader *)((byte *)info + infoSize);
                RSTMDATAHeader *data = (RSTMDATAHeader *)((byte *)seek + seekSize);

                //Initialize sections
                rstm->Set(infoSize, seekSize, dataSize);
                info->Set(infoSize, channels, (WaveEncoding)cstm->INFOData->_dataInfo._format._encoding);
                seek->Set(seekSize);
                data->Set(dataSize);

                //Set HEAD data
                *info->Part1 = new StrmDataInfo(cstmDataInfo, rstmSize + infoSize + seekSize + 0x20);

                //Create one ADPCMInfo for each channel
                IntPtr *    adpcData = stackalloc IntPtr[channels];
                ADPCMInfo **pAdpcm   = (ADPCMInfo **)adpcData;

                for (int i = 0; i < channels; i++)
                {
                    *(pAdpcm[i] = info->GetChannelInfo(i)) = new ADPCMInfo(*cstm->INFOData->GetChannelInfo(i));
                }

                bshort *seekFrom = (bshort *)cstm->SEEKData->Data;
                short * seekTo   = (short *)seek->Data;

                for (int i = 0; i < seek->_length / 2 - 8; i++)
                {
                    *(seekTo++) = *(seekFrom++);
                }

                VoidPtr dataFrom = cstm->DATAData->Data;
                VoidPtr dataTo   = data->Data;

                Memory.Move(dataTo, dataFrom, (uint)data->_length - 8);
            }

            return(array);
        }
Esempio n. 6
0
        public static unsafe FileMap Encode(IAudioStream stream, IProgressTracker progress, WaveEncoding encoding = WaveEncoding.ADPCM)
#endif
        {
            int    tmp;
            bool   looped   = stream.IsLooping;
            int    channels = stream.Channels;
            int    samples;
            int    blocks;
            int    sampleRate = stream.Frequency;
            int    lbSamples, lbSize, lbTotal;
            int    loopPadding, loopStart, totalSamples;
            short *tPtr;

            int samplesPerBlock = encoding == WaveEncoding.ADPCM ? 0x3800
                : encoding == WaveEncoding.PCM16 ? 0x1000
                : 0;

            if (samplesPerBlock == 0)
            {
                throw new ArgumentException("Encoding must be ADPCM or PCM16");
            }

            if (looped)
            {
                loopStart = stream.LoopStartSample;
                samples   = stream.LoopEndSample; //Set sample size to end sample. That way the audio gets cut off when encoding.

                //If loop point doesn't land on a block, pad the stream so that it does.
                if ((tmp = loopStart % samplesPerBlock) != 0)
                {
                    loopPadding = samplesPerBlock - tmp;
                    loopStart  += loopPadding;
                }
                else
                {
                    loopPadding = 0;
                }

                totalSamples = loopPadding + samples;
            }
            else
            {
                loopPadding  = loopStart = 0;
                totalSamples = samples = stream.Samples;
            }

            if (progress != null)
            {
                progress.Begin(0, totalSamples * channels * 3, 0);
            }

            blocks = (totalSamples + samplesPerBlock - 1) / samplesPerBlock;

            //Initialize stream info
            if ((tmp = totalSamples % samplesPerBlock) != 0)
            {
                lbSamples = tmp;
                if (encoding == WaveEncoding.ADPCM)
                {
                    lbSize = (lbSamples + 13) / 14 * 8;
                }
                else if (encoding == WaveEncoding.PCM16)
                {
                    lbTotal = lbSize = lbSamples * 2;
                }
                else if (encoding == WaveEncoding.PCM8)
                {
                    lbTotal = lbSize = lbSamples;
                }
                else
                {
                    throw new NotImplementedException();
                }
                lbTotal = lbSize.Align(0x20);
            }
            else
            {
                lbSamples = samplesPerBlock;
                lbTotal   = lbSize = 0x2000;
            }

            //Get section sizes
            int rstmSize = 0x40;
            int headSize = (0x68 + (channels * (encoding == WaveEncoding.ADPCM ? 0x40 : 0x10))).Align(0x20);
            int adpcSize = encoding == WaveEncoding.ADPCM
                ? ((blocks - 1) * 4 * channels + 0x10).Align(0x20)
                : 0;
            int dataSize = ((blocks - 1) * 0x2000 + lbTotal) * channels + 0x20;

#if RSTMLIB
            //Create byte array
            byte[] array = new byte[rstmSize + headSize + adpcSize + dataSize];
            fixed(byte *address = array)
            {
#else
            //Create file map
            FileMap map     = FileMap.FromTempFile(rstmSize + headSize + adpcSize + dataSize);
            VoidPtr address = map.Address;
#endif

                //Get section pointers
                RSTMHeader *    rstm = (RSTMHeader *)address;
                HEADHeader *    head = (HEADHeader *)((byte *)rstm + rstmSize);
                ADPCHeader *    adpc = (ADPCHeader *)((byte *)head + headSize);
                RSTMDATAHeader *data = (RSTMDATAHeader *)((byte *)adpc + adpcSize);

                //Initialize sections
                rstm->Set(headSize, adpcSize, dataSize);
                head->Set(headSize, channels, encoding);
                if (adpcSize > 0)
                {
                    adpc->Set(adpcSize);
                }
                data->Set(dataSize);

                //Set HEAD data
                StrmDataInfo *part1 = head->Part1;
                part1->_format            = new AudioFormatInfo((byte)encoding, (byte)(looped ? 1 : 0), (byte)channels, 0);
                part1->_sampleRate        = (ushort)sampleRate;
                part1->_blockHeaderOffset = 0;
                part1->_loopStartSample   = loopStart;
                part1->_numSamples        = totalSamples;
                part1->_dataOffset        = rstmSize + headSize + adpcSize + 0x20;
                part1->_numBlocks         = blocks;
                part1->_blockSize         = 0x2000;
                part1->_samplesPerBlock   = samplesPerBlock;
                part1->_lastBlockSize     = lbSize;
                part1->_lastBlockSamples  = lbSamples;
                part1->_lastBlockTotal    = lbTotal;
                part1->_dataInterval      = encoding == WaveEncoding.ADPCM ? samplesPerBlock : 0;
                part1->_bitsPerSample     = encoding == WaveEncoding.ADPCM ? 4 : 0;

                if (encoding == WaveEncoding.ADPCM)
                {
                    //Create one ADPCMInfo for each channel
                    int *       adpcData = stackalloc int[channels];
                    ADPCMInfo **pAdpcm   = (ADPCMInfo **)adpcData;
                    for (int i = 0; i < channels; i++)
                    {
                        *(pAdpcm[i] = head->GetChannelInfo(i)) = new ADPCMInfo()
                        {
                            _pad = 0
                        }
                    }
                    ;

                    //Create buffer for each channel
                    int *   bufferData     = stackalloc int[channels];
                    short **channelBuffers = (short **)bufferData;
                    int     bufferSamples  = totalSamples + 2; //Add two samples for initial yn values
                    for (int i = 0; i < channels; i++)
                    {
                        channelBuffers[i] = tPtr = (short *)Marshal.AllocHGlobal(bufferSamples * 2); //Two bytes per sample

                        //Zero padding samples and initial yn values
                        for (int x = 0; x < (loopPadding + 2); x++)
                        {
                            *tPtr++ = 0;
                        }
                    }

                    //Fill buffers
                    stream.SamplePosition = 0;
                    short *sampleBuffer = stackalloc short[channels];

                    for (int i = 2; i < bufferSamples; i++)
                    {
                        if (stream.SamplePosition == stream.LoopEndSample && looped)
                        {
                            stream.SamplePosition = stream.LoopStartSample;
                        }

                        stream.ReadSamples(sampleBuffer, 1);
                        for (int x = 0; x < channels; x++)
                        {
                            channelBuffers[x][i] = sampleBuffer[x];
                        }
                    }

                    //Calculate coefs
                    for (int i = 0; i < channels; i++)
                    {
                        AudioConverter.CalcCoefs(channelBuffers[i] + 2, totalSamples, (short *)pAdpcm[i], progress);
                    }

                    //Encode blocks
                    byte *  dPtr = (byte *)data->Data;
                    bshort *pyn  = (bshort *)adpc->Data;
                    for (int x = 0; x < channels; x++)
                    {
                        *pyn++ = 0;
                        *pyn++ = 0;
                    }

                    for (int sIndex = 0, bIndex = 1; sIndex < totalSamples; sIndex += samplesPerBlock, bIndex++)
                    {
                        int blockSamples = Math.Min(totalSamples - sIndex, samplesPerBlock);
                        for (int x = 0; x < channels; x++)
                        {
                            short *sPtr = channelBuffers[x] + sIndex;

                            //Set block yn values
                            if (bIndex != blocks)
                            {
                                *pyn++ = sPtr[samplesPerBlock + 1];
                                *pyn++ = sPtr[samplesPerBlock];
                            }

                            //Encode block (include yn in sPtr)
                            AudioConverter.EncodeBlock(sPtr, blockSamples, dPtr, (short *)pAdpcm[x]);

                            //Set initial ps
                            if (bIndex == 1)
                            {
                                pAdpcm[x]->_ps = *dPtr;
                            }

                            //Advance output pointer
                            if (bIndex == blocks)
                            {
                                //Fill remaining
                                dPtr += lbSize;
                                for (int i = lbSize; i < lbTotal; i++)
                                {
                                    *dPtr++ = 0;
                                }
                            }
                            else
                            {
                                dPtr += 0x2000;
                            }
                        }

                        if (progress != null)
                        {
                            if ((sIndex % samplesPerBlock) == 0)
                            {
                                progress.Update(progress.CurrentValue + (0x7000 * channels));
                            }
                        }
                    }

                    //Reverse coefs
                    for (int i = 0; i < channels; i++)
                    {
                        short *p = pAdpcm[i]->_coefs;
                        for (int x = 0; x < 16; x++, p++)
                        {
                            *p = p->Reverse();
                        }
                    }

                    //Write loop states
                    if (looped)
                    {
                        //Can't we just use block states?
                        int loopBlock = loopStart / samplesPerBlock;
                        int loopChunk = (loopStart - (loopBlock * samplesPerBlock)) / 14;
                        dPtr = (byte *)data->Data + (loopBlock * 0x2000 * channels) + (loopChunk * 8);
                        tmp  = (loopBlock == blocks - 1) ? lbTotal : 0x2000;

                        for (int i = 0; i < channels; i++, dPtr += tmp)
                        {
                            //Use adjusted samples for yn values
                            tPtr             = channelBuffers[i] + loopStart;
                            pAdpcm[i]->_lps  = *dPtr;
                            pAdpcm[i]->_lyn2 = *tPtr++;
                            pAdpcm[i]->_lyn1 = *tPtr;
                        }
                    }

                    //Free memory
                    for (int i = 0; i < channels; i++)
                    {
                        Marshal.FreeHGlobal((IntPtr)channelBuffers[i]);
                    }
                }
                else if (encoding == WaveEncoding.PCM16)
                {
                    bshort *destPtr = (bshort *)data->Data;
                    for (int i = 0; i < blocks; i++)
                    {
                        int samplesPerChannel = i < blocks - 1
                        ? part1->_samplesPerBlock
                        : part1->_lastBlockSamples;
                        int bytesPerChannel = i < blocks - 1
                        ? part1->_blockSize
                        : part1->_lastBlockTotal;
                        short[] sampleData = new short[channels * bytesPerChannel / sizeof(short)];

                        fixed(short *sampleDataPtr = sampleData)
                        {
                            int read = 0;

                            do
                            {
                                if (stream.SamplePosition == stream.LoopEndSample && looped)
                                {
                                    stream.SamplePosition = stream.LoopStartSample;
                                }
                                int s = stream.ReadSamples(sampleDataPtr + read, samplesPerChannel - read);
                                if (s == 0)
                                {
                                    throw new Exception("No samples could be read from the stream");
                                }
                                read += s;
                            }while (read < samplesPerChannel);
                        }

                        for (int j = 0; j < channels; j++)
                        {
                            for (int k = j; k < sampleData.Length; k += channels)
                            {
                                *(destPtr++) = sampleData[k];
                            }
                        }

                        progress.Update(progress.CurrentValue + (samplesPerChannel * channels * 3));
                    }
                }

                if (progress != null)
                {
                    progress.Finish();
                }

#if RSTMLIB
            }

            return(array);
#else
                return(map);
#endif
        }