From: <sag...@us...> - 2012-05-27 15:53:06
|
Revision: 1287 http://modplug.svn.sourceforge.net/modplug/?rev=1287&view=rev Author: saga-games Date: 2012-05-27 15:52:58 +0000 (Sun, 27 May 2012) Log Message: ----------- [Ref] Rewrote WAV loaders to share more code. The actual WAV loading is now done in WAVTools.cpp. Modified Paths: -------------- trunk/OpenMPT/common/StringFixer.h trunk/OpenMPT/mptrack/mptrack_08.vcproj trunk/OpenMPT/mptrack/mptrack_10.vcxproj trunk/OpenMPT/mptrack/mptrack_10.vcxproj.filters trunk/OpenMPT/soundlib/ChunkReader.h trunk/OpenMPT/soundlib/Dlsbank.cpp trunk/OpenMPT/soundlib/Load_umx.cpp trunk/OpenMPT/soundlib/Load_wav.cpp trunk/OpenMPT/soundlib/Loaders.h trunk/OpenMPT/soundlib/ModSample.h trunk/OpenMPT/soundlib/SampleFormats.cpp trunk/OpenMPT/soundlib/Sndfile.cpp trunk/OpenMPT/soundlib/Sndfile.h Added Paths: ----------- trunk/OpenMPT/soundlib/WAVTools.cpp trunk/OpenMPT/soundlib/WAVTools.h Modified: trunk/OpenMPT/common/StringFixer.h =================================================================== --- trunk/OpenMPT/common/StringFixer.h 2012-05-26 14:55:51 UTC (rev 1286) +++ trunk/OpenMPT/common/StringFixer.h 2012-05-27 15:52:58 UTC (rev 1287) @@ -83,7 +83,7 @@ //---------------------------------------------------------------------------------------- { STATIC_ASSERT(destSize > 0); - ASSERT(srcSize > 0); + //ASSERT(srcSize > 0); const size_t maxSize = min(destSize, srcSize); char *dst = destBuffer; Modified: trunk/OpenMPT/mptrack/mptrack_08.vcproj =================================================================== --- trunk/OpenMPT/mptrack/mptrack_08.vcproj 2012-05-26 14:55:51 UTC (rev 1286) +++ trunk/OpenMPT/mptrack/mptrack_08.vcproj 2012-05-27 15:52:58 UTC (rev 1287) @@ -612,10 +612,6 @@ RelativePath="..\soundlib\WindowedFIR.cpp" > </File> - <File - RelativePath="..\soundlib\XMTools.cpp" - > - </File> <Filter Name="PatternRandomizer" > @@ -1246,10 +1242,6 @@ RelativePath="..\soundlib\WindowedFIR.h" > </File> - <File - RelativePath=".\soundlib\XMTools.h" - > - </File> <Filter Name="tuning" > @@ -1486,6 +1478,22 @@ RelativePath="..\soundlib\Loaders.h" > </File> + <File + RelativePath="..\soundlib\WAVTools.cpp" + > + </File> + <File + RelativePath="..\soundlib\WAVTools.h" + > + </File> + <File + RelativePath="..\soundlib\XMTools.cpp" + > + </File> + <File + RelativePath="..\soundlib\XMTools.h" + > + </File> </Filter> <File RelativePath=".\res\built-inTunings.tc" Modified: trunk/OpenMPT/mptrack/mptrack_10.vcxproj =================================================================== --- trunk/OpenMPT/mptrack/mptrack_10.vcxproj 2012-05-26 14:55:51 UTC (rev 1286) +++ trunk/OpenMPT/mptrack/mptrack_10.vcxproj 2012-05-27 15:52:58 UTC (rev 1287) @@ -179,6 +179,7 @@ <ClCompile Include="..\soundlib\RowVisitor.cpp" /> <ClCompile Include="..\soundlib\SampleFormats.cpp" /> <ClCompile Include="..\soundlib\SampleIO.cpp" /> + <ClCompile Include="..\soundlib\WAVTools.cpp" /> <ClCompile Include="..\soundlib\XMTools.cpp" /> <ClCompile Include="AbstractVstEditor.cpp" /> <ClCompile Include="ACMConvert.cpp" /> @@ -349,6 +350,7 @@ <ClInclude Include="..\soundlib\RowVisitor.h" /> <ClInclude Include="..\soundlib\SampleFormatConverters.h" /> <ClInclude Include="..\soundlib\SampleIO.h" /> + <ClInclude Include="..\soundlib\WAVTools.h" /> <ClInclude Include="..\soundlib\XMTools.h" /> <ClInclude Include="ACMConvert.h" /> <ClInclude Include="Autotune.h" /> Modified: trunk/OpenMPT/mptrack/mptrack_10.vcxproj.filters =================================================================== --- trunk/OpenMPT/mptrack/mptrack_10.vcxproj.filters 2012-05-26 14:55:51 UTC (rev 1286) +++ trunk/OpenMPT/mptrack/mptrack_10.vcxproj.filters 2012-05-27 15:52:58 UTC (rev 1287) @@ -463,6 +463,9 @@ <ClCompile Include="..\soundlib\SampleFormats.cpp"> <Filter>Module Loaders</Filter> </ClCompile> + <ClCompile Include="..\soundlib\WAVTools.cpp"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="AbstractVstEditor.h"> @@ -825,6 +828,9 @@ <ClInclude Include="..\soundlib\SampleFormatConverters.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="..\soundlib\WAVTools.h"> + <Filter>Header Files</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <None Include="res\bitmap1.bmp"> Modified: trunk/OpenMPT/soundlib/ChunkReader.h =================================================================== --- trunk/OpenMPT/soundlib/ChunkReader.h 2012-05-26 14:55:51 UTC (rev 1286) +++ trunk/OpenMPT/soundlib/ChunkReader.h 2012-05-27 15:52:58 UTC (rev 1287) @@ -29,24 +29,21 @@ //================= { private: - const T chunkHeader; - ChunkReader chunkData; + T chunkHeader; + FileReader chunkData; public: ChunkListItem(const T &header, const FileReader &data) : chunkHeader(header), chunkData(data) { } - // VC2008 needs operator=, VC2010 doesn't... - ChunkListItem<T>& operator= (const ChunkListItem<T> &other) + + ChunkListItem<T> &operator= (const ChunkListItem<T> &other) { - return MemCopy(*this, other); + chunkHeader = other.chunkHeader; + chunkData = other.chunkData; + return *this; } const T &GetHeader() const { return chunkHeader; } - FileReader &GetData() { return chunkData; } - - bool operator== (const ChunkListItem<T> &other) - { - return (GetHeader().GetID() == other.GetHeader().GetID()); - } + const FileReader &GetData() const { return chunkData; } }; template<typename T> @@ -57,8 +54,7 @@ public: // Check if the list contains a given chunk. - template<typename IdType> - bool ChunkExists(IdType id) const + bool ChunkExists(typename T::id_type id) const { for(const_iterator iter = begin(); iter != end(); iter++) { @@ -71,25 +67,23 @@ } // Retrieve the first chunk with a given ID. - template<typename IdType> - FileReader GetChunk(IdType id) + FileReader GetChunk(typename T::id_type id) const { - for(iterator iter = begin(); iter != end(); iter++) + for(const_iterator iter = begin(); iter != end(); iter++) { if(iter->GetHeader().GetID() == id) { return iter->GetData(); } } - return FileReader(nullptr, 0); + return FileReader(); } // Retrieve all chunks with a given ID. - template<typename IdType> - std::vector<FileReader> GetAllChunks(IdType id) + std::vector<FileReader> GetAllChunks(typename T::id_type id) const { std::vector<FileReader> result; - for(iterator iter = begin(); iter != end(); iter++) + for(const_iterator iter = begin(); iter != end(); iter++) { if(iter->GetHeader().GetID() == id) { @@ -98,11 +92,13 @@ } return result; } - }; // Read a series of "T" chunks until the end of file is reached. + // T is required to have the methods GetID() and GetLength(), as well as an id_type typedef. + // GetLength() should return the chunk size in bytes, and GetID() the chunk ID. + // id_type must reflect the type that is returned by GetID(). template<typename T> ChunkList<T> ReadChunks(size_t padding) { Modified: trunk/OpenMPT/soundlib/Dlsbank.cpp =================================================================== --- trunk/OpenMPT/soundlib/Dlsbank.cpp 2012-05-26 14:55:51 UTC (rev 1286) +++ trunk/OpenMPT/soundlib/Dlsbank.cpp 2012-05-27 15:52:58 UTC (rev 1287) @@ -1469,11 +1469,11 @@ BOOL CDLSBank::ExtractSample(CSoundFile *pSndFile, SAMPLEINDEX nSample, UINT nIns, UINT nRgn, int transpose) -//--------------------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------------------------- { DLSINSTRUMENT *pDlsIns; LPBYTE pWaveForm = NULL; - DWORD dwLen = 0, dwWSMPOffset = 0; + DWORD dwLen = 0; BOOL bOk, bWaveForm; if ((!m_pInstruments) || (nIns >= m_nInstruments) || (!pSndFile)) return FALSE; @@ -1482,6 +1482,8 @@ if (!ExtractWaveForm(nIns, nRgn, &pWaveForm, &dwLen)) return FALSE; if ((!pWaveForm) || (dwLen < 16)) return FALSE; bOk = FALSE; + + FileReader wsmpChunk; if (m_nType & SOUNDBANK_TYPE_SF2) { pSndFile->DestroySample(nSample); @@ -1512,7 +1514,8 @@ bWaveForm = (sample.pSample) ? TRUE : FALSE; } else { - bWaveForm = pSndFile->ReadWAVSample(nSample, pWaveForm, dwLen, &dwWSMPOffset); + FileReader file(reinterpret_cast<const char *>(pWaveForm), dwLen); + bWaveForm = pSndFile->ReadWAVSample(nSample, file, &wsmpChunk); } if (bWaveForm) { @@ -1545,21 +1548,24 @@ UINT usUnityNote = pRgn->uUnityNote; int sFineTune = pRgn->sFineTune; int lVolume = pRgn->usVolume; - if ((dwWSMPOffset) && (!(pRgn->fuOptions & DLSREGION_OVERRIDEWSMP))) + + WSMPCHUNK wsmp; + if(!(pRgn->fuOptions & DLSREGION_OVERRIDEWSMP) && wsmpChunk.ReadStructPartial(wsmp)) { - WSMPCHUNK *p = (WSMPCHUNK *)(pWaveForm + dwWSMPOffset); - usUnityNote = p->usUnityNote; - sFineTune = p->sFineTune; - lVolume = DLS32BitRelativeGainToLinear(p->lAttenuation) / 256; - if (p->cSampleLoops) + usUnityNote = wsmp.usUnityNote; + sFineTune = wsmp.sFineTune; + lVolume = DLS32BitRelativeGainToLinear(wsmp.lAttenuation) / 256; + if(wsmp.cSampleLoops) { - WSMPSAMPLELOOP *ploop = (WSMPSAMPLELOOP *)(pWaveForm+dwWSMPOffset+8+p->cbSize); - if (ploop->ulLoopLength > 3) + WSMPSAMPLELOOP loop; + wsmpChunk.Skip(8 + wsmp.cbSize); + wsmpChunk.Read(loop); + if(loop.ulLoopLength > 3) { sample.uFlags |= CHN_LOOP; - //if (ploop->ulLoopType) sample.uFlags |= CHN_PINGPONGLOOP; - sample.nLoopStart = ploop->ulLoopStart; - sample.nLoopEnd = ploop->ulLoopStart + ploop->ulLoopLength; + //if (loop.ulLoopType) sample.uFlags |= CHN_PINGPONGLOOP; + sample.nLoopStart = loop.ulLoopStart; + sample.nLoopEnd = loop.ulLoopStart + loop.ulLoopLength; } } } else Modified: trunk/OpenMPT/soundlib/Load_umx.cpp =================================================================== --- trunk/OpenMPT/soundlib/Load_umx.cpp 2012-05-26 14:55:51 UTC (rev 1286) +++ trunk/OpenMPT/soundlib/Load_umx.cpp 2012-05-27 15:52:58 UTC (rev 1287) @@ -274,7 +274,7 @@ if(ReadIT(data, fileChunk.GetLength()) || ReadXM(data, fileChunk.GetLength()) || ReadS3M(fileChunk) - || ReadWav(data, fileChunk.GetLength()) + || ReadWav(fileChunk) || ReadMod(fileChunk)) { return true; Modified: trunk/OpenMPT/soundlib/Load_wav.cpp =================================================================== --- trunk/OpenMPT/soundlib/Load_wav.cpp 2012-05-26 14:55:51 UTC (rev 1286) +++ trunk/OpenMPT/soundlib/Load_wav.cpp 2012-05-27 15:52:58 UTC (rev 1287) @@ -11,12 +11,9 @@ #include "stdafx.h" #include "Loaders.h" -#include "Wav.h" +#include "WAVTools.h" #include "SampleFormatConverters.h" -#ifndef WAVE_FORMAT_EXTENSIBLE -#define WAVE_FORMAT_EXTENSIBLE 0xFFFE -#endif ///////////////////////////////////////////////////////////// // WAV file support @@ -43,126 +40,136 @@ } -bool CSoundFile::ReadWav(const BYTE *lpStream, const DWORD dwMemLength) -//--------------------------------------------------------------------- +bool CSoundFile::ReadWav(FileReader &file) +//---------------------------------------- { - DWORD dwMemPos = 0; - WAVEFILEHEADER *phdr = (WAVEFILEHEADER *)lpStream; - WAVEFORMATHEADER *pfmt = (WAVEFORMATHEADER *)(lpStream + sizeof(WAVEFILEHEADER)); - if ((!lpStream) || (dwMemLength < (DWORD)sizeof(WAVEFILEHEADER))) return false; - if ((phdr->id_RIFF != IFFID_RIFF) || (phdr->id_WAVE != IFFID_WAVE) - || (pfmt->id_fmt != IFFID_fmt)) return false; - dwMemPos = sizeof(WAVEFILEHEADER) + 8 + pfmt->hdrlen; - if ((dwMemPos + 8 >= dwMemLength) - || ((pfmt->format != WAVE_FORMAT_PCM) && (pfmt->format != WAVE_FORMAT_EXTENSIBLE) && (pfmt->format != WAVE_FORMAT_IEEE_FLOAT)) - || (pfmt->channels > 4) - || (!pfmt->channels) - || (!pfmt->freqHz) - || (pfmt->bitspersample == 0) - || (pfmt->bitspersample > 32)) return false; - WAVEDATAHEADER *pdata; - for (;;) + WAVReader wavFile(file); + + if(!wavFile.IsValid() + || wavFile.GetNumChannels() == 0 + || wavFile.GetNumChannels() > MAX_BASECHANNELS + || wavFile.GetBitsPerSample() > 32 + || (wavFile.GetSampleFormat() != WAVFormatChunk::fmtPCM && wavFile.GetSampleFormat() != WAVFormatChunk::fmtFloat)) { - pdata = (WAVEDATAHEADER *)(lpStream + dwMemPos); - if (pdata->id_data == IFFID_data) break; - dwMemPos += pdata->length + 8; - if (dwMemPos >= dwMemLength - 8) return false; + return false; } + + m_nChannels = Util::Max(wavFile.GetNumChannels(), uint16(2)); + if(Patterns.Insert(0, 64) || Patterns.Insert(1, 64)) + { + return false; + } + + const SmpLength sampleLength = wavFile.GetSampleLength(); + + // Setting up module length + // Calculate sample length in ticks at tempo 125 + const uint32 sampleTicks = ((sampleLength * 50) / wavFile.GetSampleRate()) + 1; + uint32 ticksPerRow = Util::Max((sampleTicks + 63u) / 63u, 1u); + + Order.clear(); + Order.Append(0); + ORDERINDEX numOrders = 1; + while(ticksPerRow >= 32) + { + Order.Append(1); + + numOrders++; + ticksPerRow = (sampleTicks + (64 * numOrders - 1)) / (64 * numOrders); + if(numOrders == MAX_ORDERS) + { + break; + } + } + m_nType = MOD_TYPE_WAV; - m_nSamples = 0; + m_nSamples = wavFile.GetNumChannels(); m_nInstruments = 0; - m_nChannels = 4; - m_nDefaultSpeed = 8; + m_nDefaultSpeed = ticksPerRow; m_nDefaultTempo = 125; - m_dwSongFlags = SONG_LINEARSLIDES; // For no resampling - Order.resize(MAX_ORDERS, Order.GetInvalidPatIndex()); - Order[0] = 0; - bool fail = Patterns.Insert(0, 64); - fail = Patterns.Insert(1, 64); - if(fail) return true; - UINT samplesize = ((pfmt->channels * pfmt->bitspersample) + 7) / 8; - SmpLength len = pdata->length; - if (len > dwMemLength - 8 - dwMemPos) len = dwMemLength - dwMemPos - 8; - len /= samplesize; - LimitMax(len, MAX_SAMPLE_LENGTH); - if (!len) return true; - // Setting up module length - DWORD dwTime = ((len * 50) / pfmt->freqHz) + 1; - DWORD framesperrow = (dwTime + 63) / 63; - if (framesperrow < 4) framesperrow = 4; - UINT norders = 1; - while (framesperrow >= 0x20) + m_dwSongFlags = SONG_LINEARSLIDES; + + for(CHANNELINDEX channel = 0; channel < wavFile.GetNumChannels(); channel++) { - Order[norders++] = 1; - Order[norders] = 0xFF; - framesperrow = (dwTime + (64 * norders - 1)) / (64 * norders); - if (norders >= MAX_ORDERS-1) break; + ChnSettings[channel].nPan = (channel % 2) ? 256 : 0; + ChnSettings[channel].nVolume = 64; + ChnSettings[channel].dwFlags = 0; } - m_nDefaultSpeed = framesperrow; - for (UINT iChn=0; iChn<4; iChn++) - { - ChnSettings[iChn].nPan = (iChn & 1) ? 256 : 0; - ChnSettings[iChn].nVolume = 64; - ChnSettings[iChn].dwFlags = 0; - } - // Setting up speed command - ModCommand *pcmd = Patterns[0]; - pcmd[0].command = CMD_SPEED; - pcmd[0].param = (BYTE)m_nDefaultSpeed; - pcmd[0].note = 5*12+1; - pcmd[0].instr = 1; - pcmd[1].note = pcmd[0].note; - pcmd[1].instr = pcmd[0].instr; - m_nSamples = pfmt->channels; - // Support for Multichannel Wave - FileReader file((char*)(lpStream + dwMemPos + 8), dwMemLength - dwMemPos - 8); - for (UINT nChn=0; nChn<m_nSamples; nChn++) + // Setting up pattern + PatternRow pattern = Patterns[0].GetRow(0); + pattern[0].note = pattern[1].note = NOTE_MIDDLEC; + pattern[0].instr = pattern[1].instr = 1; + + const FileReader sampleChunk = wavFile.GetSampleData(); + + // Read every channel into its own sample lot. + for(SAMPLEINDEX channel = 0; channel < GetNumSamples(); channel++) { - ModSample &sample = Samples[nChn + 1]; - pcmd[nChn].note = pcmd[0].note; - pcmd[nChn].instr = (BYTE)(nChn+1); + pattern[channel].note = pattern[0].note; + pattern[channel].instr = static_cast<ModCommand::INSTR>(channel + 1); + + ModSample &sample = Samples[channel + 1]; sample.Initialize(); - sample.nLength = len; - sample.nC5Speed = pfmt->freqHz; sample.uFlags = CHN_PANNING; - if (m_nSamples > 1) + sample.nLength = sampleLength; + sample.nC5Speed = wavFile.GetSampleRate(); + wavFile.ApplySampleSettings(sample, m_szNames[channel + 1]); + + if(wavFile.GetNumChannels() > 1) { - switch(nChn) + // Pan all samples appropriately + switch(channel) { - case 0: sample.nPan = 0; break; - case 1: sample.nPan = 256; break; - case 2: sample.nPan = (m_nSamples == 3 ? 128u : 64u); pcmd[nChn].command = CMD_S3MCMDEX; pcmd[nChn].param = 0x91; break; - case 3: sample.nPan = 192; pcmd[nChn].command = CMD_S3MCMDEX; pcmd[nChn].param = 0x91; break; - default: sample.nPan = 128; break; + case 0: + sample.nPan = 0; + break; + case 1: + sample.nPan = 256; + break; + case 2: + sample.nPan = (wavFile.GetNumChannels() == 3 ? 128u : 64u); + pattern[channel].command = CMD_S3MCMDEX; + pattern[channel].param = 0x91; + break; + case 3: + sample.nPan = 192; + pattern[channel].command = CMD_S3MCMDEX; + pattern[channel].param = 0x91; + break; + default: + sample.nPan = 128; + break; } } - if(pfmt->format == WAVE_FORMAT_IEEE_FLOAT) + if(wavFile.GetBitsPerSample() > 8) { sample.uFlags |= CHN_16BIT; - CopyWavChannel<ReadFloat32toInt16PCM<littleEndian32> >(sample, file, nChn, m_nSamples); + } + + if(wavFile.GetSampleFormat() == WAVFormatChunk::fmtFloat) + { + CopyWavChannel<ReadFloat32toInt16PCM<littleEndian32> >(sample, sampleChunk, channel, wavFile.GetNumChannels()); } else { - if(pfmt->bitspersample <= 8) + if(wavFile.GetBitsPerSample() <= 8) { - CopyWavChannel<ReadInt8PCM<0x80u> >(sample, file, nChn, m_nSamples); - } else if(pfmt->bitspersample <= 16) + CopyWavChannel<ReadInt8PCM<0x80u> >(sample, sampleChunk, channel, wavFile.GetNumChannels()); + } else if(wavFile.GetBitsPerSample() <= 16) { - sample.uFlags |= CHN_16BIT; - CopyWavChannel<ReadInt16PCM<0, littleEndian16> >(sample, file, nChn, m_nSamples); - } else if(pfmt->bitspersample <= 24) + CopyWavChannel<ReadInt16PCM<0, littleEndian16> >(sample, sampleChunk, channel, wavFile.GetNumChannels()); + } else if(wavFile.GetBitsPerSample() <= 24) { - sample.uFlags |= CHN_16BIT; - CopyWavChannel<ReadBigIntTo16PCM<3, 1, 2> >(sample, file, nChn, m_nSamples); - } else if(pfmt->bitspersample <= 32) + CopyWavChannel<ReadBigIntTo16PCM<3, 1, 2> >(sample, sampleChunk, channel, wavFile.GetNumChannels()); + } else if(wavFile.GetBitsPerSample() <= 32) { - sample.uFlags |= CHN_16BIT; - CopyWavChannel<ReadBigIntTo16PCM<4, 2, 3> >(sample, file, nChn, m_nSamples); + CopyWavChannel<ReadBigIntTo16PCM<4, 2, 3> >(sample, sampleChunk, channel, wavFile.GetNumChannels()); } } } + return true; } @@ -170,76 +177,62 @@ //////////////////////////////////////////////////////////////////////// // IMA ADPCM Support -#pragma pack(push, 1) -typedef struct IMAADPCMBLOCK +// Note: Only works for mono samples. +bool IMAADPCMUnpack16(int16 *target, SmpLength sampleLen, const void *source, size_t sourceSize, uint16 blockAlign) +//----------------------------------------------------------------------------------------------------------------- { - WORD sample; - BYTE index; - BYTE Reserved; -} DVI_ADPCMBLOCKHEADER; + static const int32 IMAIndexTab[8] = { -1, -1, -1, -1, 2, 4, 6, 8 }; + static const int32 IMAUnpackTable[90] = + { + 7, 8, 9, 10, 11, 12, 13, 14, + 16, 17, 19, 21, 23, 25, 28, 31, + 34, 37, 41, 45, 50, 55, 60, 66, + 73, 80, 88, 97, 107, 118, 130, 143, + 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, + 724, 796, 876, 963, 1060, 1166, 1282, 1411, + 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, + 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, + 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, + 32767, 0 + }; -#pragma pack(pop) + if ((sampleLen < 4) || (!target) || (!source) + || (blockAlign < 5) || (blockAlign > sourceSize)) return false; + SmpLength nPos = 0; -static const int gIMAUnpackTable[90] = -{ - 7, 8, 9, 10, 11, 12, 13, 14, - 16, 17, 19, 21, 23, 25, 28, 31, - 34, 37, 41, 45, 50, 55, 60, 66, - 73, 80, 88, 97, 107, 118, 130, 143, - 157, 173, 190, 209, 230, 253, 279, 307, - 337, 371, 408, 449, 494, 544, 598, 658, - 724, 796, 876, 963, 1060, 1166, 1282, 1411, - 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, - 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, - 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, - 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, - 32767, 0 -}; - - -BOOL IMAADPCMUnpack16(signed short *pdest, UINT nLen, LPBYTE psrc, DWORD dwBytes, UINT pkBlkAlign) -//------------------------------------------------------------------------------------------------ -{ - static const int gIMAIndexTab[8] = { -1, -1, -1, -1, 2, 4, 6, 8 }; - UINT nPos; - int value; - - if ((nLen < 4) || (!pdest) || (!psrc) - || (pkBlkAlign < 5) || (pkBlkAlign > dwBytes)) return false; - nPos = 0; - while ((nPos < nLen) && (dwBytes > 4)) + const uint8 *psrc = static_cast<const uint8 *>(source); + while((nPos < sampleLen) && (sourceSize > 4)) { - int nIndex; - value = *((short int *)psrc); + int32 nIndex; + int32 value = *((int16 *)psrc); nIndex = psrc[2]; psrc += 4; - dwBytes -= 4; - pdest[nPos++] = (short int)value; - for (UINT i=0; ((i<(pkBlkAlign-4)*2) && (nPos < nLen) && (dwBytes)); i++) + sourceSize -= 4; + target[nPos++] = (int16)value; + for(uint32 i = 0; (i < (blockAlign - 4u) * 2u) && (nPos < sampleLen) && sourceSize > 0; i++) { - BYTE delta; - if (i & 1) + uint8 delta; + if(i & 1) { - delta = (BYTE)(((*(psrc++)) >> 4) & 0x0F); - dwBytes--; + delta = ((*(psrc++)) >> 4) & 0x0F; + sourceSize--; } else { - delta = (BYTE)((*psrc) & 0x0F); + delta = (*psrc) & 0x0F; } - int v = gIMAUnpackTable[nIndex] >> 3; - if (delta & 1) v += gIMAUnpackTable[nIndex] >> 2; - if (delta & 2) v += gIMAUnpackTable[nIndex] >> 1; - if (delta & 4) v += gIMAUnpackTable[nIndex]; + int32 v = IMAUnpackTable[nIndex] >> 3; + if (delta & 1) v += IMAUnpackTable[nIndex] >> 2; + if (delta & 2) v += IMAUnpackTable[nIndex] >> 1; + if (delta & 4) v += IMAUnpackTable[nIndex]; if (delta & 8) value -= v; else value += v; - nIndex += gIMAIndexTab[delta & 7]; + nIndex += IMAIndexTab[delta & 7]; if (nIndex < 0) nIndex = 0; else if (nIndex > 88) nIndex = 88; - Limit(value, -32768, 32767); - pdest[nPos++] = (short int)value; + target[nPos++] = static_cast<int16>(Clamp(value, -32768, 32767)); } } return true; } - - Modified: trunk/OpenMPT/soundlib/Loaders.h =================================================================== --- trunk/OpenMPT/soundlib/Loaders.h 2012-05-26 14:55:51 UTC (rev 1286) +++ trunk/OpenMPT/soundlib/Loaders.h 2012-05-27 15:52:58 UTC (rev 1287) @@ -33,7 +33,11 @@ size_t streamPos; // Cursor location in the file public: + // Initialize invalid file reader object. + FileReader() : streamData(nullptr), streamLength(0), streamPos(0) { } + // Initialize file reader object with pointer to data and data length. FileReader(const char *data, size_t length) : streamData(data), streamLength(length), streamPos(0) { } + // Initialize file reader object based on an existing file reader object. The other object's stream position is copied. FileReader(const FileReader &other) : streamData(other.streamData), streamLength(other.streamLength), streamPos(other.streamPos) { } // Returns true if the object points to a valid stream. @@ -126,7 +130,7 @@ return FileReader(streamData + position, Util::Min(length, streamLength - position)); } else { - return FileReader(nullptr, 0); + return FileReader(); } } @@ -330,12 +334,12 @@ // Allow to read a struct partially (if there's less memory available than the struct's size, fill it up with zeros). // The file cursor is advanced by "partialSize" bytes. template <typename T> - bool ReadStructPartial(T &target, size_t partialSize = sizeof(target)) + bool ReadStructPartial(T &target, size_t partialSize = sizeof(T)) { const size_t copyBytes = Util::Min(partialSize, sizeof(target), BytesLeft()); memcpy(&target, streamData + streamPos, copyBytes); - memset(reinterpret_cast<const char *>(&target) + copyBytes, 0, sizeof(target) - copyBytes); + memset(reinterpret_cast<char *>(&target) + copyBytes, 0, sizeof(target) - copyBytes); Skip(partialSize); return true; @@ -420,9 +424,25 @@ && std::numeric_limits<T>::is_signed == false, "Target type is a not an unsigned integer"); - target = 0; + if(!BytesLeft()) + { + target = 0; + return false; + } + size_t writtenBits = 0; - uint8 b = 0x80; + uint8 b = ReadUint8(); + target = (b & 0x7F); + + // Count actual bits used in most significant byte (i.e. this one) + for(size_t bit = 0; bit < 7; bit++) + { + if((b & (1 << bit)) != 0) + { + writtenBits = bit + 1; + } + } + while(BytesLeft() && (b & 0x80) != 0) { b = ReadUint8(); @@ -434,7 +454,7 @@ if(writtenBits > sizeof(target) * 8) { // Overflow - target = std::numeric_limits<T>::max; + target = Util::MaxValueOfType<T>(target); return false; } else if((b & 0x80) != 0) { @@ -447,4 +467,4 @@ }; #include "Sndfile.h" -#include "SampleIO.h" \ No newline at end of file +#include "SampleIO.h" Modified: trunk/OpenMPT/soundlib/ModSample.h =================================================================== --- trunk/OpenMPT/soundlib/ModSample.h 2012-05-26 14:55:51 UTC (rev 1286) +++ trunk/OpenMPT/soundlib/ModSample.h 2012-05-27 15:52:58 UTC (rev 1287) @@ -28,8 +28,8 @@ uint8 nVibSweep; // Auto vibrato sweep (i.e. how long it takes until the vibrato effect reaches its full strength) uint8 nVibDepth; // Auto vibrato depth uint8 nVibRate; // Auto vibrato rate (speed) - //CHAR name[MAX_SAMPLENAME]; // Maybe it would be nicer to have sample names here, but that would require some refactoring. Also, would this slow down the mixer (cache misses)? - CHAR filename[MAX_SAMPLEFILENAME]; + //char name[MAX_SAMPLENAME]; // Maybe it would be nicer to have sample names here, but that would require some refactoring. Also, would this slow down the mixer (cache misses)? + char filename [MAX_SAMPLEFILENAME]; // Return the size of one (elementary) sample in bytes. uint8 GetElementarySampleSize() const { return (uFlags & CHN_16BIT) ? 2 : 1; } Modified: trunk/OpenMPT/soundlib/SampleFormats.cpp =================================================================== --- trunk/OpenMPT/soundlib/SampleFormats.cpp 2012-05-26 14:55:51 UTC (rev 1286) +++ trunk/OpenMPT/soundlib/SampleFormats.cpp 2012-05-27 15:52:58 UTC (rev 1287) @@ -18,19 +18,18 @@ #include "Wav.h" #include "ITTools.h" #include "XMTools.h" -#include "../common/StringFixer.h" +#include "WAVTools.h" #include "../common/Reporting.h" #include "../mptrack/version.h" #include "ChunkReader.h" -#pragma warning(disable:4244) bool CSoundFile::ReadSampleFromFile(SAMPLEINDEX nSample, const LPBYTE lpMemFile, DWORD dwFileLength) //-------------------------------------------------------------------------------------------------- { FileReader file(reinterpret_cast<const char *>(lpMemFile), dwFileLength); if(!nSample || nSample >= MAX_SAMPLES) return false; - if(!ReadWAVSample(nSample, lpMemFile, dwFileLength) + if(!ReadWAVSample(nSample, file) && !ReadXISample(nSample, lpMemFile, dwFileLength) && !ReadAIFFSample(nSample, file) && !ReadITSSample(nSample, lpMemFile, dwFileLength) @@ -297,307 +296,75 @@ //////////////////////////////////////////////////////////////////////////////// // WAV Open -#define IFFID_pcm 0x206d6370 -#define IFFID_fact 0x74636166 +extern bool IMAADPCMUnpack16(int16 *target, SmpLength sampleLen, const void *source, size_t sourceSize, uint16 blockAlign); -extern BOOL IMAADPCMUnpack16(signed short *pdest, UINT nLen, LPBYTE psrc, DWORD dwBytes, UINT pkBlkAlign); - -bool CSoundFile::ReadWAVSample(SAMPLEINDEX nSample, const LPBYTE lpMemFile, DWORD dwFileLength, DWORD *pdwWSMPOffset) -//------------------------------------------------------------------------------------------------------------------- +bool CSoundFile::ReadWAVSample(SAMPLEINDEX nSample, FileReader &file, FileReader *wsmpChunk) +//------------------------------------------------------------------------------------------ { - DWORD dwMemPos = 0, dwDataPos; - WAVEFILEHEADER *phdr = (WAVEFILEHEADER *)lpMemFile; - WAVEFORMATHEADER *pfmt, *pfmtpk; - WAVEDATAHEADER *pdata; - WAVESMPLHEADER *psh; - WAVEEXTRAHEADER *pxh; - DWORD dwInfoList, dwFact, dwSamplesPerBlock; - - if (!nSample || !lpMemFile || (dwFileLength < sizeof(WAVEFILEHEADER) + sizeof(WAVEFORMATHEADER))) - return false; - if ((phdr->id_RIFF != LittleEndian(IFFID_RIFF) && phdr->id_RIFF != LittleEndian(IFFID_LIST)) - || (phdr->id_WAVE != LittleEndian(IFFID_WAVE) && phdr->id_WAVE != LittleEndian(IFFID_wave))) - return false; + WAVReader wavFile(file); - dwMemPos = sizeof(WAVEFILEHEADER); - dwDataPos = 0; - pfmt = NULL; - pfmtpk = NULL; - pdata = NULL; - psh = NULL; - pxh = NULL; - dwSamplesPerBlock = 0; - dwInfoList = 0; - dwFact = 0; - - while ((dwMemPos + 8 < dwFileLength) && (dwMemPos < phdr->filesize)) + if(!wavFile.IsValid() + || wavFile.GetNumChannels() == 0 + || wavFile.GetNumChannels() > 2 + || wavFile.GetBitsPerSample() > 32 + || (wavFile.GetSampleFormat() != WAVFormatChunk::fmtPCM && wavFile.GetSampleFormat() != WAVFormatChunk::fmtFloat && wavFile.GetSampleFormat() != WAVFormatChunk::fmtIMA_ADPCM)) { - uint32 dwIFFID = LittleEndian(*reinterpret_cast<uint32 *>(lpMemFile + dwMemPos)); - uint32 dwLen = LittleEndian(*reinterpret_cast<uint32 *>(lpMemFile + dwMemPos + 4)); - if ((dwLen > dwFileLength) || (dwMemPos + 8 + dwLen > dwFileLength)) - break; - - switch(dwIFFID) - { - // "fmt " - case IFFID_fmt: - if (pfmt) break; - if (dwLen + 8 >= sizeof(WAVEFORMATHEADER)) - { - pfmt = (WAVEFORMATHEADER *)(lpMemFile + dwMemPos); - if (dwLen + 8 >= sizeof(WAVEFORMATHEADER)+4) - { - dwSamplesPerBlock = LittleEndianW(*reinterpret_cast<uint16 *>(lpMemFile + dwMemPos + sizeof(WAVEFORMATHEADER) + 2)); - } - } - break; - // "pcm " - case IFFID_pcm: - if (pfmtpk) break; - if (dwLen+8 >= sizeof(WAVEFORMATHEADER)) pfmtpk = (WAVEFORMATHEADER *)(lpMemFile + dwMemPos); - break; - // "fact" - case IFFID_fact: - if (!dwFact) dwFact = LittleEndian(*reinterpret_cast<uint32 *>(lpMemFile + dwMemPos + 8)); - break; - // "data" - case IFFID_data: - if(dwLen + 8 >= sizeof(WAVEDATAHEADER) && !pdata) - { - pdata = (WAVEDATAHEADER *)(lpMemFile + dwMemPos); - dwDataPos = dwMemPos + 8; - } - break; - // "xtra" - case 0x61727478: - if(dwLen + 8 >= sizeof(WAVEEXTRAHEADER)) - { - pxh = (WAVEEXTRAHEADER *)(lpMemFile + dwMemPos); - if(LittleEndian(pxh->xtra_len) + 8 > dwFileLength - dwMemPos) - { - pxh = nullptr; - } - } - break; - // "smpl" - case 0x6C706D73: - if(dwLen + 8 >= sizeof(WAVESMPLHEADER)) - psh = (WAVESMPLHEADER *)(lpMemFile + dwMemPos); - break; - // "LIST"."info" - case IFFID_LIST: - if(*reinterpret_cast<uint32 *>(lpMemFile + dwMemPos + 8) == LittleEndian(IFFID_INFO)) // "INFO" - dwInfoList = dwMemPos; - break; - // "wsmp": - case IFFID_wsmp: - if (pdwWSMPOffset) *pdwWSMPOffset = dwMemPos; - break; - } - dwMemPos += dwLen + 8; - - if((dwLen % 2) != 0 && dwMemPos < dwFileLength) - { - // Ignore the padding byte. Problem: Old versions of OpenMPT didn't write padding. -_- - // We will just do a heuristic check here: As OpenMPT always writes the "smpl" chunk after the "data" chunk - // (which happens to be the only chunk that has to be padded in OpenMPT's case), we just check if we can read "smpl" - // without incrementing the position. - if((dwMemPos + 8 < dwFileLength) && LittleEndian(*reinterpret_cast<uint32 *>(lpMemFile + dwMemPos)) == 0x6C706D73) - { - continue; - } - dwMemPos++; - } - } - if (!pdata || !pfmt || pdata->length < 4) return false; - - if ((pfmtpk) && (pfmt)) - { - if (pfmt->format != 1) - { - WAVEFORMATHEADER *tmp = pfmt; - pfmt = pfmtpk; - pfmtpk = tmp; - if(pfmtpk->format != 0x11 || pfmtpk->bitspersample != 4 - || pfmtpk->channels != 1) - return false; - } else pfmtpk = NULL; } - uint16 wavFormat = LittleEndianW(pfmt->format); - uint16 channels = LittleEndianW(pfmt->channels); - uint16 bitsPerSmp = LittleEndianW(pfmt->bitspersample); - - // WAVE_FORMAT_PCM (1), WAVE_FORMAT_IEEE_FLOAT (3), WAVE_FORMAT_EXTENSIBLE (0xFFFE) - if (((wavFormat != 1 && wavFormat != 0xFFFE) - && (wavFormat != 3 || bitsPerSmp != 32)) //Microsoft IEEE FLOAT - || channels > 2 - || channels == 0 - || bitsPerSmp == 0 - || bitsPerSmp > 32 - ) return false; - DestroySample(nSample); - ModSample &sample = Samples[nSample]; - MemsetZero(m_szNames[nSample]); sample.Initialize(); - sample.nC5Speed = LittleEndian(pfmt->freqHz); + sample.nLength = wavFile.GetSampleLength(); + sample.nC5Speed = wavFile.GetSampleRate(); + wavFile.ApplySampleSettings(sample, m_szNames[nSample]); + sample.Convert(MOD_TYPE_IT, GetType()); - // IMA ADPCM 4:1 - if (pfmtpk) + FileReader sampleChunk = wavFile.GetSampleData(); + + if(wavFile.GetSampleFormat() == WAVFormatChunk::fmtIMA_ADPCM) { - if (dwFact < 4) dwFact = LittleEndian(pdata->length) * 2; - sample.nLength = dwFact; + // IMA ADPCM 4:1 LimitMax(sample.nLength, MAX_SAMPLE_LENGTH); - sample.pSample = AllocateSample(sample.nLength * 2 + 16); - IMAADPCMUnpack16((signed short *)sample.pSample, sample.nLength, - (LPBYTE)(lpMemFile + dwDataPos), dwFileLength - dwDataPos, LittleEndianW(pfmtpk->samplesize)); + sample.uFlags |= CHN_16BIT; + if(!sample.AllocateSample()) + { + return false; + } + IMAADPCMUnpack16((int16 *)sample.pSample, sample.nLength, sampleChunk.GetRawData(), sampleChunk.BytesLeft(), wavFile.GetBlockAlign()); AdjustSampleLoop(sample); } else { - // Some samples have an incorrect blockAlign/sample size set (e.g. it's 8 in SQUARE.WAV while it should be 1), so let's better not trust this value. - sample.nLength = LittleEndian(pdata->length) / (((channels * bitsPerSmp) + 7) / 8); - + // PCM / Float SampleIO sampleIO( SampleIO::_8bit, - (channels > 1) ? SampleIO::stereoInterleaved : SampleIO::mono, + (wavFile.GetNumChannels() > 1) ? SampleIO::stereoInterleaved : SampleIO::mono, SampleIO::littleEndian, - (bitsPerSmp > 8) ? SampleIO::signedPCM : SampleIO::unsignedPCM); + (wavFile.GetBitsPerSample() > 8) ? SampleIO::signedPCM : SampleIO::unsignedPCM); - if(bitsPerSmp <= 8) + if(wavFile.GetBitsPerSample() <= 8) sampleIO |= SampleIO::_8bit; - else if(bitsPerSmp <= 16) + else if(wavFile.GetBitsPerSample() <= 16) sampleIO |= SampleIO::_16bit; - else if(bitsPerSmp <= 24) + else if(wavFile.GetBitsPerSample() <= 24) sampleIO |= SampleIO::_24bit; - else if(bitsPerSmp <= 32) + else if(wavFile.GetBitsPerSample() <= 32) sampleIO |= SampleIO::_32bit; - if(wavFormat == 3 /*WAVE_FORMAT_IEEE_FLOAT*/) + if(wavFile.GetSampleFormat() == WAVFormatChunk::fmtFloat) { sampleIO |= SampleIO::floatPCM; } - sampleIO.ReadSample(sample, (LPSTR)(lpMemFile + dwDataPos), dwFileLength - dwDataPos); + sampleIO.ReadSample(sample, sampleChunk); } - - bool fixSampleLoops = false; - // LIST field - if (dwInfoList) + if(wsmpChunk != nullptr) { - uint32 dwLSize = LittleEndian(*reinterpret_cast<uint32 *>(lpMemFile + dwInfoList + 4)) + 8; - uint32 d = 12; - while (d + 8 < dwLSize) - { - if (!lpMemFile[dwInfoList + d]) d++; - uint32 id = LittleEndian(*reinterpret_cast<uint32 *>(lpMemFile + dwInfoList + d)); - uint32 len = LittleEndian(*reinterpret_cast<uint32 *>(lpMemFile + dwInfoList + d + 4)); - - if ((dwInfoList + d + 8 + len <= dwFileLength) && (len)) - { - switch(id) - { - case IFFID_INAM: - StringFixer::ReadString<StringFixer::nullTerminated>(m_szNames[nSample], reinterpret_cast<const char *>(lpMemFile + dwInfoList + d + 8), len); - if (phdr->id_RIFF != LittleEndian(IFFID_RIFF)) - { - // DLS sample -> sample filename - StringFixer::ReadString<StringFixer::nullTerminated>(sample.filename, reinterpret_cast<const char *>(lpMemFile + dwInfoList + d + 8), len); - } - break; - - case IFFID_ISFT: - { - // MPT / old versions of OpenMPT stored the sample loop information incorrectly: - // In the RIFF standard, the loop end point is *inclusive*, in OpenMPT it's *exclusive*. - char *softwareId = reinterpret_cast<char *>(lpMemFile + dwInfoList + d + 8); - fixSampleLoops = !strncmp(softwareId, "Modplug Tracker", min(len, 16)); - } - break; - } - } - d += 8 + len; - } + // DLS WSMP chunk + *wsmpChunk = wavFile.GetWsmpChunk(); } - // smpl field - if (psh) - { - sample.nLoopStart = sample.nLoopEnd = 0; - int numLoops = LittleEndian(psh->dwSampleLoops); - if(numLoops && (sizeof(WAVESMPLHEADER) + numLoops * sizeof(SAMPLELOOPSTRUCT) <= LittleEndian(psh->smpl_len) + 8)) - { - SAMPLELOOPSTRUCT *psl = (SAMPLELOOPSTRUCT *)(&psh[1]); - if(numLoops > 1) - { - sample.uFlags |= (CHN_LOOP | CHN_SUSTAINLOOP); - if(LittleEndian(psl[0].dwLoopType)) sample.uFlags |= CHN_PINGPONGSUSTAIN; - if(LittleEndian(psl[1].dwLoopType)) sample.uFlags |= CHN_PINGPONGLOOP; - sample.nSustainStart = LittleEndian(psl[0].dwLoopStart); - sample.nSustainEnd = LittleEndian(psl[0].dwLoopEnd); - sample.nLoopStart = LittleEndian(psl[1].dwLoopStart); - sample.nLoopEnd = LittleEndian(psl[1].dwLoopEnd); - } else - { - sample.uFlags |= CHN_LOOP; - if(LittleEndian(psl->dwLoopType)) sample.uFlags |= CHN_PINGPONGLOOP; - sample.nLoopStart = LittleEndian(psl->dwLoopStart); - sample.nLoopEnd = LittleEndian(psl->dwLoopEnd); - } - - if(!fixSampleLoops) - { - // RIFF loop end points are inclusive - if(sample.nLoopEnd < sample.nLength && (sample.uFlags & CHN_LOOP)) - { - sample.nLoopEnd++; - } - - if(sample.nSustainEnd < sample.nLength && (sample.uFlags & CHN_SUSTAINLOOP)) - { - sample.nSustainEnd++; - } - } - - if (sample.nLoopStart >= sample.nLoopEnd) sample.uFlags &= ~(CHN_LOOP|CHN_PINGPONGLOOP); - if (sample.nSustainStart >= sample.nSustainEnd) sample.uFlags &= ~(CHN_PINGPONGLOOP|CHN_PINGPONGSUSTAIN); - } - } - - // xtra field - if (pxh) - { - DWORD extFlags = LittleEndian(pxh->dwFlags); - if (extFlags & CHN_PINGPONGLOOP) sample.uFlags |= CHN_PINGPONGLOOP; - if (extFlags & CHN_SUSTAINLOOP) sample.uFlags |= CHN_SUSTAINLOOP; - if (extFlags & CHN_PINGPONGSUSTAIN) sample.uFlags |= CHN_PINGPONGSUSTAIN; - if (extFlags & CHN_PANNING) sample.uFlags |= CHN_PANNING; - - sample.nPan = LittleEndianW(pxh->wPan); - sample.nVolume = LittleEndianW(pxh->wVolume); - sample.nGlobalVol = LittleEndianW(pxh->wGlobalVol); - sample.nVibType = pxh->nVibType; - sample.nVibSweep = pxh->nVibSweep; - sample.nVibDepth = pxh->nVibDepth; - sample.nVibRate = pxh->nVibRate; - // Name present (clipboard only) - UINT xtrabytes = LittleEndian(pxh->xtra_len) - (sizeof(WAVEEXTRAHEADER) - 8); - LPSTR pszTextEx = (LPSTR)(pxh + 1); - if (xtrabytes >= MAX_SAMPLENAME) - { - StringFixer::ReadString<StringFixer::nullTerminated>(m_szNames[nSample], pszTextEx, MAX_SAMPLENAME); - pszTextEx += MAX_SAMPLENAME; - xtrabytes -= MAX_SAMPLENAME; - if(xtrabytes > 0) - { - StringFixer::ReadString<StringFixer::nullTerminated>(sample.filename, pszTextEx, xtrabytes); - } - } - } - sample.Convert(MOD_TYPE_IT, GetType()); return true; } @@ -606,8 +373,8 @@ // Save WAV -bool CSoundFile::SaveWAVSample(UINT nSample, const LPCSTR lpszFileName) const -//--------------------------------------------------------------------------- +bool CSoundFile::SaveWAVSample(SAMPLEINDEX nSample, const LPCSTR lpszFileName) const +//---------------------------------------------------------------------------------- { char *softwareId = "OpenMPT " MPT_VERSION_STR; size_t softwareIdLength = strlen(softwareId) + 1; @@ -714,6 +481,11 @@ list.list_len = LittleEndian(softwareIdLength); fwrite(&list, 1, 8, f); fwrite(softwareId, 1, list.list_len, f); + if((softwareIdLength % 2) != 0) + { + int8 padding = 0; + fwrite(&padding, 1, 1, f); + } // "xtra" field extra.xtra_id = LittleEndian(IFFID_xtra); @@ -743,8 +515,8 @@ /////////////////////////////////////////////////////////////// // Save RAW -bool CSoundFile::SaveRAWSample(UINT nSample, const LPCSTR lpszFileName) const -//--------------------------------------------------------------------------- +bool CSoundFile::SaveRAWSample(SAMPLEINDEX nSample, const LPCSTR lpszFileName) const +//---------------------------------------------------------------------------------- { FILE *f; if ((f = fopen(lpszFileName, "wb")) == NULL) return false; @@ -875,8 +647,8 @@ } -void PatchToSample(CSoundFile *that, UINT nSample, LPBYTE lpStream, DWORD dwMemLength) -//------------------------------------------------------------------------------------ +void PatchToSample(CSoundFile *that, SAMPLEINDEX nSample, LPBYTE lpStream, DWORD dwMemLength) +//------------------------------------------------------------------------------------------- { ModSample &sample = that->GetSample(nSample); DWORD dwMemPos = sizeof(GF1SAMPLEHEADER); @@ -901,8 +673,11 @@ sample.nVibDepth = psh->vibrato_depth; sample.nVibRate = psh->vibrato_rate/4; CSoundFile::FrequencyToTranspose(&sample); - sample.RelativeTone += 84 - PatchFreqToNote(psh->root_freq); - if (psh->scale_factor) sample.RelativeTone -= psh->scale_frequency - 60; + sample.RelativeTone += static_cast<uint8>(84 - PatchFreqToNote(psh->root_freq)); + if(psh->scale_factor) + { + sample.RelativeTone = static_cast<uint8>(sample.RelativeTone - psh->scale_frequency - 60); + } sample.nC5Speed = CSoundFile::TransposeToFrequency(sample.RelativeTone, sample.nFineTune); SampleIO sampleIO( @@ -983,9 +758,9 @@ pIns->nNNA = NNA_NOTEOFF; pIns->nDNA = DNA_NOTEFADE; } - UINT nFreeSmp = 0; + SAMPLEINDEX nFreeSmp = 0; UINT nMinSmpNote = 0xff; - UINT nMinSmp = 0; + SAMPLEINDEX nMinSmp = 0; for (UINT iSmp=0; iSmp<nSamples; iSmp++) { // Find a free sample @@ -1389,7 +1164,7 @@ }; uint32 magic; // FORM - uint32 length; // Size of the file, not including magic an length + uint32 length; // Size of the file, not including magic and length uint32 type; // AIFF or AIFC // Convert all multi-byte numeric values to current platform's endianness or vice versa. @@ -1414,6 +1189,8 @@ idNAME = 0x4E414D45, }; + typedef ChunkIdentifiers id_type; + uint32 id; // See ChunkIdentifiers uint32 length; // Chunk size without header @@ -1562,8 +1339,7 @@ // Read COMM chunk FileReader commChunk(chunks.GetChunk(AIFFChunk::idCOMM)); AIFFCommonChunk sampleInfo; - if(!commChunk.IsValid() - || !commChunk.ReadConvertEndianness(sampleInfo)) + if(!commChunk.ReadConvertEndianness(sampleInfo)) { return false; } @@ -1591,8 +1367,7 @@ // Read SSND chunk FileReader soundChunk(chunks.GetChunk(AIFFChunk::idSSND)); AIFFSoundChunk sampleHeader; - if(!soundChunk.IsValid() - || !soundChunk.ReadConvertEndianness(sampleHeader) + if(!soundChunk.ReadConvertEndianness(sampleHeader) || soundChunk.BytesLeft() <= sampleHeader.offset) { return false; @@ -1643,9 +1418,8 @@ // Read MARK and INST chunk to extract sample loops FileReader markerChunk(chunks.GetChunk(AIFFChunk::idMARK)); - FileReader instChunk(chunks.GetChunk(AIFFChunk::idINST)); AIFFInstrumentChunk instrHeader; - if(markerChunk.IsValid() && instChunk.IsValid() && instChunk.ReadConvertEndianness(instrHeader)) + if(markerChunk.IsValid() && chunks.GetChunk(AIFFChunk::idINST).ReadConvertEndianness(instrHeader)) { size_t numMarkers = markerChunk.ReadUint16BE(); @@ -1760,7 +1534,7 @@ //---------------------------------------------------------------------------------------------- { ITInstrument *pinstr = (ITInstrument *)lpMemFile; - UINT nsmp = 0, nsamples; + SAMPLEINDEX nsmp = 0, nsamples; if ((!lpMemFile) || (dwFileLength < sizeof(ITInstrument)) || (pinstr->id != LittleEndian(ITInstrument::magic))) return false; @@ -1862,9 +1636,9 @@ { // We haven't considered this sample yet. smptable.push_back(smp); - smpmap[smp - 1] = smptable.size(); + smpmap[smp - 1] = static_cast<SAMPLEINDEX>(smptable.size()); } - iti.iti.keyboard[i * 2 + 1] = smpmap[smp - 1]; + iti.iti.keyboard[i * 2 + 1] = static_cast<uint8>(smpmap[smp - 1]); } else { iti.iti.keyboard[i * 2 + 1] = 0; @@ -2049,8 +1823,8 @@ -bool CSoundFile::Read8SVXSample(UINT nSample, LPBYTE lpMemFile, DWORD dwFileLength) -//--------------------------------------------------------------------------------- +bool CSoundFile::Read8SVXSample(SAMPLEINDEX nSample, LPBYTE lpMemFile, DWORD dwFileLength) +//---------------------------------------------------------------------------------------- { IFF8SVXFILEHEADER *pfh = (IFF8SVXFILEHEADER *)lpMemFile; IFFVHDR *pvh = (IFFVHDR *)(lpMemFile + 12); Modified: trunk/OpenMPT/soundlib/Sndfile.cpp =================================================================== --- trunk/OpenMPT/soundlib/Sndfile.cpp 2012-05-26 14:55:51 UTC (rev 1286) +++ trunk/OpenMPT/soundlib/Sndfile.cpp 2012-05-27 15:52:58 UTC (rev 1287) @@ -611,7 +611,7 @@ && !ReadIT(lpStream, dwMemLength) /*&& !ReadMPT(lpStream, dwMemLength)*/ && !ReadS3M(file) - && !ReadWav(lpStream, dwMemLength) + && !ReadWav(file) #ifndef MODPLUG_BASIC_SUPPORT && !ReadSTM(lpStream, dwMemLength) && !ReadMed(lpStream, dwMemLength) Modified: trunk/OpenMPT/soundlib/Sndfile.h =================================================================== --- trunk/OpenMPT/soundlib/Sndfile.h 2012-05-26 14:55:51 UTC (rev 1286) +++ trunk/OpenMPT/soundlib/Sndfile.h 2012-05-27 15:52:58 UTC (rev 1287) @@ -18,7 +18,6 @@ #include <bitset> #include <set> #include "Snd_defs.h" -#include "Endianness.h" #include "tuning.h" #include "MIDIMacros.h" #ifdef MODPLUG_TRACKER @@ -389,7 +388,7 @@ bool ReadITProject(const LPCBYTE lpStream, const DWORD dwMemLength); // -> CODE#0023 -> DESC="IT project files (.itp)" -! NEW_FEATURE#0023 bool Read669(FileReader &file); bool ReadUlt(const LPCBYTE lpStream, const DWORD dwMemLength); - bool ReadWav(const LPCBYTE lpStream, const DWORD dwMemLength); + bool ReadWav(FileReader &file); bool ReadDSM(const LPCBYTE lpStream, const DWORD dwMemLength); bool ReadFAR(const LPCBYTE lpStream, const DWORD dwMemLength); bool ReadAMS(const LPCBYTE lpStream, const DWORD dwMemLength); @@ -624,15 +623,15 @@ // Samples file I/O bool ReadSampleFromFile(SAMPLEINDEX nSample, const LPBYTE lpMemFile, DWORD dwFileLength); - bool ReadWAVSample(SAMPLEINDEX nSample, const LPBYTE lpMemFile, DWORD dwFileLength, DWORD *pdwWSMPOffset=NULL); + bool ReadWAVSample(SAMPLEINDEX nSample, FileReader &file, FileReader *wsmpChunk = nullptr); bool ReadPATSample(SAMPLEINDEX nSample, const LPBYTE lpMemFile, DWORD dwFileLength); bool ReadS3ISample(SAMPLEINDEX nSample, const LPBYTE lpMemFile, DWORD dwFileLength); bool ReadAIFFSample(SAMPLEINDEX nSample, FileReader &file); bool ReadXISample(SAMPLEINDEX nSample, const LPBYTE lpMemFile, DWORD dwFileLength); UINT ReadITSSample(SAMPLEINDEX nSample, const LPBYTE lpMemFile, DWORD dwFileLength, DWORD dwOffset=0); - bool Read8SVXSample(UINT nInstr, const LPBYTE lpMemFile, DWORD dwFileLength); - bool SaveWAVSample(UINT nSample, const LPCSTR lpszFileName) const; - bool SaveRAWSample(UINT nSample, const LPCSTR lpszFileName) const; + bool Read8SVXSample(SAMPLEINDEX nInstr, const LPBYTE lpMemFile, DWORD dwFileLength); + bool SaveWAVSample(SAMPLEINDEX nSample, const LPCSTR lpszFileName) const; + bool SaveRAWSample(SAMPLEINDEX nSample, const LPCSTR lpszFileName) const; // Instrument file I/O bool ReadInstrumentFromFile(INSTRUMENTINDEX nInstr, const LPBYTE lpMemFile, DWORD dwFileLength); Added: trunk/OpenMPT/soundlib/WAVTools.cpp =================================================================== --- trunk/OpenMPT/soundlib/WAVTools.cpp (rev 0) +++ trunk/OpenMPT/soundlib/WAVTools.cpp 2012-05-27 15:52:58 UTC (rev 1287) @@ -0,0 +1,191 @@ +/* + * WAVTools.cpp + * ------------ + * Purpose: Definition of WAV file structures and helper functions + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "WAVTools.h" + + +WAVReader::WAVReader(FileReader &inputFile) : file(inputFile) +//----------------------------------------------------------- +{ + file.Rewind(); + + RIFFHeader fileHeader; + if(!file.ReadConvertEndianness(fileHeader) + || (fileHeader.magic != RIFFHeader::idRIFF && fileHeader.magic != RIFFHeader::idLIST) + || (fileHeader.type != RIFFHeader::idWAVE && fileHeader.type != RIFFHeader::idwave)) + { + return; + } + + isDLS = (fileHeader.magic == RIFFHeader::idLIST); + + ChunkReader::ChunkList<RIFFChunk> chunks = file.ReadChunks<RIFFChunk>(2); + + if(chunks.size() >= 4 + && chunks[1].GetHeader().GetID() == RIFFChunk::iddata + && chunks[1].GetHeader().GetLength() % 2 != 0 + && chunks[2].GetHeader().GetLength() == 0 + && chunks[3].GetHeader().GetID() == RIFFChunk::id____) + { + // Houston, we have a problem: Old versions of (Open)MPT didn't write RIFF padding bytes. -_- + // Luckily, the only RIFF chunk with an odd size those versions would ever write would be the "data" chunk + // (which contains the sample data), and its size is only odd iff the sample has an odd length and is in + // 8-Bit mono format. In all other cases, the sample size (and thus the chunk size) is even. + + // And we're even more lucky: The versions of (Open)MPT in question will always write a relatively small + // (smaller than 256 bytes) "smpl" chunk after the "data" chunk. This means that after an unpadded sample, + // we will always read "mpl?" (? being the length of the "smpl" chunk) as the next chunk magic. The first two + // 32-Bit members of the "smpl" chunk are always zero in our case, so we are going to read a chunk length of 0 + // next and the next chunk magic, which will always consist of four zero bytes. Hooray! We just checked for those + // four zero bytes and can be pretty confident that we should not have applied padding. + file.Seek(sizeof(RIFFHeader)); + chunks = file.ReadChunks<RIFFChunk>(1); + } + + // Read format chunk + FileReader formatChunk = chunks.GetChunk(RIFFChunk::idfmt_); + if(!formatChunk.ReadConvertEndianness(formatInfo)) + { + return; + } + if(formatInfo.format == WAVFormatChunk::fmtExtensible) + { + WAVFormatChunkExtension extFormat; + if(!formatChunk.ReadConvertEndianness(extFormat)) + { + return; + } + formatInfo.format = extFormat.subFormat; + } + + // Read sample data + sampleData = chunks.GetChunk(RIFFChunk::iddata); + + if(!sampleData.IsValid() && chunks.ChunkExists(RIFFChunk::idpcm_)) + { + // The old IMA ADPCM loader code looked for the "pcm " chunk instead of the "data" chunk... + // Dunno why, but we will just look for both. + sampleData = chunks.GetChunk(RIFFChunk::idpcm_); + } + + // "fact" chunk should contain sample length of compressed samples. + sampleLength = chunks.GetChunk(RIFFChunk::idfact).ReadUint32LE(); + + if(formatInfo.format != WAVFormatChunk::fmtIMA_ADPCM || sampleLength == 0) + { + // Some samples have an incorrect blockAlign / sample size set (e.g. it's 8 in SQUARE.WAV while it should be 1), so let's better not trust this value. + sampleLength = sampleData.GetLength() / GetSampleSize(); + } + + // Read sample loop points + FileReader smplChunk(chunks.GetChunk(RIFFChunk::idsmpl)); + WAVSampleInfoChunk sampleInfo; + if(smplChunk.ReadConvertEndianness(sampleInfo)) + { + for(size_t i = 0; i < sampleInfo.numLoops; i++) + { + WAVSampleLoop loopData; + if(smplChunk.ReadConvertEndianness(loopData)) + { + sampleLoops.push_back(loopData); + } + } + } + + // Read text chunks + ChunkReader listChunk = chunks.GetChunk(RIFFChunk::idLIST); + if(listChunk.ReadMagic("INFO")) + { + infoChunk = listChunk.ReadChunks<RIFFChunk>(2); + } + + // Read MPT sample information + xtraChunk = chunks.GetChunk(RIFFChunk::idxtra); + + // DLS bank chunk + wsmpChunk = chunks.GetChunk(RIFFChunk::idwsmp); +} + + +void WAVReader::ApplySampleSettings(ModSample &sample, char (&sampleName)[MAX_SAMPLENAME]) +//---------------------------------------------------------------------------------------- +{ + // Read sample name + FileReader textChunk = infoChunk.GetChunk(RIFFChunk::idINAM); + textChunk.ReadString<StringFixer::nullTerminated>(sampleName, textChunk.GetLength()); + if(isDLS) + { + // DLS sample -> sample filename + strncpy(sample.filename, sampleName, CountOf(sample.filename)); + StringFixer::SetNullTerminator(sample.filename); + } + + // Read software name + const bool isOldMPT = infoChunk.GetChunk(RIFFChunk::idISFT).ReadMagic("Modplug Tracker"); + + sample.uFlags &= ~(CHN_LOOP | CHN_SUSTAINLOOP); + + // Convert loops + if(!sampleLoops.empty()) + { + size_t normalLoopIndex = 0; + if(sampleLoops.size() > 1) + { + sampleLoops[0].ApplyToSample(sample.nSustainStart, sample.nSustainStart, sample.nLength, sample.uFlags, CHN_SUSTAINLOOP, CHN_PINGPONGSUSTAIN, isOldMPT); + normalLoopIndex = 1; + } + sampleLoops[normalLoopIndex].ApplyToSample(sample.nLoopStart, sample.nLoopEnd, sample.nLength, sample.uFlags, CHN_LOOP, CHN_PINGPONGLOOP, isOldMPT); + } + + WAVExtraChunk mptInfo; + xtraChunk.Rewind(); + if(xtraChunk.ReadConvertEndianness(mptInfo)) + { + if(mptInfo.flags & WAVExtraChunk::bidiLoop) sample.uFlags |= CHN_PINGPONGLOOP; + if(mptInfo.flags & WAVExtraChunk::sustainLoop) sample.uFlags |= CHN_SUSTAINLOOP; + if(mptInfo.flags & WAVExtraChunk::sustainBidi) sample.uFlags |= CHN_PINGPONGSUSTAIN; + if(mptInfo.flags & WAVExtraChunk::setPanning) sample.uFlags |= CHN_PANNING; + + sample.nPan = Util::Min(mptInfo.defaultPan, uint16(256)); + sample.nVolume = Util::Min(mptInfo.defaultVolume, uint16(256)); + sample.nGlobalVol = Util::Min(mptInfo.globalVolume, uint16(64)); + sample.nVibType = mptInfo.vibratoType; + sample.nVibSweep = mptInfo.vibratoSweep; + sample.nVibDepth = mptInfo.vibratoDepth; + sample.nVibRate = mptInfo.vibratoRate; + + if(xtraChunk.BytesLeft() >= MAX_SAMPLENAME) + { + // Name present (clipboard only) + xtraChunk.ReadString<StringFixer::nullTerminated>(sampleName, MAX_SAMPLENAME); + xtraChunk.ReadString<StringFixer::nullTerminated>(sample.filename, xtraChunk.BytesLeft()); + } + } +} + + +void WAVSampleLoop::ApplyToSample(SmpLength &start, SmpLength &end, uint32 sampleLength, uint16 &flags, uint16 enableFlag, uint16 bidiFlag, bool mptLoopFix) const +//---------------------------------------------------------------------------------------------------------------------------------------------------------------- +{ + start = Util::Min(static_cast<SmpLength>(loopStart), sampleLength); + end = Clamp(static_cast<SmpLength>(loopEnd), start, sampleLength); + if(!mptLoopFix && end < sampleLength) + { + // RIFF loop end points are inclusive - old versions of MPT didn't consider this. + end++; + } + + flags |= enableFlag; + if(loopType == loopBidi) + { + flags |= bidiFlag; + } +} Added: trunk/OpenMPT/soundlib/WAVTools.h =================================================================== --- trunk/OpenMPT/soundlib/WAVTools.h (rev 0) +++ trunk/OpenMPT/soundlib/WAVTools.h 2012-05-27 15:52:58 UTC (rev 1287) @@ -0,0 +1,266 @@ +/* + * WAVTools.h + * ---------- + * Purpose: Definition of WAV file structures and helper functions + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "ChunkReader.h" + +#pragma pack(push, 1) + +// RIFF header +struct RIFFHeader +{ + // 32-Bit chunk identifiers + enum RIFFMagic + { + idRIFF = 0x46464952, // magic for WAV files + idLIST = 0x5453494C, // magic for samples in DLS banks + idWAVE = 0x45564157, // type for WAV files + idwave = 0x65766177, // type for samples in DLS banks + }; + + uint32 magic; // RIFF (in WAV files) or LIST (in DLS banks) + uint32 length; // Size of the file, not including magic and length + uint32 type; // WAVE (in WAV files) or wave (in DLS banks) + + // Convert all multi-byte numeric values to current platform's endianness or vice versa. + void ConvertEndianness() + { + SwapBytesLE(magic); + SwapBytesLE(length); + SwapBytesLE(type); + } +}; + + +// General RIFF Chunk header +struct RIFFChunk +{ + // 32-Bit chunk identifiers + enum ChunkIdentifiers + { + idfmt_ = 0x20746D66, // "fmt " + iddata = 0x61746164, // "data" + idpcm_ = 0x206d6370, // "pcm " (IMA ADPCM samples) + idfact = 0x74636166, // "fact" (compressed samples) + idsmpl = 0x6C706D73, // "smpl" + idLIST = 0x5453494C, // "LIST" + idxtra = 0x61727478, // "xtra" + idcue_ = 0x20657563, // "cue " + idwsmp = 0x706D7377, // "wsmp" (DLS bank samples) + id____ = 0x00000000, // Found when loading buggy MPT samples + + // Identifiers in "LIST" chunk + idINAM = 0x4D414E49, // "INAM" + idISFT = 0x54465349, // "ISFT" + }; + + typedef ChunkIdentifiers id_type; + + uint32 id; // See ChunkIdentifiers + uint32 length; // Chunk size without header + + size_t GetLength() const + { + uint32 l = length; + return SwapBytesLE(l); + } + + ChunkIdentifiers GetID() const + { + uint32 i = id; + return static_cast<ChunkIdentifiers>(SwapBytesLE(i)); + } +}; + + +// Format Chunk +struct WAVFormatChunk +{ + // Sample formats + enum SampleFormats + { + fmtPCM = 1, + fmtFloat = 3, + fmtIMA_ADPCM = 17, + fmtExtensible = 0xFFFE, + }; + + uint16 format; // Sample format, see SampleFormats + uint16 numChannels; // Number of audio channels + uint32 sampleRate; // Sample rate in Hz + uint32 byteRate; // Bytes per second (should be freqHz * blockAlign) + uint16 blockAlign; // Size of a sample, in bytes (do not trust this value, it's incorrect in some files) + uint16 bitsPerSample; // Bits per sample + + // Convert all multi-byte numeric values to current platform's endianness or vice versa. + void ConvertEndianness() + { + SwapBytesLE(format); + SwapBytesLE(numChannels); + SwapBytesLE(sampleRate); + SwapBytesLE(byteRate); + SwapBytesLE(blockAlign); + SwapBytesLE(bitsPerSample); + } +}; + + +// Extension of the WAVFormatChunk structure, used if format == formatExtensible +struct WAVFormatChunkExtension +{ + uint16 size; + uint16 validBitsPerSample; + uint32 channelMask; + uint16 subFormat; + + // Convert all multi-byte numeric values to current platform's endianness or vice versa. + void ConvertEndianness() + { + SwapBytesLE(size); + SwapBytesLE(validBitsPerSample); + SwapBytesLE(channelMask); + SwapBytesLE(subFormat); + } +}; + + +// Sample information chunk +struct WAVSampleInfoChunk +{ + uint32 manufacturer; + uint32 product; + uint32 samplePeriod; // 1000000000 / sampleRate + uint32 baseNote; // 3Ch = C-4 -> 60 + RelativeTone + uint32 pitchFraction; + uint32 SMPTEFormat; + uint32 SMPTEOffset; + uint32 numLoops; // number of loops + uint32 samplerData; + + // Convert all multi-byte numeric values to current platform's endianness or vice versa. + void ConvertEndianness() + { + SwapBytesLE(manufacturer); + SwapBytesLE(product); + SwapBytesLE(samplePeriod); + SwapBytesLE(baseNote); + SwapBytesLE(pitchFraction); + SwapBytesLE(SMPTEFormat); + SwapBytesLE(SMPTEOffset); + SwapBytesLE(numLoops); + SwapBytesLE(samplerData); + } +}; + + +// Sample loop information chunk (found after WAVSampleInfoChunk in "smpl" chunk) +struct WAVSampleLoop +{ + // Sample Loop Types + enum LoopType + { + loopForward = 0, + loopBidi = 1, + loopBackward = 2, + }; + + uint32 identifier; + uint32 loopType; // See LoopType + uint32 loopStart; // Loop start in samples + uint32 loopEnd; // Loop end in samples + uint32 fraction; + uint32 playCount; // Loop Count, 0 = infinite + + // Convert all... [truncated message content] |