PageRenderTime 60ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/libs/openmpt/soundlib/ITCompression.cpp

https://gitlab.com/maep/demosauce
C++ | 443 lines | 335 code | 73 blank | 35 comment | 65 complexity | 4714e4bc8bd9c9a58db7b4acf435bd53 MD5 | raw file
Possible License(s): ISC
  1. /*
  2. * ITCompression.cpp
  3. * -----------------
  4. * Purpose: Code for IT sample compression and decompression.
  5. * Notes : The original Python compression code was written by GreaseMonkey and has been released into the public domain.
  6. * Authors: OpenMPT Devs
  7. * Ben "GreaseMonkey" Russell
  8. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  9. */
  10. #include "stdafx.h"
  11. #include <ostream>
  12. #include "ITCompression.h"
  13. #include "../common/misc_util.h"
  14. #include "../common/mptIO.h"
  15. #include "ModSample.h"
  16. OPENMPT_NAMESPACE_BEGIN
  17. // Algorithm parameters for 16-Bit samples
  18. struct IT16BitParams
  19. {
  20. typedef int16 sample_t;
  21. static const int16 lowerTab[];
  22. static const int16 upperTab[];
  23. static const int8 fetchA = 4, lowerB = -8, upperB = 7, defWidth = 17;
  24. static const int mask = 0xFFFF;
  25. };
  26. const int16 IT16BitParams::lowerTab[] = { 0, -1, -3, -7, -15, -31, -56, -120, -248, -504, -1016, -2040, -4088, -8184, -16376, -32760, -32768 };
  27. const int16 IT16BitParams::upperTab[] = { 0, 1, 3, 7, 15, 31, 55, 119, 247, 503, 1015, 2039, 4087, 8183, 16375, 32759, 32767 };
  28. // Algorithm parameters for 8-Bit samples
  29. struct IT8BitParams
  30. {
  31. typedef int8 sample_t;
  32. static const int8 lowerTab[];
  33. static const int8 upperTab[];
  34. static const int8 fetchA = 3, lowerB = -4, upperB = 3, defWidth = 9;
  35. static const int mask = 0xFF;
  36. };
  37. const int8 IT8BitParams::lowerTab[] = { 0, -1, -3, -7, -15, -31, -60, -124, -128 };
  38. const int8 IT8BitParams::upperTab[] = { 0, 1, 3, 7, 15, 31, 59, 123, 127 };
  39. static const int8 ITWidthChangeSize[] = { 4, 5, 6, 7, 8, 9, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 };
  40. //////////////////////////////////////////////////////////////////////////////
  41. // IT 2.14 compression
  42. ITCompression::ITCompression(const ModSample &sample, bool it215, std::ostream *f, SmpLength maxLength)
  43. : file(f)
  44. , mptSample(sample)
  45. , is215(it215)
  46. {
  47. packedData = new (std::nothrow) uint8[bufferSize];
  48. sampleData = new (std::nothrow) uint8[blockSize];
  49. packedTotalLength = 0;
  50. if(packedData == nullptr || sampleData == nullptr)
  51. {
  52. return;
  53. }
  54. if(maxLength == 0 || maxLength > mptSample.nLength)
  55. maxLength = mptSample.nLength;
  56. for(uint8 chn = 0; chn < mptSample.GetNumChannels(); chn++)
  57. {
  58. SmpLength offset = 0;
  59. SmpLength remain = maxLength;
  60. while(remain > 0)
  61. {
  62. // Initialise output buffer and bit writer positions
  63. packedLength = 2;
  64. bitPos = 0;
  65. remBits = 8;
  66. byteVal = 0;
  67. if(mptSample.GetElementarySampleSize() > 1)
  68. Compress<IT16BitParams>(sample.pSample16 + chn, offset, remain);
  69. else
  70. Compress<IT8BitParams>(sample.pSample8 + chn, offset, remain);
  71. if(file) mpt::IO::WriteRaw(*file, packedData, packedLength);
  72. packedTotalLength += packedLength;
  73. offset += baseLength;
  74. remain -= baseLength;
  75. }
  76. }
  77. delete[] packedData;
  78. delete[] reinterpret_cast<uint8*>(sampleData);
  79. }
  80. template<typename T>
  81. void ITCompression::CopySample(void *target, const void *source, SmpLength offset, SmpLength length, SmpLength skip)
  82. {
  83. T *out = static_cast<T *>(target);
  84. const T *in = static_cast<const T *>(source) + offset * skip;
  85. for(SmpLength i = 0, j = 0; j < length; i += skip, j++)
  86. {
  87. out[j] = in[i];
  88. }
  89. }
  90. // Convert sample to delta values.
  91. template<typename T>
  92. void ITCompression::Deltafy()
  93. {
  94. T *p = static_cast<T *>(sampleData);
  95. int oldVal = 0;
  96. for(SmpLength i = 0; i < baseLength; i++)
  97. {
  98. int newVal = p[i];
  99. p[i] = static_cast<T>(newVal - oldVal);
  100. oldVal = newVal;
  101. }
  102. }
  103. template<typename Properties>
  104. void ITCompression::Compress(const void *data, SmpLength offset, SmpLength actualLength)
  105. {
  106. baseLength = std::min(actualLength, SmpLength(blockSize / sizeof(typename Properties::sample_t)));
  107. CopySample<typename Properties::sample_t>(sampleData, data, offset, baseLength, mptSample.GetNumChannels());
  108. Deltafy<typename Properties::sample_t>();
  109. if(is215)
  110. {
  111. Deltafy<typename Properties::sample_t>();
  112. }
  113. const int8 defWidth = Properties::defWidth; // gcc static const member reference workaround
  114. // Initialise bit width table with initial values
  115. bwt.assign(baseLength, defWidth);
  116. // Recurse!
  117. SquishRecurse<Properties>(defWidth, defWidth, defWidth, defWidth - 2, 0, baseLength);
  118. // Write those bits!
  119. const typename Properties::sample_t *p = static_cast<typename Properties::sample_t *>(sampleData);
  120. int8 width = defWidth;
  121. for(size_t i = 0; i < baseLength; i++)
  122. {
  123. if(bwt[i] != width)
  124. {
  125. if(width <= 6)
  126. {
  127. // Mode A: 1 to 6 bits
  128. MPT_ASSERT(width);
  129. WriteBits(width, (1 << (width - 1)));
  130. WriteBits(Properties::fetchA, ConvertWidth(width, bwt[i]));
  131. } else if(width < defWidth)
  132. {
  133. // Mode B: 7 to 8 / 16 bits
  134. int xv = (1 << (width - 1)) + Properties::lowerB + ConvertWidth(width, bwt[i]);
  135. WriteBits(width, xv);
  136. } else
  137. {
  138. // Mode C: 9 / 17 bits
  139. MPT_ASSERT((bwt[i] - 1) >= 0);
  140. WriteBits(width, (1 << (width - 1)) + bwt[i] - 1);
  141. }
  142. width = bwt[i];
  143. }
  144. WriteBits(width, static_cast<int>(p[i]) & Properties::mask);
  145. }
  146. // Write last byte and update block length
  147. WriteByte(byteVal);
  148. packedData[0] = static_cast<uint8>((packedLength - 2) & 0xFF);
  149. packedData[1] = static_cast<uint8>((packedLength - 2) >> 8);
  150. }
  151. int8 ITCompression::GetWidthChangeSize(int8 w, bool is16)
  152. {
  153. MPT_ASSERT(w > 0 && static_cast<unsigned int>(w) <= CountOf(ITWidthChangeSize));
  154. int8 wcs = ITWidthChangeSize[w - 1];
  155. if(w <= 6 && is16)
  156. wcs++;
  157. return wcs;
  158. }
  159. template<typename Properties>
  160. void ITCompression::SquishRecurse(int8 sWidth, int8 lWidth, int8 rWidth, int8 width, SmpLength offset, SmpLength length)
  161. {
  162. if(width + 1 < 1)
  163. {
  164. for(SmpLength i = offset; i < offset + length; i++)
  165. bwt[i] = sWidth;
  166. return;
  167. }
  168. MPT_ASSERT(width >= 0 && static_cast<unsigned int>(width) < CountOf(Properties::lowerTab));
  169. SmpLength i = offset;
  170. SmpLength end = offset + length;
  171. const typename Properties::sample_t *p = static_cast<typename Properties::sample_t *>(sampleData);
  172. while(i < end)
  173. {
  174. if(p[i] >= Properties::lowerTab[width] && p[i] <= Properties::upperTab[width])
  175. {
  176. SmpLength start = i;
  177. // Check for how long we can keep this bit width
  178. while(i < end && p[i] >= Properties::lowerTab[width] && p[i] <= Properties::upperTab[width])
  179. {
  180. i++;
  181. }
  182. const SmpLength blockLength = i - start;
  183. const int8 xlwidth = start == offset ? lWidth : sWidth;
  184. const int8 xrwidth = i == end ? rWidth : sWidth;
  185. const bool is16 = sizeof(typename Properties::sample_t) > 1;
  186. const int8 wcsl = GetWidthChangeSize(xlwidth, is16);
  187. const int8 wcss = GetWidthChangeSize(sWidth, is16);
  188. const int8 wcsw = GetWidthChangeSize(width + 1, is16);
  189. bool comparison;
  190. if(i == baseLength)
  191. {
  192. SmpLength keepDown = wcsl + (width + 1) * blockLength;
  193. SmpLength levelLeft = wcsl + sWidth * blockLength;
  194. if(xlwidth == sWidth)
  195. levelLeft -= wcsl;
  196. comparison = (keepDown <= levelLeft);
  197. } else
  198. {
  199. SmpLength keepDown = wcsl + (width + 1) * blockLength + wcsw;
  200. SmpLength levelLeft = wcsl + sWidth * blockLength + wcss;
  201. if(xlwidth == sWidth)
  202. levelLeft -= wcsl;
  203. if(xrwidth == sWidth)
  204. levelLeft -= wcss;
  205. comparison = (keepDown <= levelLeft);
  206. }
  207. SquishRecurse<Properties>(comparison ? (width + 1) : sWidth, xlwidth, xrwidth, width - 1, start, blockLength);
  208. } else
  209. {
  210. bwt[i] = sWidth;
  211. i++;
  212. }
  213. }
  214. }
  215. int8 ITCompression::ConvertWidth(int8 curWidth, int8 newWidth)
  216. {
  217. curWidth--;
  218. newWidth--;
  219. MPT_ASSERT(newWidth != curWidth);
  220. if(newWidth > curWidth)
  221. newWidth--;
  222. return newWidth;
  223. }
  224. void ITCompression::WriteBits(int8 width, int v)
  225. {
  226. while(width > remBits)
  227. {
  228. byteVal |= (v << bitPos);
  229. width -= remBits;
  230. v >>= remBits;
  231. bitPos = 0;
  232. remBits = 8;
  233. WriteByte(byteVal);
  234. byteVal = 0;
  235. }
  236. if(width > 0)
  237. {
  238. byteVal |= (v & ((1 << width) - 1)) << bitPos;
  239. remBits -= width;
  240. bitPos += width;
  241. }
  242. }
  243. void ITCompression::WriteByte(uint8 v)
  244. {
  245. if(packedLength < bufferSize)
  246. {
  247. packedData[packedLength++] = v;
  248. } else
  249. {
  250. // How could this happen, anyway?
  251. MPT_ASSERT_NOTREACHED();
  252. }
  253. }
  254. //////////////////////////////////////////////////////////////////////////////
  255. // IT 2.14 decompression
  256. ITDecompression::ITDecompression(FileReader &file, ModSample &sample, bool it215)
  257. : mptSample(sample)
  258. , is215(it215)
  259. {
  260. for(uint8 chn = 0; chn < mptSample.GetNumChannels(); chn++)
  261. {
  262. writtenSamples = writePos = 0;
  263. while(writtenSamples < sample.nLength && file.CanRead(sizeof(uint16)))
  264. {
  265. dataSize = file.ReadUint16LE();
  266. if(!dataSize)
  267. continue; // Malformed sample?
  268. file.ReadRaw(chunk, dataSize);
  269. // Initialise bit reader
  270. dataPos = 0;
  271. bitPos = 0;
  272. remBits = 8;
  273. mem1 = mem2 = 0;
  274. if(mptSample.GetElementarySampleSize() > 1)
  275. Uncompress<IT16BitParams>(mptSample.pSample16 + chn);
  276. else
  277. Uncompress<IT8BitParams>(mptSample.pSample8 + chn);
  278. }
  279. }
  280. }
  281. template<typename Properties>
  282. void ITDecompression::Uncompress(typename Properties::sample_t *target)
  283. {
  284. curLength = std::min(mptSample.nLength - writtenSamples, SmpLength(ITCompression::blockSize / sizeof(typename Properties::sample_t)));
  285. const int defWidth = Properties::defWidth; // gcc static const member reference workaround
  286. int width = defWidth;
  287. while(curLength > 0)
  288. {
  289. if(width < 1 || width > defWidth || dataPos >= dataSize)
  290. {
  291. // Error!
  292. return;
  293. }
  294. int v = ReadBits(width);
  295. const int topBit = (1 << (width - 1));
  296. if(width <= 6)
  297. {
  298. // Mode A: 1 to 6 bits
  299. if(v == topBit)
  300. ChangeWidth(width, ReadBits(Properties::fetchA));
  301. else
  302. Write<Properties>(v, topBit, target);
  303. } else if(width < defWidth)
  304. {
  305. // Mode B: 7 to 8 / 16 bits
  306. if(v >= topBit + Properties::lowerB && v <= topBit + Properties::upperB)
  307. ChangeWidth(width, v - (topBit + Properties::lowerB));
  308. else
  309. Write<Properties>(v, topBit, target);
  310. } else
  311. {
  312. // Mode C: 9 / 17 bits
  313. if(v & topBit)
  314. width = (v & ~topBit) + 1;
  315. else
  316. Write<Properties>((v & ~topBit), 0, target);
  317. }
  318. }
  319. }
  320. #if MPT_MSVC_AT_LEAST(2017,3) && MPT_MSVC_BEFORE(2017,5)
  321. // Work-around compiler crash in VS2017.3 / cl 19.11.25506
  322. // https://developercommunity.visualstudio.com/content/problem/96687/c1063-and-c1001-while-compiling-trivial-code-in-vs.html
  323. MPT_NOINLINE
  324. #endif
  325. void ITDecompression::ChangeWidth(int &curWidth, int width)
  326. {
  327. width++;
  328. if(width >= curWidth)
  329. width++;
  330. curWidth = width;
  331. }
  332. #if MPT_MSVC_AT_LEAST(2017,3) && MPT_MSVC_BEFORE(2017,5)
  333. // Work-around compiler crash in VS2017.3 / cl 19.11.25506
  334. // https://developercommunity.visualstudio.com/content/problem/96687/c1063-and-c1001-while-compiling-trivial-code-in-vs.html
  335. MPT_NOINLINE
  336. #endif
  337. int ITDecompression::ReadBits(int width)
  338. {
  339. int v = 0, vPos = 0, vMask = (1 << width) - 1;
  340. while(width >= remBits && dataPos < dataSize)
  341. {
  342. v |= (chunk[dataPos] >> bitPos) << vPos;
  343. vPos += remBits;
  344. width -= remBits;
  345. dataPos++;
  346. remBits = 8;
  347. bitPos = 0;
  348. }
  349. if(width > 0 && dataPos < dataSize)
  350. {
  351. v |= (chunk[dataPos] >> bitPos) << vPos;
  352. v &= vMask;
  353. remBits -= width;
  354. bitPos += width;
  355. }
  356. return v;
  357. }
  358. template<typename Properties>
  359. void ITDecompression::Write(int v, int topBit, typename Properties::sample_t *target)
  360. {
  361. if(v & topBit)
  362. v -= (topBit << 1);
  363. mem1 += v;
  364. mem2 += mem1;
  365. target[writePos] = static_cast<typename Properties::sample_t>(static_cast<int>(is215 ? mem2 : mem1));
  366. writtenSamples++;
  367. writePos += mptSample.GetNumChannels();
  368. curLength--;
  369. }
  370. OPENMPT_NAMESPACE_END