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; } }
public override unsafe void Export(string outPath) { if (outPath.EndsWith(".wav")) { WAV.ToFile(CreateStreams()[0], outPath); } else { if (_audioSource != DataSource.Empty) { int size = WorkingUncompressed.Length + _audioSource.Length; using (FileStream stream = new FileStream(outPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)) { stream.SetLength(size); using (FileMap map = FileMap.FromStreamInternal(stream, FileMapProtect.ReadWrite, 0, size)) { VoidPtr addr = map.Address; //Write header Memory.Move(addr, WorkingUncompressed.Address, (uint)WorkingUncompressed.Length); //Set the offset to the audio samples (_dataLocation) RSTMHeader *hdr = (RSTMHeader *)addr; hdr->_header._length = WorkingUncompressed.Length + _audioSource.Length; hdr->DATAData->Set(_audioSource.Length + 0x20); addr += WorkingUncompressed.Length; //Append audio samples to the end Memory.Move(addr, _audioSource.Address, (uint)_audioSource.Length); if (outPath.EndsWith(".bcstm")) { ShowADPCMConversionWarning(); byte[] bcstm_temp = CSTMConverter.FromRSTM(hdr); fixed(byte *ptr = bcstm_temp) { Memory.Move(map.Address, ptr, (uint)bcstm_temp.Length); } } else if (outPath.EndsWith(".bfstm")) { ShowADPCMConversionWarning(); byte[] bfstm_temp = FSTMConverter.FromRSTM(hdr); fixed(byte *ptr = bfstm_temp) { Memory.Move(map.Address, ptr, (uint)bfstm_temp.Length); } } } } } else { base.Export(outPath); } } }
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); }
static unsafe IAudioStream[] CreateStreams(RSTMHeader *rstm) { switch ((WaveEncoding)rstm->HEADData->Part1->_format._encoding) { case WaveEncoding.ADPCM: return(ADPCMStream.GetStreams(rstm, rstm->DATAData->Data)); case WaveEncoding.PCM16: return(PCMStream.GetStreams(rstm, rstm->DATAData->Data)); } throw new Exception("RSTMLib does not support decoding RSTM files with this encoding."); }
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; }
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); }
internal static unsafe byte[] FromRSTM(RSTMHeader *rstm) { StrmDataInfo strmDataInfo = *rstm->HEADData->Part1; int channels = strmDataInfo._format._channels; if (strmDataInfo._format._encoding != (byte)WaveEncoding.ADPCM) { throw new NotImplementedException("CSTM export only supports ADPCM encoding."); } // 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 = rstm->_headLength; int seekSize = rstm->_adpcLength; int dataSize = rstm->_dataLength; //Create byte array byte[] array = new byte[rstmSize + infoSize + seekSize + dataSize]; fixed(byte *address = array) { //Get section pointers CSTMHeader * cstm = (CSTMHeader *)address; CSTMINFOHeader *info = (CSTMINFOHeader *)((byte *)cstm + rstmSize); CSTMSEEKHeader *seek = (CSTMSEEKHeader *)((byte *)info + infoSize); CSTMDATAHeader *data = (CSTMDATAHeader *)((byte *)seek + seekSize); //Initialize sections cstm->Set(infoSize, seekSize, dataSize); info->Set(infoSize, channels); seek->Set(seekSize); data->Set(dataSize); //Set HEAD data info->_dataInfo = new CSTMDataInfo(strmDataInfo); //Create one ADPCMInfo for each channel IntPtr * adpcData = stackalloc IntPtr[channels]; CSTMADPCMInfo **pAdpcm = (CSTMADPCMInfo **)adpcData; for (int i = 0; i < channels; i++) { *(pAdpcm[i] = info->GetChannelInfo(i)) = new CSTMADPCMInfo(*rstm->HEADData->GetChannelInfo(i)); } bshort *seekFrom = (bshort *)rstm->ADPCData->Data; short * seekTo = (short *)seek->Data; for (int i = 0; i < seek->_length / 2 - 8; i++) { *(seekTo++) = *(seekFrom++); } VoidPtr dataFrom = rstm->DATAData->Data; VoidPtr dataTo = data->Data; Memory.Move(dataTo, dataFrom, (uint)data->_length - 8); } return(array); }
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 }
internal PCMStream(RSTMHeader *header, int channels, int startChannel, void *audioSource) { StrmDataInfo *info = header->HEADData->Part1; if (channels > 2) { throw new NotImplementedException("Cannot load PCM16 audio with more than 2 channels"); } if (info->_format._channels < channels) { throw new Exception("Not enough channels"); } int size = info->_numSamples * channels * sizeof(short); _allocatedHGlobal = Marshal.AllocHGlobal(size); byte *fromL = (byte *)audioSource; byte *fromR = fromL; byte *to = (byte *)_allocatedHGlobal; bool stereo = channels == 2; for (int block = 0; block < info->_numBlocks; block++) { int bs = (block == info->_numBlocks - 1) ? info->_lastBlockSize : info->_blockSize; if (block == 0) { fromL += (bs * startChannel); fromR += (bs * startChannel); } else { fromL += bs * (info->_format._channels - channels); fromR += bs * (info->_format._channels - channels); } if (stereo) { fromR += bs; } for (int i = 0; i < bs; i += 2) { to[0] = fromL[1]; to[1] = fromL[0]; fromL += 2; to += 2; if (stereo) { to[0] = fromR[1]; to[1] = fromR[0]; fromR += 2; to += 2; } } if (stereo) { fromL += bs; } } _bps = 16; _numChannels = channels; _frequency = info->_sampleRate; _numSamples = info->_numSamples; _source = (short *)_allocatedHGlobal; _samplePos = 0; _looped = info->_format._looped != 0; _loopStart = info->_loopStartSample; _loopEnd = _numSamples; }
internal PCMStream(RSTMHeader *header, int channels, int startChannel, void *audioSource) { StrmDataInfo *info = header->HEADData->Part1; if (info->_format._channels < startChannel + channels) { throw new Exception("Not enough channels"); } List <short[]>[] blocksByChannel = new List <short[]> [info->_format._channels]; for (int i = 0; i < blocksByChannel.Length; i++) { blocksByChannel[i] = new List <short[]>(); } byte *from = (byte *)audioSource; for (int block = 0; block < info->_numBlocks; block++) { int blockSize = (block == info->_numBlocks - 1) ? info->_lastBlockSize : info->_blockSize; int blockTotal = (block == info->_numBlocks - 1) ? info->_lastBlockTotal : info->_blockSize; for (int channel = 0; channel < info->_format._channels; channel++) { short[] b = new short[blockSize / sizeof(short)]; Marshal.Copy((IntPtr)from, b, 0, b.Length); from += blockTotal; blocksByChannel[channel].Add(b); } } List <List <short[]> > blocksToUseByChannel = new List <List <short[]> >(); for (int i = 0; i < channels; i++) { blocksToUseByChannel.Add(blocksByChannel[startChannel + i]); } int size = info->_numSamples * channels * sizeof(short); //int size2 = blocksToUseByChannel.SelectMany(blocks => blocks.Select(block => block.Length)).Sum() * sizeof(short); //if (size != size2) //{ // throw new Exception($"{size} != {size2}"); //} _allocatedHGlobal = Marshal.AllocHGlobal(size); short *toPtr = (short *)_allocatedHGlobal; //short* endPtr = (short*)(_allocatedHGlobal + size); int blockCount = blocksToUseByChannel.Select(b => b.Count).Distinct().Single(); for (int blockIndex = 0; blockIndex < blockCount; blockIndex++) { List <short[]> blocks = blocksToUseByChannel.Select(l => l[blockIndex]).ToList(); int blockShorts = blocks.Select(a => a.Length).Distinct().Single(); for (int i = 0; i < blockShorts; i++) { foreach (short[] block in blocks) { *toPtr++ = block[i].Reverse(); } } } //if (toPtr != endPtr) throw new Exception($"Not all data filled ({(long)_allocatedHGlobal}, {(long)toPtr}, {(long)endPtr})"); _bps = 16; _numChannels = channels; _frequency = info->_sampleRate; _numSamples = info->_numSamples; _source = (short *)_allocatedHGlobal; _samplePos = 0; _looped = info->_format._looped != 0; _loopStart = info->_loopStartSample; _loopEnd = _numSamples; }