#include "stdafx.h"
#include "Sndfile.h"
#include "ModSample.h"
#include "modsmp_ctrl.h"
#include <cmath>
OPENMPT_NAMESPACE_BEGIN
void ModSample::Convert(MODTYPE fromType, MODTYPE toType)
{
if((!(toType & (MOD_TYPE_MOD | MOD_TYPE_XM))) && (fromType & (MOD_TYPE_MOD | MOD_TYPE_XM)))
{
TransposeToFrequency();
RelativeTone = 0;
nFineTune = 0;
if(fromType == MOD_TYPE_MOD)
nC5Speed = Util::muldivr_unsigned(nC5Speed, 8272, 8363);
} else if((toType & (MOD_TYPE_MOD | MOD_TYPE_XM)) && (!(fromType & (MOD_TYPE_MOD | MOD_TYPE_XM))))
{
if(toType == MOD_TYPE_MOD)
nC5Speed = Util::muldivr_unsigned(nC5Speed, 8363, 8272);
FrequencyToTranspose();
}
if(toType & (MOD_TYPE_MOD | MOD_TYPE_S3M))
{
uFlags.reset(CHN_PINGPONGLOOP | CHN_PANNING);
nVibDepth = 0;
nVibRate = 0;
nVibSweep = 0;
nVibType = VIB_SINE;
RelativeTone = 0;
}
if(toType & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_S3M))
{
nGlobalVol = 64;
if(uFlags[CHN_SUSTAINLOOP])
{
nLoopStart = nSustainStart;
nLoopEnd = nSustainEnd;
uFlags.set(CHN_LOOP);
uFlags.set(CHN_PINGPONGLOOP, uFlags[CHN_PINGPONGSUSTAIN]);
}
nSustainStart = nSustainEnd = 0;
uFlags.reset(CHN_SUSTAINLOOP|CHN_PINGPONGSUSTAIN);
}
if(toType & MOD_TYPE_XM)
{
if(!uFlags[CHN_PANNING])
{
uFlags.set(CHN_PANNING);
nPan = 128;
}
LimitMax(nVibDepth, uint8(15));
LimitMax(nVibRate, uint8(63));
}
if(((fromType & MOD_TYPE_XM) && (toType & (MOD_TYPE_IT | MOD_TYPE_MPT))) || ((toType & MOD_TYPE_XM) && (fromType & (MOD_TYPE_IT | MOD_TYPE_MPT))))
{
if(nVibRate != 0 && nVibDepth != 0)
{
if(nVibSweep != 0)
nVibSweep = mpt::saturate_cast<decltype(nVibSweep)>(Util::muldivr_unsigned(nVibDepth, 256, nVibSweep));
else
nVibSweep = 255;
}
}
if(toType == MOD_TYPE_IT && nVibType == VIB_RAMP_UP)
{
nVibType = VIB_RAMP_DOWN;
} else if(toType == MOD_TYPE_XM && nVibType == VIB_RANDOM)
{
nVibType = VIB_SINE;
}
if(toType != MOD_TYPE_MPT)
{
uFlags.reset(SMP_KEEPONDISK);
}
if(!CSoundFile::SupportsOPL(toType) && uFlags[CHN_ADLIB])
{
SetAdlib(false);
}
}
void ModSample::Initialize(MODTYPE type)
{
nLength = 0;
nLoopStart = nLoopEnd = 0;
nSustainStart = nSustainEnd = 0;
nC5Speed = 8363;
nPan = 128;
nVolume = 256;
nGlobalVol = 64;
uFlags.reset(CHN_PANNING | CHN_SUSTAINLOOP | CHN_LOOP | CHN_PINGPONGLOOP | CHN_PINGPONGSUSTAIN | CHN_ADLIB | SMP_MODIFIED | SMP_KEEPONDISK);
if(type == MOD_TYPE_XM)
{
uFlags.set(CHN_PANNING);
}
RelativeTone = 0;
nFineTune = 0;
nVibType = VIB_SINE;
nVibSweep = 0;
nVibDepth = 0;
nVibRate = 0;
rootNote = 0;
filename[0] = '\0';
SetDefaultCuePoints();
}
uint32 ModSample::GetSampleRate(const MODTYPE type) const
{
uint32 rate;
if(CSoundFile::UseFinetuneAndTranspose(type))
rate = TransposeToFrequency(RelativeTone, nFineTune);
else
rate = nC5Speed;
if(type == MOD_TYPE_MOD)
rate = Util::muldivr_unsigned(rate, 8272, 8363);
return (rate > 0) ? rate : 8363;
}
size_t ModSample::AllocateSample()
{
FreeSample();
if((pData.pSample = AllocateSample(nLength, GetBytesPerSample())) == nullptr)
{
return 0;
} else
{
return GetSampleSizeInBytes();
}
}
void *ModSample::AllocateSample(SmpLength numFrames, size_t bytesPerSample)
{
const size_t allocSize = GetRealSampleBufferSize(numFrames, bytesPerSample);
if(allocSize != 0)
{
char *p = new (std::nothrow) char[allocSize];
if(p != nullptr)
{
memset(p, 0, allocSize);
return p + (InterpolationMaxLookahead * MaxSamplingPointSize);
}
}
return nullptr;
}
size_t ModSample::GetRealSampleBufferSize(SmpLength numSamples, size_t bytesPerSample)
{
const SmpLength maxSize = Util::MaxValueOfType(numSamples);
const SmpLength lookaheadBufferSize = (MaxSamplingPointSize + 1 + 4 + 4) * InterpolationMaxLookahead;
if(numSamples == 0 || numSamples > MAX_SAMPLE_LENGTH || lookaheadBufferSize > maxSize - numSamples)
{
return 0;
}
numSamples += lookaheadBufferSize;
if(maxSize / bytesPerSample < numSamples)
{
return 0;
}
return numSamples * bytesPerSample;
}
void ModSample::FreeSample()
{
FreeSample(pData.pSample);
pData.pSample = nullptr;
}
void ModSample::FreeSample(void *samplePtr)
{
if(samplePtr)
{
delete[] (((char *)samplePtr) - (InterpolationMaxLookahead * MaxSamplingPointSize));
}
}
void ModSample::SetLoop(SmpLength start, SmpLength end, bool enable, bool pingpong, CSoundFile &sndFile)
{
nLoopStart = start;
nLoopEnd = end;
LimitMax(nLoopEnd, nLength);
if(nLoopStart < nLoopEnd)
{
uFlags.set(CHN_LOOP, enable);
uFlags.set(CHN_PINGPONGLOOP, pingpong && enable);
} else
{
nLoopStart = nLoopEnd = 0;
uFlags.reset(CHN_LOOP | CHN_PINGPONGLOOP);
}
PrecomputeLoops(sndFile, true);
}
void ModSample::SetSustainLoop(SmpLength start, SmpLength end, bool enable, bool pingpong, CSoundFile &sndFile)
{
nSustainStart = start;
nSustainEnd = end;
LimitMax(nLoopEnd, nLength);
if(nSustainStart < nSustainEnd)
{
uFlags.set(CHN_SUSTAINLOOP, enable);
uFlags.set(CHN_PINGPONGSUSTAIN, pingpong && enable);
} else
{
nSustainStart = nSustainEnd = 0;
uFlags.reset(CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN);
}
PrecomputeLoops(sndFile, true);
}
void ModSample::PrecomputeLoops(CSoundFile &sndFile, bool updateChannels)
{
ctrlSmp::PrecomputeLoops(*this, sndFile, updateChannels);
}
void ModSample::SanitizeLoops()
{
LimitMax(nSustainEnd, nLength);
LimitMax(nLoopEnd, nLength);
if(nSustainStart >= nSustainEnd)
{
nSustainStart = nSustainEnd = 0;
uFlags.reset(CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN);
}
if(nLoopStart >= nLoopEnd)
{
nLoopStart = nLoopEnd = 0;
uFlags.reset(CHN_LOOP | CHN_PINGPONGLOOP);
}
}
uint32 ModSample::TransposeToFrequency(int transpose, int finetune)
{
return mpt::saturate_round<uint32>(std::pow(2.0, (transpose * 128.0 + finetune) * (1.0 / (12.0 * 128.0))) * 8363.0);
}
void ModSample::TransposeToFrequency()
{
nC5Speed = TransposeToFrequency(RelativeTone, nFineTune);
}
int ModSample::FrequencyToTranspose(uint32 freq)
{
return mpt::saturate_round<int>(std::log(freq * (1.0 / 8363.0)) * (12.0 * 128.0 * (1.0 / M_LN2)));
}
void ModSample::FrequencyToTranspose()
{
int f2t = 0;
if(nC5Speed)
f2t = FrequencyToTranspose(nC5Speed);
RelativeTone = static_cast<int8>(f2t >> 7);
nFineTune = static_cast<int8>(f2t & 0x7F);
}
void ModSample::Transpose(double amount)
{
nC5Speed = mpt::saturate_round<uint32>(nC5Speed * std::pow(2.0, amount));
}
bool ModSample::HasCustomCuePoints() const
{
if(!uFlags[CHN_ADLIB])
{
for(SmpLength i = 0; i < CountOf(cues); i++)
{
if(cues[i] != (i + 1) << 11) return true;
}
}
return false;
}
void ModSample::SetDefaultCuePoints()
{
for(int i = 0; i < 9; i++)
{
cues[i] = (i + 1) << 11;
}
}
void ModSample::SetAdlib(bool enable, OPLPatch patch)
{
if(!enable && uFlags[CHN_ADLIB])
{
SetDefaultCuePoints();
}
uFlags.set(CHN_ADLIB, enable);
if(enable)
{
uFlags.reset(CHN_16BIT | CHN_STEREO);
nLength = 4;
AllocateSample();
adlib = patch;
}
}
OPENMPT_NAMESPACE_END