// Convert an XMSample to OpenMPT's internal sample representation. void XMSample::ConvertToMPT(ModSample &mptSmp) const //-------------------------------------------------- { mptSmp.Initialize(MOD_TYPE_XM); // Volume mptSmp.nVolume = vol * 4; LimitMax(mptSmp.nVolume, uint16(256)); // Panning mptSmp.nPan = pan; mptSmp.uFlags = CHN_PANNING; // Sample Frequency mptSmp.nFineTune = finetune; mptSmp.RelativeTone = relnote; // Sample Length and Loops mptSmp.nLength = length; mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = mptSmp.nLoopStart + loopLength; if((flags & XMSample::sample16Bit)) { mptSmp.nLength /= 2; mptSmp.nLoopStart /= 2; mptSmp.nLoopEnd /= 2; } if((flags & XMSample::sampleStereo)) { mptSmp.nLength /= 2; mptSmp.nLoopStart /= 2; mptSmp.nLoopEnd /= 2; } if((flags & (XMSample::sampleLoop | XMSample::sampleBidiLoop)) && mptSmp.nLoopStart < mptSmp.nLength && mptSmp.nLoopEnd > mptSmp.nLoopStart) { mptSmp.uFlags.set(CHN_LOOP); if((flags & XMSample::sampleBidiLoop)) { mptSmp.uFlags.set(CHN_PINGPONGLOOP); } } mptSmp.SanitizeLoops(); strcpy(mptSmp.filename, ""); }
bool CopyWavChannel(ModSample &sample, const FileReader &file, size_t channelIndex, size_t numChannels, SampleConversion conv = SampleConversion()) { MPT_ASSERT(sample.GetNumChannels() == 1); MPT_ASSERT(sample.GetElementarySampleSize() == sizeof(typename SampleConversion::output_t)); const size_t offset = channelIndex * sizeof(typename SampleConversion::input_t) * SampleConversion::input_inc; if(sample.AllocateSample() == 0 || !file.CanRead(offset)) { return false; } const mpt::byte *inBuf = file.GetRawData<mpt::byte>(); CopySample<SampleConversion>(reinterpret_cast<typename SampleConversion::output_t*>(sample.pSample), sample.nLength, 1, inBuf + offset, file.BytesLeft() - offset, numChannels, conv); return true; }
SmpLength InsertSilence(ModSample &smp, const SmpLength nSilenceLength, const SmpLength nStartFrom, CSoundFile &sndFile) //---------------------------------------------------------------------------------------------------------------------- { if(nSilenceLength == 0 || nSilenceLength > MAX_SAMPLE_LENGTH || smp.nLength > MAX_SAMPLE_LENGTH - nSilenceLength || nStartFrom > smp.nLength) return smp.nLength; const bool wasEmpty = smp.nLength == 0 || smp.pSample == nullptr; const SmpLength newLength = smp.nLength + nSilenceLength; char *pNewSmp = nullptr; pNewSmp = static_cast<char *>(ModSample::AllocateSample(newLength, smp.GetBytesPerSample())); if(pNewSmp == nullptr) return smp.nLength; //Sample allocation failed. if(!wasEmpty) { // Copy over old sample const SmpLength silenceOffset = nStartFrom * smp.GetBytesPerSample(); const SmpLength silenceBytes = nSilenceLength * smp.GetBytesPerSample(); if(nStartFrom > 0) { memcpy(pNewSmp, smp.pSample, silenceOffset); } if(nStartFrom < smp.nLength) { memcpy(pNewSmp + silenceOffset + silenceBytes, static_cast<const char *>(smp.pSample) + silenceOffset, smp.GetSampleSizeInBytes() - silenceOffset); } // Update loop points if necessary. if(smp.nLoopStart >= nStartFrom) smp.nLoopStart += nSilenceLength; if(smp.nLoopEnd >= nStartFrom) smp.nLoopEnd += nSilenceLength; } else { // Set loop points automatically smp.nLoopStart = 0; smp.nLoopEnd = newLength; smp.uFlags.set(CHN_LOOP); } ReplaceSample(smp, pNewSmp, newLength, sndFile); PrecomputeLoops(smp, sndFile, true); return smp.nLength; }
SmpLength ResizeSample(ModSample &smp, const SmpLength nNewLength, CSoundFile &sndFile) //------------------------------------------------------------------------------------- { // Invalid sample size if(nNewLength > MAX_SAMPLE_LENGTH || nNewLength == smp.nLength) return smp.nLength; // New sample will be bigger so we'll just use "InsertSilence" as it's already there. if(nNewLength > smp.nLength) return InsertSilence(smp, nNewLength - smp.nLength, smp.nLength, sndFile); // Else: Shrink sample const SmpLength nNewSmpBytes = nNewLength * smp.GetBytesPerSample(); void *pNewSmp = ModSample::AllocateSample(nNewLength, smp.GetBytesPerSample()); if(pNewSmp == nullptr) return smp.nLength; //Sample allocation failed. // Copy over old data and replace sample by the new one memcpy(pNewSmp, smp.pSample, nNewSmpBytes); ReplaceSample(smp, pNewSmp, nNewLength, sndFile); // Adjust loops if(smp.nLoopStart > nNewLength) { smp.nLoopStart = smp.nLoopEnd = 0; smp.uFlags.reset(CHN_LOOP); } if(smp.nLoopEnd > nNewLength) smp.nLoopEnd = nNewLength; if(smp.nSustainStart > nNewLength) { smp.nSustainStart = smp.nSustainEnd = 0; smp.uFlags.reset(CHN_SUSTAINLOOP); } if(smp.nSustainEnd > nNewLength) smp.nSustainEnd = nNewLength; PrecomputeLoops(smp, sndFile); return smp.nLength; }
bool PrecomputeLoops(ModSample &smp, CSoundFile &sndFile, bool updateChannels) //---------------------------------------------------------------------------- { if(smp.nLength == 0 || smp.pSample == nullptr) return false; smp.SanitizeLoops(); // Update channels with possibly changed loop values if(updateChannels) { UpdateLoopPoints(smp, sndFile); } if(smp.GetElementarySampleSize() == 2) PrecomputeLoopsImpl<int16>(smp, sndFile); else if(smp.GetElementarySampleSize() == 1) PrecomputeLoopsImpl<int8>(smp, sndFile); return true; }
// Convert an S3M sample header to OpenMPT's internal sample header. void S3MSampleHeader::ConvertToMPT(ModSample &mptSmp) const //--------------------------------------------------------- { mptSmp.Initialize(MOD_TYPE_S3M); mpt::String::Read<mpt::String::maybeNullTerminated>(mptSmp.filename, filename); if((sampleType == typePCM || sampleType == typeNone) && !memcmp(magic, "SCRS", 4)) { // Sample Length and Loops if(sampleType == typePCM) { mptSmp.nLength = length; mptSmp.nLoopStart = MIN(loopStart, mptSmp.nLength - 1); mptSmp.nLoopEnd = MIN(loopEnd, mptSmp.nLength); mptSmp.uFlags.set(CHN_LOOP, (flags & smpLoop) != 0); } if(mptSmp.nLoopEnd < 2 || mptSmp.nLoopStart >= mptSmp.nLoopEnd || mptSmp.nLoopEnd - mptSmp.nLoopStart < 1) { mptSmp.nLoopStart = mptSmp.nLoopEnd = 0; mptSmp.uFlags.reset(); } // Volume / Panning mptSmp.nVolume = MIN(defaultVolume, 64) * 4; // C-5 frequency mptSmp.nC5Speed = c5speed; if(mptSmp.nC5Speed == 0) { mptSmp.nC5Speed = 8363; } else if(mptSmp.nC5Speed < 1024) { mptSmp.nC5Speed = 1024; } } }
OPENMPT_NAMESPACE_BEGIN #if MPT_COMPILER_GCC #if MPT_GCC_AT_LEAST(4,6,0) #pragma GCC diagnostic push #endif #pragma GCC diagnostic ignored "-Wswitch" #elif MPT_COMPILER_CLANG #pragma clang diagnostic push #if MPT_CLANG_AT_LEAST(3,3,0) #pragma clang diagnostic ignored "-Wswitch" #else #pragma clang diagnostic ignored "-Wswitch-enum" #endif #endif // Read a sample from memory size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const //-------------------------------------------------------------------- { if(sample.nLength < 1 || !file.IsValid()) { return 0; } LimitMax(sample.nLength, MAX_SAMPLE_LENGTH); const char * const sourceBuf = file.GetRawData(); const FileReader::off_t fileSize = file.BytesLeft(), filePosition = file.GetPosition(); FileReader::off_t bytesRead = 0; // Amount of memory that has been read from file sample.uFlags.set(CHN_16BIT, GetBitDepth() >= 16); sample.uFlags.set(CHN_STEREO, GetChannelFormat() != mono); size_t sampleSize = sample.AllocateSample(); // Target sample size in bytes if(sampleSize == 0) { sample.nLength = 0; return 0; } ASSERT(sampleSize >= sample.GetSampleSizeInBytes()); ////////////////////////////////////////////////////// // 8-Bit / Mono / PCM if(GetBitDepth() == 8 && GetChannelFormat() == mono) { switch(GetEncoding()) { case signedPCM: // 8-Bit / Mono / Signed / PCM bytesRead = CopyMonoSample<SC::DecodeInt8>(sample, sourceBuf, fileSize); break; case unsignedPCM: // 8-Bit / Mono / Unsigned / PCM bytesRead = CopyMonoSample<SC::DecodeUint8>(sample, sourceBuf, fileSize); break; case deltaPCM: // 8-Bit / Mono / Delta / PCM case MT2: bytesRead = CopyMonoSample<SC::DecodeInt8Delta>(sample, sourceBuf, fileSize); break; case PCM7to8: // 7 Bit stored as 8-Bit with highest bit unused / Mono / Signed / PCM bytesRead = CopyMonoSample<SC::DecodeInt7>(sample, sourceBuf, fileSize); break; } } ////////////////////////////////////////////////////// // 8-Bit / Stereo Split / PCM else if(GetBitDepth() == 8 && GetChannelFormat() == stereoSplit) { switch(GetEncoding()) { case signedPCM: // 8-Bit / Stereo Split / Signed / PCM bytesRead = CopyStereoSplitSample<SC::DecodeInt8>(sample, sourceBuf, fileSize); break; case unsignedPCM: // 8-Bit / Stereo Split / Unsigned / PCM bytesRead = CopyStereoSplitSample<SC::DecodeUint8>(sample, sourceBuf, fileSize); break; case deltaPCM: // 8-Bit / Stereo Split / Delta / PCM case MT2: bytesRead = CopyStereoSplitSample<SC::DecodeInt8Delta>(sample, sourceBuf, fileSize); break; } } ////////////////////////////////////////////////////// // 8-Bit / Stereo Interleaved / PCM else if(GetBitDepth() == 8 && GetChannelFormat() == stereoInterleaved) { switch(GetEncoding()) { case signedPCM: // 8-Bit / Stereo Interleaved / Signed / PCM bytesRead = CopyStereoInterleavedSample<SC::DecodeInt8>(sample, sourceBuf, fileSize); break; case unsignedPCM: // 8-Bit / Stereo Interleaved / Unsigned / PCM bytesRead = CopyStereoInterleavedSample<SC::DecodeUint8>(sample, sourceBuf, fileSize); break; case deltaPCM: // 8-Bit / Stereo Interleaved / Delta / PCM bytesRead = CopyStereoInterleavedSample<SC::DecodeInt8Delta>(sample, sourceBuf, fileSize); break; } } ////////////////////////////////////////////////////// // 16-Bit / Mono / Little Endian / PCM else if(GetBitDepth() == 16 && GetChannelFormat() == mono && GetEndianness() == littleEndian) { switch(GetEncoding()) { case signedPCM: // 16-Bit / Stereo Interleaved / Signed / PCM bytesRead = CopyMonoSample<SC::DecodeInt16<0, littleEndian16> >(sample, sourceBuf, fileSize); break; case unsignedPCM: // 16-Bit / Stereo Interleaved / Unsigned / PCM bytesRead = CopyMonoSample<SC::DecodeInt16<0x8000u, littleEndian16> >(sample, sourceBuf, fileSize); break; case deltaPCM: // 16-Bit / Stereo Interleaved / Delta / PCM case MT2: bytesRead = CopyMonoSample<SC::DecodeInt16Delta<littleEndian16> >(sample, sourceBuf, fileSize); break; } } ////////////////////////////////////////////////////// // 16-Bit / Mono / Big Endian / PCM else if(GetBitDepth() == 16 && GetChannelFormat() == mono && GetEndianness() == bigEndian) { switch(GetEncoding()) { case signedPCM: // 16-Bit / Mono / Signed / PCM bytesRead = CopyMonoSample<SC::DecodeInt16<0, bigEndian16> >(sample, sourceBuf, fileSize); break; case unsignedPCM: // 16-Bit / Mono / Unsigned / PCM bytesRead = CopyMonoSample<SC::DecodeInt16<0x8000u, bigEndian16> >(sample, sourceBuf, fileSize); break; case deltaPCM: // 16-Bit / Mono / Delta / PCM bytesRead = CopyMonoSample<SC::DecodeInt16Delta<bigEndian16> >(sample, sourceBuf, fileSize); break; } } ////////////////////////////////////////////////////// // 16-Bit / Stereo Split / Little Endian / PCM else if(GetBitDepth() == 16 && GetChannelFormat() == stereoSplit && GetEndianness() == littleEndian) { switch(GetEncoding()) { case signedPCM: // 16-Bit / Stereo Split / Signed / PCM bytesRead = CopyStereoSplitSample<SC::DecodeInt16<0, littleEndian16> >(sample, sourceBuf, fileSize); break; case unsignedPCM: // 16-Bit / Stereo Split / Unsigned / PCM bytesRead = CopyStereoSplitSample<SC::DecodeInt16<0x8000u, littleEndian16> >(sample, sourceBuf, fileSize); break; case deltaPCM: // 16-Bit / Stereo Split / Delta / PCM case MT2: bytesRead = CopyStereoSplitSample<SC::DecodeInt16Delta<littleEndian16> >(sample, sourceBuf, fileSize); break; } } ////////////////////////////////////////////////////// // 16-Bit / Stereo Split / Big Endian / PCM else if(GetBitDepth() == 16 && GetChannelFormat() == stereoSplit && GetEndianness() == bigEndian) { switch(GetEncoding()) { case signedPCM: // 16-Bit / Stereo Split / Signed / PCM bytesRead = CopyStereoSplitSample<SC::DecodeInt16<0, bigEndian16> >(sample, sourceBuf, fileSize); break; case unsignedPCM: // 16-Bit / Stereo Split / Unsigned / PCM bytesRead = CopyStereoSplitSample<SC::DecodeInt16<0x8000u, bigEndian16> >(sample, sourceBuf, fileSize); break; case deltaPCM: // 16-Bit / Stereo Split / Delta / PCM bytesRead = CopyStereoSplitSample<SC::DecodeInt16Delta<bigEndian16> >(sample, sourceBuf, fileSize); break; } } ////////////////////////////////////////////////////// // 16-Bit / Stereo Interleaved / Little Endian / PCM else if(GetBitDepth() == 16 && GetChannelFormat() == stereoInterleaved && GetEndianness() == littleEndian) { switch(GetEncoding()) { case signedPCM: // 16-Bit / Stereo Interleaved / Signed / PCM bytesRead = CopyStereoInterleavedSample<SC::DecodeInt16<0, littleEndian16> >(sample, sourceBuf, fileSize); break; case unsignedPCM: // 16-Bit / Stereo Interleaved / Unsigned / PCM bytesRead = CopyStereoInterleavedSample<SC::DecodeInt16<0x8000u, littleEndian16> >(sample, sourceBuf, fileSize); break; case deltaPCM: // 16-Bit / Stereo Interleaved / Delta / PCM bytesRead = CopyStereoInterleavedSample<SC::DecodeInt16Delta<littleEndian16> >(sample, sourceBuf, fileSize); break; } } ////////////////////////////////////////////////////// // 16-Bit / Stereo Interleaved / Big Endian / PCM else if(GetBitDepth() == 16 && GetChannelFormat() == stereoInterleaved && GetEndianness() == bigEndian) { switch(GetEncoding()) { case signedPCM: // 16-Bit / Stereo Interleaved / Signed / PCM bytesRead = CopyStereoInterleavedSample<SC::DecodeInt16<0, bigEndian16> >(sample, sourceBuf, fileSize); break; case unsignedPCM: // 16-Bit / Stereo Interleaved / Unsigned / PCM bytesRead = CopyStereoInterleavedSample<SC::DecodeInt16<0x8000u, bigEndian16> >(sample, sourceBuf, fileSize); break; case deltaPCM: // 16-Bit / Stereo Interleaved / Delta / PCM bytesRead = CopyStereoInterleavedSample<SC::DecodeInt16Delta<bigEndian16> >(sample, sourceBuf, fileSize); break; } } ////////////////////////////////////////////////////// // 24-Bit / Signed / Mono / PCM else if(GetBitDepth() == 24 && GetChannelFormat() == mono && GetEncoding() == signedPCM) { if(GetEndianness() == littleEndian) { bytesRead = CopyMonoSample<SC::ConversionChain<SC::Convert<int16, int32>, SC::DecodeInt24<0, littleEndian24> > >(sample, sourceBuf, fileSize); } else { bytesRead = CopyMonoSample<SC::ConversionChain<SC::Convert<int16, int32>, SC::DecodeInt24<0, bigEndian24> > >(sample, sourceBuf, fileSize); } } ////////////////////////////////////////////////////// // 24-Bit / Signed / Stereo Interleaved / PCM else if(GetBitDepth() == 24 && GetChannelFormat() == stereoInterleaved && GetEncoding() == signedPCM) { if(GetEndianness() == littleEndian) { bytesRead = CopyStereoInterleavedSample<SC::ConversionChain<SC::Convert<int16, int32>, SC::DecodeInt24<0, littleEndian24> > >(sample, sourceBuf, fileSize); } else { bytesRead = CopyStereoInterleavedSample<SC::ConversionChain<SC::Convert<int16, int32>, SC::DecodeInt24<0, bigEndian24> > >(sample, sourceBuf, fileSize); } } ////////////////////////////////////////////////////// // 32-Bit / Signed / Mono / PCM else if(GetBitDepth() == 32 && GetChannelFormat() == mono && GetEncoding() == signedPCM) { if(GetEndianness() == littleEndian) { bytesRead = CopyMonoSample<SC::ConversionChain<SC::Convert<int16, int32>, SC::DecodeInt32<0, littleEndian32> > >(sample, sourceBuf, fileSize); } else { bytesRead = CopyMonoSample<SC::ConversionChain<SC::Convert<int16, int32>, SC::DecodeInt32<0, bigEndian32> > >(sample, sourceBuf, fileSize); } } ////////////////////////////////////////////////////// // 32-Bit / Signed / Stereo Interleaved / PCM else if(GetBitDepth() == 32 && GetChannelFormat() == stereoInterleaved && GetEncoding() == signedPCM) { if(GetEndianness() == littleEndian) { bytesRead = CopyStereoInterleavedSample<SC::ConversionChain<SC::Convert<int16, int32>, SC::DecodeInt32<0, littleEndian32> > >(sample, sourceBuf, fileSize); } else { bytesRead = CopyStereoInterleavedSample<SC::ConversionChain<SC::Convert<int16, int32>, SC::DecodeInt32<0, bigEndian32> > >(sample, sourceBuf, fileSize); } } ////////////////////////////////////////////////////// // 32-Bit / Float / Mono / PCM else if(GetBitDepth() == 32 && GetChannelFormat() == mono && GetEncoding() == floatPCM) { if(GetEndianness() == littleEndian) { bytesRead = CopyMonoSample<SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeFloat32<littleEndian32> > >(sample, sourceBuf, fileSize); } else { bytesRead = CopyMonoSample<SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeFloat32<bigEndian32> > >(sample, sourceBuf, fileSize); } } ////////////////////////////////////////////////////// // 32-Bit / Float / Stereo Interleaved / PCM else if(GetBitDepth() == 32 && GetChannelFormat() == stereoInterleaved && GetEncoding() == floatPCM) { if(GetEndianness() == littleEndian) { bytesRead = CopyStereoInterleavedSample<SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeFloat32<littleEndian32> > >(sample, sourceBuf, fileSize); } else { bytesRead = CopyStereoInterleavedSample<SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeFloat32<bigEndian32> > >(sample, sourceBuf, fileSize); } } ////////////////////////////////////////////////////// // 24-Bit / Signed / Mono, Stereo Interleaved / PCM else if(GetBitDepth() == 24 && (GetChannelFormat() == mono || GetChannelFormat() == stereoInterleaved) && GetEncoding() == signedPCMnormalize) { // Normalize to 16-Bit uint32 srcPeak = uint32(1)<<31; if(GetEndianness() == littleEndian) { bytesRead = CopyAndNormalizeSample<SC::NormalizationChain<SC::Convert<int16, int32>, SC::DecodeInt24<0, littleEndian24> > >(sample, sourceBuf, fileSize, &srcPeak); } else { bytesRead = CopyAndNormalizeSample<SC::NormalizationChain<SC::Convert<int16, int32>, SC::DecodeInt24<0, bigEndian24> > >(sample, sourceBuf, fileSize, &srcPeak); } if(bytesRead) { // Adjust sample volume so we do not affect relative volume of the sample. Normalizing is only done to increase precision. sample.nGlobalVol = static_cast<uint16>(Clamp(Util::muldivr_unsigned(sample.nGlobalVol, srcPeak, uint32(1)<<31), uint32(1), uint32(64))); } } ////////////////////////////////////////////////////// // 32-Bit / Signed / Mono, Stereo Interleaved / PCM else if(GetBitDepth() == 32 && (GetChannelFormat() == mono || GetChannelFormat() == stereoInterleaved) && GetEncoding() == signedPCMnormalize) { // Normalize to 16-Bit uint32 srcPeak = uint32(1)<<31; if(GetEndianness() == littleEndian) { bytesRead = CopyAndNormalizeSample<SC::NormalizationChain<SC::Convert<int16, int32>, SC::DecodeInt32<0, littleEndian32> > >(sample, sourceBuf, fileSize, &srcPeak); } else { bytesRead = CopyAndNormalizeSample<SC::NormalizationChain<SC::Convert<int16, int32>, SC::DecodeInt32<0, bigEndian32> > >(sample, sourceBuf, fileSize, &srcPeak); } if(bytesRead) { // Adjust sample volume so we do not affect relative volume of the sample. Normalizing is only done to increase precision. sample.nGlobalVol = static_cast<uint16>(Clamp(Util::muldivr_unsigned(sample.nGlobalVol, srcPeak, uint32(1)<<31), uint32(1), uint32(64))); } } ////////////////////////////////////////////////////// // 32-Bit / Float / Mono, Stereo Interleaved / PCM else if(GetBitDepth() == 32 && (GetChannelFormat() == mono || GetChannelFormat() == stereoInterleaved) && GetEncoding() == floatPCMnormalize) { // Normalize to 16-Bit float32 srcPeak = 1.0f; if(GetEndianness() == littleEndian) { bytesRead = CopyAndNormalizeSample<SC::NormalizationChain<SC::Convert<int16, float32>, SC::DecodeFloat32<littleEndian32> > >(sample, sourceBuf, fileSize, &srcPeak); } else { bytesRead = CopyAndNormalizeSample<SC::NormalizationChain<SC::Convert<int16, float32>, SC::DecodeFloat32<bigEndian32> > >(sample, sourceBuf, fileSize, &srcPeak); } if(bytesRead) { // Adjust sample volume so we do not affect relative volume of the sample. Normalizing is only done to increase precision. sample.nGlobalVol = Util::Round<uint16>(Clamp(sample.nGlobalVol * srcPeak, 1.0f, 64.0f)); } } ////////////////////////////////////////////////////// // 32-Bit / Float / Mono / PCM / full scale 2^15 else if(GetBitDepth() == 32 && GetChannelFormat() == mono && GetEncoding() == floatPCM15) { if(GetEndianness() == littleEndian) { bytesRead = CopyMonoSample (sample, sourceBuf, fileSize, SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeScaledFloat32<littleEndian32> > (SC::Convert<int16, float32>(), SC::DecodeScaledFloat32<littleEndian32>(1.0f / static_cast<float>(1<<15))) ); } else { bytesRead = CopyMonoSample (sample, sourceBuf, fileSize, SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeScaledFloat32<bigEndian32> > (SC::Convert<int16, float32>(), SC::DecodeScaledFloat32<bigEndian32>(1.0f / static_cast<float>(1<<15))) ); } } ////////////////////////////////////////////////////// // 32-Bit / Float / Stereo Interleaved / PCM / full scale 2^15 else if(GetBitDepth() == 32 && GetChannelFormat() == stereoInterleaved && GetEncoding() == floatPCM15) { if(GetEndianness() == littleEndian) { bytesRead = CopyStereoInterleavedSample (sample, sourceBuf, fileSize, SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeScaledFloat32<littleEndian32> > (SC::Convert<int16, float32>(), SC::DecodeScaledFloat32<littleEndian32>(1.0f / static_cast<float>(1<<15))) ); } else { bytesRead = CopyStereoInterleavedSample (sample, sourceBuf, fileSize, SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeScaledFloat32<bigEndian32> > (SC::Convert<int16, float32>(), SC::DecodeScaledFloat32<bigEndian32>(1.0f / static_cast<float>(1<<15))) ); } } ////////////////////////////////////////////////////// // 32-Bit / Float / Stereo Interleaved / PCM / full scale 2^23 else if(GetBitDepth() == 32 && GetChannelFormat() == mono && GetEncoding() == floatPCM23) { if(GetEndianness() == littleEndian) { bytesRead = CopyMonoSample (sample, sourceBuf, fileSize, SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeScaledFloat32<littleEndian32> > (SC::Convert<int16, float32>(), SC::DecodeScaledFloat32<littleEndian32>(1.0f / static_cast<float>(1<<23))) ); } else { bytesRead = CopyMonoSample (sample, sourceBuf, fileSize, SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeScaledFloat32<bigEndian32> > (SC::Convert<int16, float32>(), SC::DecodeScaledFloat32<bigEndian32>(1.0f / static_cast<float>(1<<23))) ); } } ////////////////////////////////////////////////////// // 32-Bit / Float / Stereo Interleaved / PCM / full scale 2^23 else if(GetBitDepth() == 32 && GetChannelFormat() == stereoInterleaved && GetEncoding() == floatPCM23) { if(GetEndianness() == littleEndian) { bytesRead = CopyStereoInterleavedSample (sample, sourceBuf, fileSize, SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeScaledFloat32<littleEndian32> > (SC::Convert<int16, float32>(), SC::DecodeScaledFloat32<littleEndian32>(1.0f / static_cast<float>(1<<23))) ); } else { bytesRead = CopyStereoInterleavedSample (sample, sourceBuf, fileSize, SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeScaledFloat32<bigEndian32> > (SC::Convert<int16, float32>(), SC::DecodeScaledFloat32<bigEndian32>(1.0f / static_cast<float>(1<<23))) ); } } ////////////////////////////////////////////////////// // Compressed samples if(*this == SampleIO(_8bit, mono, littleEndian, ADPCM)) { // 4-Bit ADPCM data int8 compressionTable[16]; // ADPCM Compression LUT if(file.ReadArray(compressionTable)) { size_t readLength = (sample.nLength + 1) / 2; LimitMax(readLength, file.BytesLeft()); const uint8 *inBuf = reinterpret_cast<const uint8*>(sourceBuf) + sizeof(compressionTable); int8 *outBuf = static_cast<int8 *>(sample.pSample); int8 delta = 0; for(size_t i = readLength; i != 0; i--) { delta += compressionTable[*inBuf & 0x0F]; *(outBuf++) = delta; delta += compressionTable[(*inBuf >> 4) & 0x0F]; *(outBuf++) = delta; inBuf++; } bytesRead = sizeof(compressionTable) + readLength; }
bool ModModule::load(Stream* stream, int loadMode) { noConstMetaInfo().filename = stream->name(); setTempo(125); setSpeed(6); state().globalVolume = 0x40; char modName[20]; stream->read(modName, 20); noConstMetaInfo().title = stringncpy(modName, 20); const IdMetaInfo* meta = nullptr; if(loadMode != LoadingMode::Smp15) { // check 31-sample mod logger()->info(L4CXX_LOCATION, "Probing meta-info for 31-sample mod..."); stream->seek(0x438); meta = findMeta(stream); if(meta == nullptr) { logger()->warn(L4CXX_LOCATION, "Could not find a valid module ID"); return false; } } else { logger()->info(L4CXX_LOCATION, "Trying to load 15-sample mod..."); meta = &smp15MetaInfo; } logger()->debug(L4CXX_LOCATION, "%d-channel, ID '%s', Tracker '%s'", int(meta->channels), meta->id, meta->tracker); noConstMetaInfo().trackerInfo = meta->tracker; for(int i = 0; i < meta->channels; i++) { m_channels.emplace_back(new ModChannel(this, ((i + 1) & 2) == 0)); } stream->seek(20); const int numSamples = loadMode == LoadingMode::Smp15 ? 15 : 31; for(int i = 0; i < numSamples; i++) { ModSample* smp = new ModSample(); m_samples.push_back(smp); if(!smp->loadHeader(stream)) { logger()->warn(L4CXX_LOCATION, "Sample header could not be loaded"); return false; } } uint8_t maxPatNum = 0; { // load orders uint8_t songLen; *stream >> songLen; if(songLen > 128) { songLen = 128; } logger()->debug(L4CXX_LOCATION, "Song length: %d", int(songLen)); uint8_t tmp; *stream >> tmp; // skip the restart pos for(uint_fast8_t i = 0; i < songLen; i++) { *stream >> tmp; if(tmp >= 64) { continue; } if(tmp > maxPatNum) { maxPatNum = tmp; } logger()->trace(L4CXX_LOCATION, "Order %d index: %d", int(i), int(tmp)); addOrder(std::make_unique<OrderEntry>(tmp)); } if(loadMode != LoadingMode::Smp31Malformed) { while(songLen++ < 128) { *stream >> tmp; if(tmp >= 64) { continue; } if(tmp > maxPatNum) { maxPatNum = tmp; } } } else { stream->seekrel(128 - songLen); } }
// Remove DC offset float RemoveDCOffset(ModSample &smp, SmpLength iStart, SmpLength iEnd, const MODTYPE modtype, CSoundFile &sndFile) //--------------------------------------- { if(smp.pSample == nullptr || smp.nLength < 1) return 0; if (iEnd > smp.nLength) iEnd = smp.nLength; if (iStart > iEnd) iStart = iEnd; if (iStart == iEnd) { iStart = 0; iEnd = smp.nLength; } iStart *= smp.GetNumChannels(); iEnd *= smp.GetNumChannels(); const double dMaxAmplitude = (smp.GetElementarySampleSize() == 2) ? GetMaxAmplitude<int16>() : GetMaxAmplitude<int8>(); // step 1: Calculate offset. OffsetData oData = {0,0,0}; if(smp.GetElementarySampleSize() == 2) oData = CalculateOffset(static_cast<int16 *>(smp.pSample) + iStart, iEnd - iStart); else if(smp.GetElementarySampleSize() == 1) oData = CalculateOffset(static_cast<int8*>(smp.pSample) + iStart, iEnd - iStart); double dMin = oData.dMin, dMax = oData.dMax, dOffset = oData.dOffset; const float fReportOffset = (float)dOffset; if((int)(dOffset * dMaxAmplitude) == 0) return 0; // those will be changed... dMax += dOffset; dMin += dOffset; // ... and that might cause distortion, so we will normalize this. const double dAmplify = 1 / MAX(dMax, -dMin); // step 2: centralize + normalize sample dOffset *= dMaxAmplitude * dAmplify; if(smp.GetElementarySampleSize() == 2) RemoveOffsetAndNormalize( static_cast<int16 *>(smp.pSample) + iStart, iEnd - iStart, dOffset, dAmplify); else if(smp.GetElementarySampleSize() == 1) RemoveOffsetAndNormalize( static_cast<int8 *>(smp.pSample) + iStart, iEnd - iStart, dOffset, dAmplify); // step 3: adjust global vol (if available) if((modtype & (MOD_TYPE_IT | MOD_TYPE_MPT)) && (iStart == 0) && (iEnd == smp.nLength * smp.GetNumChannels())) { CriticalSection cs; smp.nGlobalVol = std::min((uint16)(smp.nGlobalVol / dAmplify), uint16(64)); for (CHANNELINDEX i = 0; i < MAX_CHANNELS; i++) { if(sndFile.m_PlayState.Chn[i].pModSample == &smp) { sndFile.m_PlayState.Chn[i].nInsVol = smp.nGlobalVol; if(sndFile.m_PlayState.Chn[i].pModInstrument) { sndFile.m_PlayState.Chn[i].nInsVol = (smp.nGlobalVol * sndFile.m_PlayState.Chn[i].pModInstrument->nGlobalVol) >> 6; } }