1
0

modsmp_ctrl.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. /*
  2. * modsmp_ctrl.cpp
  3. * ---------------
  4. * Purpose: Basic sample editing code.
  5. * Notes : This is a legacy namespace. Some of this stuff is not required in libopenmpt (but stuff in soundlib/ still depends on it). The rest could be merged into struct ModSample.
  6. * Authors: OpenMPT Devs
  7. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  8. */
  9. #include "stdafx.h"
  10. #include "modsmp_ctrl.h"
  11. #include "AudioCriticalSection.h"
  12. #include "Sndfile.h"
  13. OPENMPT_NAMESPACE_BEGIN
  14. namespace ctrlSmp
  15. {
  16. void ReplaceSample(ModSample &smp, void *pNewSample, const SmpLength newLength, CSoundFile &sndFile)
  17. {
  18. void * const pOldSmp = smp.samplev();
  19. FlagSet<ChannelFlags> setFlags, resetFlags;
  20. setFlags.set(CHN_16BIT, smp.uFlags[CHN_16BIT]);
  21. resetFlags.set(CHN_16BIT, !smp.uFlags[CHN_16BIT]);
  22. setFlags.set(CHN_STEREO, smp.uFlags[CHN_STEREO]);
  23. resetFlags.set(CHN_STEREO, !smp.uFlags[CHN_STEREO]);
  24. CriticalSection cs;
  25. ctrlChn::ReplaceSample(sndFile, smp, pNewSample, newLength, setFlags, resetFlags);
  26. smp.pData.pSample = pNewSample;
  27. smp.nLength = newLength;
  28. ModSample::FreeSample(pOldSmp);
  29. }
  30. // Propagate loop point changes to player
  31. bool UpdateLoopPoints(const ModSample &smp, CSoundFile &sndFile)
  32. {
  33. if(!smp.HasSampleData())
  34. return false;
  35. CriticalSection cs;
  36. // Update channels with new loop values
  37. for(auto &chn : sndFile.m_PlayState.Chn) if((chn.pModSample == &smp) && chn.nLength != 0)
  38. {
  39. bool looped = false, bidi = false;
  40. if(smp.nSustainStart < smp.nSustainEnd && smp.nSustainEnd <= smp.nLength && smp.uFlags[CHN_SUSTAINLOOP] && !chn.dwFlags[CHN_KEYOFF])
  41. {
  42. // Sustain loop is active
  43. chn.nLoopStart = smp.nSustainStart;
  44. chn.nLoopEnd = smp.nSustainEnd;
  45. chn.nLength = smp.nSustainEnd;
  46. looped = true;
  47. bidi = smp.uFlags[CHN_PINGPONGSUSTAIN];
  48. } else if(smp.nLoopStart < smp.nLoopEnd && smp.nLoopEnd <= smp.nLength && smp.uFlags[CHN_LOOP])
  49. {
  50. // Normal loop is active
  51. chn.nLoopStart = smp.nLoopStart;
  52. chn.nLoopEnd = smp.nLoopEnd;
  53. chn.nLength = smp.nLoopEnd;
  54. looped = true;
  55. bidi = smp.uFlags[CHN_PINGPONGLOOP];
  56. }
  57. chn.dwFlags.set(CHN_LOOP, looped);
  58. chn.dwFlags.set(CHN_PINGPONGLOOP, looped && bidi);
  59. if(chn.position.GetUInt() > chn.nLength)
  60. {
  61. chn.position.Set(chn.nLoopStart);
  62. chn.dwFlags.reset(CHN_PINGPONGFLAG);
  63. }
  64. if(!bidi)
  65. {
  66. chn.dwFlags.reset(CHN_PINGPONGFLAG);
  67. }
  68. if(!looped)
  69. {
  70. chn.nLength = smp.nLength;
  71. }
  72. }
  73. return true;
  74. }
  75. template <class T>
  76. static void ReverseSampleImpl(T *pStart, const SmpLength length)
  77. {
  78. for(SmpLength i = 0; i < length / 2; i++)
  79. {
  80. std::swap(pStart[i], pStart[length - 1 - i]);
  81. }
  82. }
  83. // Reverse sample data
  84. bool ReverseSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile)
  85. {
  86. if(!smp.HasSampleData()) return false;
  87. if(end == 0 || start > smp.nLength || end > smp.nLength)
  88. {
  89. start = 0;
  90. end = smp.nLength;
  91. }
  92. if(end - start < 2) return false;
  93. static_assert(MaxSamplingPointSize <= 4);
  94. if(smp.GetBytesPerSample() == 4) // 16 bit stereo
  95. ReverseSampleImpl(static_cast<int32 *>(smp.samplev()) + start, end - start);
  96. else if(smp.GetBytesPerSample() == 2) // 16 bit mono / 8 bit stereo
  97. ReverseSampleImpl(static_cast<int16 *>(smp.samplev()) + start, end - start);
  98. else if(smp.GetBytesPerSample() == 1) // 8 bit mono
  99. ReverseSampleImpl(static_cast<int8 *>(smp.samplev()) + start, end - start);
  100. else
  101. return false;
  102. smp.PrecomputeLoops(sndFile, false);
  103. return true;
  104. }
  105. template <class T>
  106. static void InvertSampleImpl(T *pStart, const SmpLength length)
  107. {
  108. for(SmpLength i = 0; i < length; i++)
  109. {
  110. pStart[i] = ~pStart[i];
  111. }
  112. }
  113. // Invert sample data (flip by 180 degrees)
  114. bool InvertSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile)
  115. {
  116. if(!smp.HasSampleData()) return false;
  117. if(end == 0 || start > smp.nLength || end > smp.nLength)
  118. {
  119. start = 0;
  120. end = smp.nLength;
  121. }
  122. start *= smp.GetNumChannels();
  123. end *= smp.GetNumChannels();
  124. if(smp.GetElementarySampleSize() == 2)
  125. InvertSampleImpl(smp.sample16() + start, end - start);
  126. else if(smp.GetElementarySampleSize() == 1)
  127. InvertSampleImpl(smp.sample8() + start, end - start);
  128. else
  129. return false;
  130. smp.PrecomputeLoops(sndFile, false);
  131. return true;
  132. }
  133. template <class T>
  134. static void XFadeSampleImpl(const T *srcIn, const T *srcOut, T *output, const SmpLength fadeLength, double e)
  135. {
  136. const double length = 1.0 / static_cast<double>(fadeLength);
  137. for(SmpLength i = 0; i < fadeLength; i++, srcIn++, srcOut++, output++)
  138. {
  139. double fact1 = std::pow(i * length, e);
  140. double fact2 = std::pow((fadeLength - i) * length, e);
  141. int32 val = static_cast<int32>(
  142. static_cast<double>(*srcIn) * fact1 +
  143. static_cast<double>(*srcOut) * fact2);
  144. *output = mpt::saturate_cast<T>(val);
  145. }
  146. }
  147. // X-Fade sample data to create smooth loop transitions
  148. bool XFadeSample(ModSample &smp, SmpLength fadeLength, int fadeLaw, bool afterloopFade, bool useSustainLoop, CSoundFile &sndFile)
  149. {
  150. if(!smp.HasSampleData()) return false;
  151. const SmpLength loopStart = useSustainLoop ? smp.nSustainStart : smp.nLoopStart;
  152. const SmpLength loopEnd = useSustainLoop ? smp.nSustainEnd : smp.nLoopEnd;
  153. if(loopEnd <= loopStart || loopEnd > smp.nLength) return false;
  154. if(loopStart < fadeLength) return false;
  155. const SmpLength start = (loopStart - fadeLength) * smp.GetNumChannels();
  156. const SmpLength end = (loopEnd - fadeLength) * smp.GetNumChannels();
  157. const SmpLength afterloopStart = loopStart * smp.GetNumChannels();
  158. const SmpLength afterloopEnd = loopEnd * smp.GetNumChannels();
  159. const SmpLength afterLoopLength = std::min(smp.nLength - loopEnd, fadeLength) * smp.GetNumChannels();
  160. fadeLength *= smp.GetNumChannels();
  161. // e=0.5: constant power crossfade (for uncorrelated samples), e=1.0: constant volume crossfade (for perfectly correlated samples)
  162. const double e = 1.0 - fadeLaw / 200000.0;
  163. if(smp.GetElementarySampleSize() == 2)
  164. {
  165. XFadeSampleImpl(smp.sample16() + start, smp.sample16() + end, smp.sample16() + end, fadeLength, e);
  166. if(afterloopFade) XFadeSampleImpl(smp.sample16() + afterloopEnd, smp.sample16() + afterloopStart, smp.sample16() + afterloopEnd, afterLoopLength, e);
  167. } else if(smp.GetElementarySampleSize() == 1)
  168. {
  169. XFadeSampleImpl(smp.sample8() + start, smp.sample8() + end, smp.sample8() + end, fadeLength, e);
  170. if(afterloopFade) XFadeSampleImpl(smp.sample8() + afterloopEnd, smp.sample8() + afterloopStart, smp.sample8() + afterloopEnd, afterLoopLength, e);
  171. } else
  172. return false;
  173. smp.PrecomputeLoops(sndFile, true);
  174. return true;
  175. }
  176. template <class T>
  177. static void ConvertStereoToMonoMixImpl(T *pDest, const SmpLength length)
  178. {
  179. const T *pEnd = pDest + length;
  180. for(T *pSource = pDest; pDest != pEnd; pDest++, pSource += 2)
  181. {
  182. *pDest = static_cast<T>(mpt::rshift_signed(pSource[0] + pSource[1] + 1, 1));
  183. }
  184. }
  185. template <class T>
  186. static void ConvertStereoToMonoOneChannelImpl(T *pDest, const T *pSource, const SmpLength length)
  187. {
  188. for(const T *pEnd = pDest + length; pDest != pEnd; pDest++, pSource += 2)
  189. {
  190. *pDest = *pSource;
  191. }
  192. }
  193. // Convert a multichannel sample to mono (currently only implemented for stereo)
  194. bool ConvertToMono(ModSample &smp, CSoundFile &sndFile, StereoToMonoMode conversionMode)
  195. {
  196. if(!smp.HasSampleData() || smp.GetNumChannels() != 2) return false;
  197. // Note: Sample is overwritten in-place! Unused data is not deallocated!
  198. if(conversionMode == mixChannels)
  199. {
  200. if(smp.GetElementarySampleSize() == 2)
  201. ConvertStereoToMonoMixImpl(smp.sample16(), smp.nLength);
  202. else if(smp.GetElementarySampleSize() == 1)
  203. ConvertStereoToMonoMixImpl(smp.sample8(), smp.nLength);
  204. else
  205. return false;
  206. } else
  207. {
  208. if(conversionMode == splitSample)
  209. {
  210. conversionMode = onlyLeft;
  211. }
  212. if(smp.GetElementarySampleSize() == 2)
  213. ConvertStereoToMonoOneChannelImpl(smp.sample16(), smp.sample16() + (conversionMode == onlyLeft ? 0 : 1), smp.nLength);
  214. else if(smp.GetElementarySampleSize() == 1)
  215. ConvertStereoToMonoOneChannelImpl(smp.sample8(), smp.sample8() + (conversionMode == onlyLeft ? 0 : 1), smp.nLength);
  216. else
  217. return false;
  218. }
  219. CriticalSection cs;
  220. smp.uFlags.reset(CHN_STEREO);
  221. for(auto &chn : sndFile.m_PlayState.Chn)
  222. {
  223. if(chn.pModSample == &smp)
  224. {
  225. chn.dwFlags.reset(CHN_STEREO);
  226. }
  227. }
  228. smp.PrecomputeLoops(sndFile, false);
  229. return true;
  230. }
  231. template <class T>
  232. static void SplitStereoImpl(void *destL, void *destR, const T *source, SmpLength length)
  233. {
  234. T *l = static_cast<T *>(destL), *r = static_cast<T*>(destR);
  235. while(length--)
  236. {
  237. *(l++) = source[0];
  238. *(r++) = source[1];
  239. source += 2;
  240. }
  241. }
  242. // Converts a stereo sample into two mono samples. Source sample will not be deleted.
  243. bool SplitStereo(const ModSample &source, ModSample &left, ModSample &right, CSoundFile &sndFile)
  244. {
  245. if(!source.HasSampleData() || source.GetNumChannels() != 2 || &left == &right)
  246. return false;
  247. const bool sourceIsLeft = &left == &source, sourceIsRight = &right == &source;
  248. if(left.HasSampleData() && !sourceIsLeft)
  249. return false;
  250. if(right.HasSampleData() && !sourceIsRight)
  251. return false;
  252. void *leftData = sourceIsLeft ? left.samplev() : ModSample::AllocateSample(source.nLength, source.GetElementarySampleSize());
  253. void *rightData = sourceIsRight ? right.samplev() : ModSample::AllocateSample(source.nLength, source.GetElementarySampleSize());
  254. if(!leftData || !rightData)
  255. {
  256. if(!sourceIsLeft)
  257. ModSample::FreeSample(leftData);
  258. if(!sourceIsRight)
  259. ModSample::FreeSample(rightData);
  260. return false;
  261. }
  262. if(source.GetElementarySampleSize() == 2)
  263. SplitStereoImpl(leftData, rightData, source.sample16(), source.nLength);
  264. else if(source.GetElementarySampleSize() == 1)
  265. SplitStereoImpl(leftData, rightData, source.sample8(), source.nLength);
  266. else
  267. MPT_ASSERT_NOTREACHED();
  268. CriticalSection cs;
  269. left = source;
  270. left.uFlags.reset(CHN_STEREO);
  271. left.pData.pSample = leftData;
  272. right = source;
  273. right.uFlags.reset(CHN_STEREO);
  274. right.pData.pSample = rightData;
  275. for(auto &chn : sndFile.m_PlayState.Chn)
  276. {
  277. if(chn.pModSample == &left || chn.pModSample == &right)
  278. chn.dwFlags.reset(CHN_STEREO);
  279. }
  280. left.PrecomputeLoops(sndFile, false);
  281. right.PrecomputeLoops(sndFile, false);
  282. return true;
  283. }
  284. template <class T>
  285. static void ConvertMonoToStereoImpl(const T *MPT_RESTRICT src, T *MPT_RESTRICT dst, SmpLength length)
  286. {
  287. while(length--)
  288. {
  289. dst[0] = *src;
  290. dst[1] = *src;
  291. dst += 2;
  292. src++;
  293. }
  294. }
  295. // Convert a multichannel sample to mono (currently only implemented for stereo)
  296. bool ConvertToStereo(ModSample &smp, CSoundFile &sndFile)
  297. {
  298. if(!smp.HasSampleData() || smp.GetNumChannels() != 1) return false;
  299. void *newSample = ModSample::AllocateSample(smp.nLength, smp.GetBytesPerSample() * 2);
  300. if(newSample == nullptr)
  301. {
  302. return 0;
  303. }
  304. if(smp.GetElementarySampleSize() == 2)
  305. ConvertMonoToStereoImpl(smp.sample16(), (int16 *)newSample, smp.nLength);
  306. else if(smp.GetElementarySampleSize() == 1)
  307. ConvertMonoToStereoImpl(smp.sample8(), (int8 *)newSample, smp.nLength);
  308. else
  309. return false;
  310. CriticalSection cs;
  311. smp.uFlags.set(CHN_STEREO);
  312. ReplaceSample(smp, newSample, smp.nLength, sndFile);
  313. smp.PrecomputeLoops(sndFile, false);
  314. return true;
  315. }
  316. } // namespace ctrlSmp
  317. namespace ctrlChn
  318. {
  319. void ReplaceSample( CSoundFile &sndFile,
  320. const ModSample &sample,
  321. const void * const pNewSample,
  322. const SmpLength newLength,
  323. FlagSet<ChannelFlags> setFlags,
  324. FlagSet<ChannelFlags> resetFlags)
  325. {
  326. const bool periodIsFreq = sndFile.PeriodsAreFrequencies();
  327. for(auto &chn : sndFile.m_PlayState.Chn)
  328. {
  329. if(chn.pModSample == &sample)
  330. {
  331. if(chn.pCurrentSample != nullptr)
  332. chn.pCurrentSample = pNewSample;
  333. if(chn.position.GetUInt() > newLength)
  334. chn.position.Set(0);
  335. if(chn.nLength > 0)
  336. LimitMax(chn.nLength, newLength);
  337. if(chn.InSustainLoop())
  338. {
  339. chn.nLoopStart = sample.nSustainStart;
  340. chn.nLoopEnd = sample.nSustainEnd;
  341. } else
  342. {
  343. chn.nLoopStart = sample.nLoopStart;
  344. chn.nLoopEnd = sample.nLoopEnd;
  345. }
  346. chn.dwFlags.set(setFlags);
  347. chn.dwFlags.reset(resetFlags);
  348. if(chn.nC5Speed && sample.nC5Speed && !sndFile.UseFinetuneAndTranspose())
  349. {
  350. if(periodIsFreq)
  351. chn.nPeriod = Util::muldivr_unsigned(chn.nPeriod, sample.nC5Speed, chn.nC5Speed);
  352. else
  353. chn.nPeriod = Util::muldivr_unsigned(chn.nPeriod, chn.nC5Speed, sample.nC5Speed);
  354. }
  355. chn.nC5Speed = sample.nC5Speed;
  356. }
  357. }
  358. }
  359. } // namespace ctrlChn
  360. OPENMPT_NAMESPACE_END