Esempio n. 1
0
// Translate instrument properties between two given formats.
void ModInstrument::Convert(MODTYPE fromType, MODTYPE toType)
//-----------------------------------------------------------
{
	MPT_UNREFERENCED_PARAMETER(fromType);

	if(toType & MOD_TYPE_XM)
	{
		ResetNoteMap();

		PitchEnv.dwFlags.reset(ENV_ENABLED | ENV_FILTER);

		dwFlags.reset(INS_SETPANNING);
		SetCutoff(GetCutoff(), false);
		SetResonance(GetResonance(), false);
		nFilterMode = FLTMODE_UNCHANGED;

		nCutSwing = nPanSwing = nResSwing = nVolSwing = 0;

		nPPC = NOTE_MIDDLEC - 1;
		nPPS = 0;

		nNNA = NNA_NOTECUT;
		nDCT = DCT_NONE;
		nDNA = DNA_NOTECUT;

		if(nMidiChannel == MidiMappedChannel)
		{
			nMidiChannel = 1;
		}

		// FT2 only has signed Pitch Wheel Depth, and it's limited to 0...36 (in the GUI, at least. As you would expect it from FT2, this value is actually not sanitized on load).
		midiPWD = static_cast<int8>(abs(midiPWD));
		Limit(midiPWD, int8(0), int8(36));

		nGlobalVol = 64;
		nPan = 128;

		LimitMax(nFadeOut, 32767u);
	}

	VolEnv.Convert(fromType, toType);
	PanEnv.Convert(fromType, toType);
	PitchEnv.Convert(fromType, toType);

	// Limit fadeout length for IT / MPT
	if(toType & (MOD_TYPE_IT | MOD_TYPE_MPT))
	{
		LimitMax(nFadeOut, 8192u);
	}

	// MPT-specific features - remove instrument tunings, Pitch/Tempo Lock, cutoff / resonance swing and filter mode for other formats
	if(!(toType & MOD_TYPE_MPT))
	{
		SetTuning(nullptr);
		wPitchToTempoLock = 0;
		nCutSwing = nResSwing = 0;
		nFilterMode = FLTMODE_UNCHANGED;
	}
}
Esempio n. 2
0
// Remove loop points if they're invalid.
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);
	}
}
Esempio n. 3
0
//Format: First bit tells whether the size indicator is 1 or 2 bytes.
static void WriteAdaptive12String(std::ostream& oStrm, const std::string& str)
//----------------------------------------------------------------------------
{
	uint16 s = static_cast<uint16>(str.size());
	LimitMax(s, uint16(uint16_max / 2));
	WriteAdaptive12(oStrm, s);
	oStrm.write(str.c_str(), s);
}
Esempio n. 4
0
void Gargle::RecalculateGargleParams()
//------------------------------------
{
	m_period = m_SndFile.GetSampleRate() / RateInHertz();
	if(m_period < 2) m_period = 2;
	m_periodHalf = m_period / 2;
	LimitMax(m_counter, m_period);
}
Esempio n. 5
0
void ModInstrument::Sanitize(MODTYPE modType)
{
	LimitMax(nFadeOut, 65536u);
	LimitMax(nGlobalVol, 64u);
	LimitMax(nPan, 256u);

	LimitMax(wMidiBank, uint16(16384));
	LimitMax(nMidiProgram, uint8(128));
	LimitMax(nMidiChannel, uint8(17));

	if(nNNA > NNA_NOTEFADE) nNNA = NNA_NOTECUT;
	if(nDCT > DCT_PLUGIN) nDCT = DCT_NONE;
	if(nDNA > DNA_NOTEFADE) nDNA = DNA_NOTECUT;

	LimitMax(nPanSwing, uint8(64));
	LimitMax(nVolSwing, uint8(100));

	Limit(nPPS, int8(-32), int8(32));

	LimitMax(nCutSwing, uint8(64));
	LimitMax(nResSwing, uint8(64));
	
#ifdef MODPLUG_TRACKER
	MPT_UNREFERENCED_PARAMETER(modType);
	const uint8 range = ENVELOPE_MAX;
#else
	const uint8 range = modType == MOD_TYPE_AMS2 ? uint8_max : ENVELOPE_MAX;
#endif
	VolEnv.Sanitize();
	PanEnv.Sanitize();
	PitchEnv.Sanitize(range);

	for(size_t i = 0; i < CountOf(NoteMap); i++)
	{
		if(NoteMap[i] < NOTE_MIN || NoteMap[i] > NOTE_MAX)
			NoteMap[i] = static_cast<uint8>(i + NOTE_MIN);
	}
}
Esempio n. 6
0
// Convert an XMSample to OpenMPT's internal sample representation.
void XMSample::ConvertToMPT(ModSample &mptSmp) const
//--------------------------------------------------
{
	mptSmp.Initialize(MOD_TYPE_XM);

	// Volume
	mptSmp.nVolume = vol * 4;
	LimitMax(mptSmp.nVolume, uint16(256));

	// Panning
	mptSmp.nPan = pan;
	mptSmp.uFlags = CHN_PANNING;

	// Sample Frequency
	mptSmp.nFineTune = finetune;
	mptSmp.RelativeTone = relnote;

	// Sample Length and Loops
	mptSmp.nLength = length;
	mptSmp.nLoopStart = loopStart;
	mptSmp.nLoopEnd = mptSmp.nLoopStart + loopLength;

	if((flags & XMSample::sample16Bit))
	{
		mptSmp.nLength /= 2;
		mptSmp.nLoopStart /= 2;
		mptSmp.nLoopEnd /= 2;
	}

	if((flags & XMSample::sampleStereo))
	{
		mptSmp.nLength /= 2;
		mptSmp.nLoopStart /= 2;
		mptSmp.nLoopEnd /= 2;
	}

	if((flags & (XMSample::sampleLoop | XMSample::sampleBidiLoop)) && mptSmp.nLoopStart < mptSmp.nLength && mptSmp.nLoopEnd > mptSmp.nLoopStart)
	{
		mptSmp.uFlags.set(CHN_LOOP);
		if((flags & XMSample::sampleBidiLoop))
		{
			mptSmp.uFlags.set(CHN_PINGPONGLOOP);
		}
	}

	mptSmp.SanitizeLoops();

	strcpy(mptSmp.filename, "");
}
Esempio n. 7
0
void ReadModPatterns(std::istream& iStrm, CPatternContainer& patc, const size_t)
//--------------------------------------------------------------------------------
{
    srlztn::Ssb ssb(iStrm);
    ssb.BeginRead(FileIdPatterns, MptVersion::num);
    if ((ssb.m_Status & srlztn::SNT_FAILURE) != 0)
            return;
    modplug::tracker::patternindex_t nPatterns = patc.Size();
    uint16_t nCount = UINT16_MAX;
    if (ssb.ReadItem(nCount, "num") != srlztn::Ssb::EntryNotFound)
            nPatterns = nCount;
    LimitMax(nPatterns, ModSpecs::mptm.patternsMax);
    if (nPatterns > patc.Size())
            patc.ResizeArray(nPatterns);
    for(uint16_t i = 0; i < nPatterns; i++)
    {
            ssb.ReadItem(patc[i], &i, sizeof(i), &ReadModPattern);
    }
}
Esempio n. 8
0
void ReadModPatterns(std::istream& iStrm, CPatternContainer& patc, const size_t)
//--------------------------------------------------------------------------------
{
	srlztn::SsbRead ssb(iStrm);
	ssb.BeginRead(FileIdPatterns, MptVersion::num);
	if ((ssb.m_Status & srlztn::SNT_FAILURE) != 0)
		return;
	PATTERNINDEX nPatterns = patc.Size();
	uint16 nCount = uint16_max;
	if (ssb.ReadItem(nCount, "num") != srlztn::SsbRead::EntryNotFound)
		nPatterns = nCount;
	LimitMax(nPatterns, ModSpecs::mptm.patternsMax);
	if (nPatterns > patc.Size())
		patc.ResizeArray(nPatterns);	
	for(uint16 i = 0; i < nPatterns; i++)
	{
		ssb.ReadItem(patc[i], srlztn::IdLE<uint16>(i).GetChars(), sizeof(i), &ReadModPattern);
	}
}
Esempio n. 9
0
void InstrumentEnvelope::Sanitize(uint8 maxValue)
{
	if(!empty())
	{
		front().tick = 0;
		LimitMax(front().value, maxValue);
		for(iterator it = begin() + 1; it != end(); it++)
		{
			it->tick = std::max(it->tick, (it - 1)->tick);
			LimitMax(it->value, maxValue);
		}
	}
	LimitMax(nLoopEnd, static_cast<decltype(nLoopEnd)>(size() - 1));
	LimitMax(nLoopStart, nLoopEnd);
	LimitMax(nSustainEnd, static_cast<decltype(nSustainEnd)>(size() - 1));
	LimitMax(nSustainStart, nSustainEnd);
	if(nReleaseNode != ENV_RELEASE_NODE_UNSET)
		LimitMax(nReleaseNode, static_cast<decltype(nReleaseNode)>(size() - 1));
}
Esempio n. 10
0
// Simple 2-poles resonant filter
void CSoundFile::SetupChannelFilter(ModChannel *pChn, bool bReset, int flt_modifier) const
{
	int cutoff = (int)pChn->nCutOff + (int)pChn->nCutSwing;
	int resonance = (int)(pChn->nResonance & 0x7F) + (int)pChn->nResSwing;

	Limit(cutoff, 0, 127);
	Limit(resonance, 0, 127);

	if(!m_playBehaviour[kMPTOldSwingBehaviour])
	{
		pChn->nCutOff = (uint8)cutoff;
		pChn->nCutSwing = 0;
		pChn->nResonance = (uint8)resonance;
		pChn->nResSwing = 0;
	}

	// flt_modifier is in [-256, 256], so cutoff is in [0, 127 * 2] after this calculation.
	const int computedCutoff = cutoff * (flt_modifier + 256) / 256;

	// Filtering is only ever done in IT if either cutoff is not full or if resonance is set.
	if(m_playBehaviour[kITFilterBehaviour] && resonance == 0 && computedCutoff >= 254)
	{
		if(pChn->rowCommand.IsNote() && !pChn->rowCommand.IsPortamento() && !pChn->nMasterChn && m_SongFlags[SONG_FIRSTTICK])
		{
			// Z7F next to a note disables the filter, however in other cases this should not happen.
			// Test cases: filter-reset.it, filter-reset-carry.it, filter-nna.it
			pChn->dwFlags.reset(CHN_FILTER);
		}
		return;
	}

	pChn->dwFlags.set(CHN_FILTER);

	// 2 * damping factor
	const float dmpfac = std::pow(10.0f, -resonance * ((24.0f / 128.0f) / 20.0f));
	const float fc = CutOffToFrequency(cutoff, flt_modifier) * (2.0f * (float)M_PI);
	float d, e;
	if(m_playBehaviour[kITFilterBehaviour] && !m_SongFlags[SONG_EXFILTERRANGE])
	{
		const float r = m_MixerSettings.gdwMixingFreq / fc;

		d = dmpfac * r + dmpfac - 1.0f;
		e = r * r;
	} else
	{
		const float r = fc / m_MixerSettings.gdwMixingFreq;

		d = (1.0f - 2.0f * dmpfac) * r;
		LimitMax(d, 2.0f);
		d = (2.0f * dmpfac - d) / r;
		e = 1.0f / (r * r);
	}

	float fg = 1.0f / (1.0f + d + e);
	float fb0 = (d + e + e) / (1 + d + e);
	float fb1 = -e / (1.0f + d + e);

#if defined(MPT_INTMIXER)
#define FILTER_CONVERT(x) Util::Round<mixsample_t>((x) * (1 << MIXING_FILTER_PRECISION))
#else
#define FILTER_CONVERT(x) (x)
#endif

	switch(pChn->nFilterMode)
	{
	case FLTMODE_HIGHPASS:
		pChn->nFilter_A0 = FILTER_CONVERT(1.0f - fg);
		pChn->nFilter_B0 = FILTER_CONVERT(fb0);
		pChn->nFilter_B1 = FILTER_CONVERT(fb1);
#ifdef MPT_INTMIXER
		pChn->nFilter_HP = -1;
#else
		pChn->nFilter_HP = 1.0f;
#endif // MPT_INTMIXER
		break;

	default:
		pChn->nFilter_A0 = FILTER_CONVERT(fg);
		pChn->nFilter_B0 = FILTER_CONVERT(fb0);
		pChn->nFilter_B1 = FILTER_CONVERT(fb1);
#ifdef MPT_INTMIXER
		if(pChn->nFilter_A0 == 0)
			pChn->nFilter_A0 = 1;	// Prevent silence at low filter cutoff and very high sampling rate
		pChn->nFilter_HP = 0;
#else
		pChn->nFilter_HP = 0;
#endif // MPT_INTMIXER
		break;
	}
#undef FILTER_CONVERT

	if (bReset)
	{
		pChn->nFilter_Y[0][0] = pChn->nFilter_Y[0][1] = 0;
		pChn->nFilter_Y[1][0] = pChn->nFilter_Y[1][1] = 0;
	}

}
Esempio n. 11
0
// Translate sample properties between two given formats.
void ModSample::Convert(MODTYPE fromType, MODTYPE toType)
//-------------------------------------------------------
{
	// Convert between frequency and transpose values if necessary.
	if((!(toType & (MOD_TYPE_MOD | MOD_TYPE_XM))) && (fromType & (MOD_TYPE_MOD | MOD_TYPE_XM)))
	{
		TransposeToFrequency();
		RelativeTone = 0;
		nFineTune = 0;
	} else if((toType & (MOD_TYPE_MOD | MOD_TYPE_XM)) && (!(fromType & (MOD_TYPE_MOD | MOD_TYPE_XM))))
	{
		FrequencyToTranspose();
		if(toType & MOD_TYPE_MOD)
		{
			RelativeTone = 0;
		}
	}

	// No ping-pong loop, panning and auto-vibrato for MOD / S3M samples
	if(toType & (MOD_TYPE_MOD | MOD_TYPE_S3M))
	{
		uFlags &= ~(CHN_PINGPONGLOOP | CHN_PANNING);

		nVibDepth = 0;
		nVibRate = 0;
		nVibSweep = 0;
		nVibType = VIB_SINE;
	}

	// No global volume sustain loops for MOD/S3M/XM
	if(toType & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_S3M))
	{
		nGlobalVol = 64;
		// Sustain loops - convert to normal loops
		if((uFlags & CHN_SUSTAINLOOP) != 0)
		{
			// We probably overwrite a normal loop here, but since sustain loops are evaluated before normal loops, this is just correct.
			nLoopStart = nSustainStart;
			nLoopEnd = nSustainEnd;
			uFlags |= CHN_LOOP;
			if(uFlags & CHN_PINGPONGSUSTAIN)
			{
				uFlags |= CHN_PINGPONGLOOP;
			} else
			{
				uFlags &= ~CHN_PINGPONGLOOP;
			}
		}
		nSustainStart = nSustainEnd = 0;
		uFlags &= ~(CHN_SUSTAINLOOP|CHN_PINGPONGSUSTAIN);
	}

	// All XM samples have default panning, and XM's autovibrato settings are rather limited.
	if(toType & MOD_TYPE_XM)
	{
		if(!(uFlags & CHN_PANNING))
		{
			uFlags |= CHN_PANNING;
			nPan = 128;
		}

		LimitMax(nVibDepth, BYTE(15));
		LimitMax(nVibRate, BYTE(63));
	}


	// Autovibrato sweep setting is inverse in XM (0 = "no sweep") and IT (0 = "no vibrato")
	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)
		{
			nVibSweep = 255 - nVibSweep;
		}
	}
}
Esempio n. 12
0
// Translate instrument properties between two given formats.
void ModInstrument::Convert(MODTYPE fromType, MODTYPE toType)
{
	MPT_UNREFERENCED_PARAMETER(fromType);

	if(toType & MOD_TYPE_XM)
	{
		ResetNoteMap();

		PitchEnv.dwFlags.reset(ENV_ENABLED | ENV_FILTER);

		dwFlags.reset(INS_SETPANNING);
		SetCutoff(GetCutoff(), false);
		SetResonance(GetResonance(), false);
		nFilterMode = FLTMODE_UNCHANGED;

		nCutSwing = nPanSwing = nResSwing = nVolSwing = 0;

		nPPC = NOTE_MIDDLEC - 1;
		nPPS = 0;

		nNNA = NNA_NOTECUT;
		nDCT = DCT_NONE;
		nDNA = DNA_NOTECUT;

		if(nMidiChannel == MidiMappedChannel)
		{
			nMidiChannel = 1;
		}

		// FT2 only has unsigned Pitch Wheel Depth, and it's limited to 0...36 (in the GUI, at least. As you would expect it from FT2, this value is actually not sanitized on load).
		midiPWD = static_cast<int8>(mpt::abs(midiPWD));
		Limit(midiPWD, int8(0), int8(36));

		nGlobalVol = 64;
		nPan = 128;

		LimitMax(nFadeOut, 32767u);
	}

	VolEnv.Convert(fromType, toType);
	PanEnv.Convert(fromType, toType);
	PitchEnv.Convert(fromType, toType);

	if(fromType == MOD_TYPE_XM && (toType & (MOD_TYPE_IT | MOD_TYPE_MPT)))
	{
		if(!VolEnv.dwFlags[ENV_ENABLED])
		{
			// Note-Off with no envelope cuts the note immediately in XM
			VolEnv.resize(2);
			VolEnv[0].tick = 0;
			VolEnv[0].value =  ENVELOPE_MAX;
			VolEnv[1].tick = 1;
			VolEnv[1].value =  ENVELOPE_MIN;
			VolEnv.dwFlags.set(ENV_ENABLED | ENV_SUSTAIN);
			VolEnv.dwFlags.reset(ENV_LOOP);
			VolEnv.nSustainStart = VolEnv.nSustainEnd = 0;
		}
	}

	// Limit fadeout length for IT
	if(toType & MOD_TYPE_IT)
	{
		LimitMax(nFadeOut, 8192u);
	}

	// MPT-specific features - remove instrument tunings, Pitch/Tempo Lock, cutoff / resonance swing and filter mode for other formats
	if(!(toType & MOD_TYPE_MPT))
	{
		SetTuning(nullptr);
		pitchToTempoLock.Set(0);
		nCutSwing = nResSwing = 0;
		nFilterMode = FLTMODE_UNCHANGED;
		nVolRampUp = 0;
	}
}
Esempio n. 13
0
	// Check how many samples can be rendered without encountering loop or sample end, and also update loop position / direction
	MPT_FORCEINLINE uint32 GetSampleCount(ModChannel &chn, uint32 nSamples, bool ITPingPongMode) const
	{
		int32 nLoopStart = chn.dwFlags[CHN_LOOP] ? chn.nLoopStart : 0;
		SamplePosition nInc = chn.increment;

		if ((nSamples <= 0) || nInc.IsZero() || (!chn.nLength)) return 0;

		// Part 1: Making sure the play position is valid, and if necessary, invert the play direction in case we reached a loop boundary of a ping-pong loop.
		chn.pCurrentSample = samplePointer;

		// Under zero ?
		if (chn.position.GetInt() < nLoopStart)
		{
			if (nInc.IsNegative())
			{
				// Invert loop for bidi loops
				chn.position = SamplePosition(nLoopStart + nLoopStart, 0) - chn.position;
				if ((chn.position.GetInt() < nLoopStart) || (chn.position.GetUInt() >= (nLoopStart + chn.nLength) / 2))
				{
					chn.position.Set(nLoopStart, 0);
				}
				nInc.Negate();
				chn.increment = nInc;
				if(chn.dwFlags[CHN_PINGPONGLOOP])
				{
					chn.dwFlags.reset(CHN_PINGPONGFLAG); // go forward
				} else
				{
					chn.dwFlags.set(CHN_PINGPONGFLAG);
					chn.position.SetInt(chn.nLength - 1);
					chn.increment.Negate();
				}
				if(!chn.dwFlags[CHN_LOOP] || chn.position.GetUInt() >= chn.nLength)
				{
					chn.position.Set(chn.nLength);
					return 0;
				}
			} else
			{
				// We probably didn't hit the loop end yet (first loop), so we do nothing
				if (chn.position.GetInt() < 0) chn.position.SetInt(0);
			}
		} else if (chn.position.GetUInt() >= chn.nLength)
		{
			// Past the end
			if(!chn.dwFlags[CHN_LOOP]) return 0; // not looping -> stop this channel
			if(chn.dwFlags[CHN_PINGPONGLOOP])
			{
				// Invert loop
				if (nInc.IsPositive())
				{
					nInc.Negate();
					chn.increment = nInc;
				}
				chn.dwFlags.set(CHN_PINGPONGFLAG);
				// adjust loop position

				SamplePosition invFract = chn.position.GetInvertedFract();
				chn.position = SamplePosition(chn.nLength - (chn.position.GetInt() - chn.nLength) - invFract.GetInt(), invFract.GetFract());
				if ((chn.position.GetUInt() <= chn.nLoopStart) || (chn.position.GetUInt() >= chn.nLength))
				{
					// Impulse Tracker's software mixer would put a -2 (instead of -1) in the following line (doesn't happen on a GUS)
					chn.position.SetInt(chn.nLength - std::min<SmpLength>(chn.nLength, ITPingPongMode ? 2 : 1));
				}
			} else
			{
				if (nInc.IsNegative()) // This is a bug
				{
					nInc.Negate();
					chn.increment = nInc;
				}
				// Restart at loop start
				chn.position += SamplePosition(nLoopStart - chn.nLength, 0);
				MPT_ASSERT(chn.position.GetInt() >= nLoopStart);
				// Interpolate correctly after wrapping around
				chn.dwFlags.set(CHN_WRAPPED_LOOP);
			}
		}

		// Part 2: Compute how many samples we can render until we reach the end of sample / loop boundary / etc.

		SamplePosition nPos = chn.position;
		// too big increment, and/or too small loop length
		if (nPos.GetInt() < nLoopStart)
		{
			if (nPos.IsNegative() || nInc.IsNegative()) return 0;
		}
		if (nPos.IsNegative() || nPos.GetUInt() >= chn.nLength) return 0;
		uint32 nSmpCount = nSamples;
		SamplePosition nInv = nInc;
		if (nInc.IsNegative())
		{
			nInv.Negate();
		}
		LimitMax(nSamples, maxSamples);
		SamplePosition incSamples = nInc * (nSamples - 1);
		int32 nPosDest = (nPos + incSamples).GetInt();

		const SmpLength nPosInt = nPos.GetUInt();
		const bool isAtLoopStart = (nPosInt >= chn.nLoopStart && nPosInt < chn.nLoopStart + InterpolationMaxLookahead);
		if(!isAtLoopStart)
		{
			chn.dwFlags.reset(CHN_WRAPPED_LOOP);
		}

		// Loop wrap-around magic.
		bool checkDest = true;
		if(lookaheadPointer != nullptr)
		{
			if(nPos.GetUInt() >= lookaheadStart)
			{
#if 0
				const uint32 oldCount = nSmpCount;

				// When going backwards - we can only go back up to lookaheadStart.
				// When going forwards - read through the whole pre-computed wrap-around buffer if possible.
				// TODO: ProTracker sample swapping needs hard cut at sample end.
				int32 samplesToRead = nInc.IsNegative()
					? (nPosInt - lookaheadStart)
					//: 2 * InterpolationMaxLookahead - (nPosInt - mixLoopState.lookaheadStart);
					: (chn.nLoopEnd - nPosInt);
				//LimitMax(samplesToRead, chn.nLoopEnd - chn.nLoopStart);
				nSmpCount = SamplesToBufferLength(samplesToRead, chn);
				Limit(nSmpCount, 1u, oldCount);
#else
				if (nInc.IsNegative())
				{
					nSmpCount = DistanceToBufferLength(SamplePosition(lookaheadStart, 0), nPos, nInv);
				} else
				{
					nSmpCount = DistanceToBufferLength(nPos, SamplePosition(chn.nLoopEnd, 0), nInv);
				}
#endif
				chn.pCurrentSample = lookaheadPointer;
				checkDest = false;
			} else if(chn.dwFlags[CHN_WRAPPED_LOOP] && isAtLoopStart)
			{
				// We just restarted the loop, so interpolate correctly after wrapping around
				nSmpCount = DistanceToBufferLength(nPos, SamplePosition(nLoopStart + InterpolationMaxLookahead, 0), nInv);
				chn.pCurrentSample = lookaheadPointer + (chn.nLoopEnd - nLoopStart) * chn.pModSample->GetBytesPerSample();
				checkDest = false;
			} else if(nInc.IsPositive() && static_cast<SmpLength>(nPosDest) >= lookaheadStart && nSmpCount > 1)
			{
				// We shouldn't read that far if we're not using the pre-computed wrap-around buffer.
				nSmpCount = DistanceToBufferLength(nPos, SamplePosition(lookaheadStart, 0), nInv);
				checkDest = false;
			}
		}

		if(checkDest)
		{
			// Fix up sample count if target position is invalid
			if (nInc.IsNegative())
			{
				if (nPosDest < nLoopStart)
				{
					nSmpCount = DistanceToBufferLength(SamplePosition(chn.nLoopStart, 0), nPos, nInv);
				}
			} else
			{
				if (nPosDest >= (int32)chn.nLength)
				{
					nSmpCount = DistanceToBufferLength(nPos, SamplePosition(chn.nLength, 0), nInv);
				}
			}
		}

		Limit(nSmpCount, 1u, nSamples);

#ifdef _DEBUG
		{
			SmpLength posDest = (nPos + nInc * (nSmpCount - 1)).GetUInt();
			if (posDest < 0 || posDest > chn.nLength)
			{
				// We computed an invalid delta!
				MPT_ASSERT_NOTREACHED();
				return 0;
			}
		}
#endif

		return nSmpCount;
	}
Esempio n. 14
0
OPENMPT_NAMESPACE_BEGIN


#if MPT_COMPILER_GCC
#if MPT_GCC_AT_LEAST(4,6,0)
#pragma GCC diagnostic push
#endif
#pragma GCC diagnostic ignored "-Wswitch"
#elif MPT_COMPILER_CLANG
#pragma clang diagnostic push
#if MPT_CLANG_AT_LEAST(3,3,0)
#pragma clang diagnostic ignored "-Wswitch"
#else
#pragma clang diagnostic ignored "-Wswitch-enum"
#endif
#endif

// Read a sample from memory
size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const
//--------------------------------------------------------------------
{
	if(sample.nLength < 1 || !file.IsValid())
	{
		return 0;
	}

	LimitMax(sample.nLength, MAX_SAMPLE_LENGTH);

	const char * const sourceBuf = file.GetRawData();
	const FileReader::off_t fileSize = file.BytesLeft(), filePosition = file.GetPosition();
	FileReader::off_t bytesRead = 0;	// Amount of memory that has been read from file

	sample.uFlags.set(CHN_16BIT, GetBitDepth() >= 16);
	sample.uFlags.set(CHN_STEREO, GetChannelFormat() != mono);
	size_t sampleSize = sample.AllocateSample();	// Target sample size in bytes

	if(sampleSize == 0)
	{
		sample.nLength = 0;
		return 0;
	}

	ASSERT(sampleSize >= sample.GetSampleSizeInBytes());

	//////////////////////////////////////////////////////
	// 8-Bit / Mono / PCM
	if(GetBitDepth() == 8 && GetChannelFormat() == mono)
	{
		switch(GetEncoding())
		{
		case signedPCM:		// 8-Bit / Mono / Signed / PCM
			bytesRead = CopyMonoSample<SC::DecodeInt8>(sample, sourceBuf, fileSize);
			break;
		case unsignedPCM:	// 8-Bit / Mono / Unsigned / PCM
			bytesRead = CopyMonoSample<SC::DecodeUint8>(sample, sourceBuf, fileSize);
			break;
		case deltaPCM:		// 8-Bit / Mono / Delta / PCM
		case MT2:
			bytesRead = CopyMonoSample<SC::DecodeInt8Delta>(sample, sourceBuf, fileSize);
			break;
		case PCM7to8:		// 7 Bit stored as 8-Bit with highest bit unused / Mono / Signed / PCM
			bytesRead = CopyMonoSample<SC::DecodeInt7>(sample, sourceBuf, fileSize);
			break;
		}
	}

	//////////////////////////////////////////////////////
	// 8-Bit / Stereo Split / PCM
	else if(GetBitDepth() == 8 && GetChannelFormat() == stereoSplit)
	{
		switch(GetEncoding())
		{
		case signedPCM:		// 8-Bit / Stereo Split / Signed / PCM
			bytesRead = CopyStereoSplitSample<SC::DecodeInt8>(sample, sourceBuf, fileSize);
			break;
		case unsignedPCM:	// 8-Bit / Stereo Split / Unsigned / PCM
			bytesRead = CopyStereoSplitSample<SC::DecodeUint8>(sample, sourceBuf, fileSize);
			break;
		case deltaPCM:		// 8-Bit / Stereo Split / Delta / PCM
		case MT2:
			bytesRead = CopyStereoSplitSample<SC::DecodeInt8Delta>(sample, sourceBuf, fileSize);
			break;
		}
	}

	//////////////////////////////////////////////////////
	// 8-Bit / Stereo Interleaved / PCM
	else if(GetBitDepth() == 8 && GetChannelFormat() == stereoInterleaved)
	{
		switch(GetEncoding())
		{
		case signedPCM:		// 8-Bit / Stereo Interleaved / Signed / PCM
			bytesRead = CopyStereoInterleavedSample<SC::DecodeInt8>(sample, sourceBuf, fileSize);
			break;
		case unsignedPCM:	// 8-Bit / Stereo Interleaved / Unsigned / PCM
			bytesRead = CopyStereoInterleavedSample<SC::DecodeUint8>(sample, sourceBuf, fileSize);
			break;
		case deltaPCM:		// 8-Bit / Stereo Interleaved / Delta / PCM
			bytesRead = CopyStereoInterleavedSample<SC::DecodeInt8Delta>(sample, sourceBuf, fileSize);
			break;
		}
	}

	//////////////////////////////////////////////////////
	// 16-Bit / Mono / Little Endian / PCM
	else if(GetBitDepth() == 16 && GetChannelFormat() == mono && GetEndianness() == littleEndian)
	{
		switch(GetEncoding())
		{
		case signedPCM:		// 16-Bit / Stereo Interleaved / Signed / PCM
			bytesRead = CopyMonoSample<SC::DecodeInt16<0, littleEndian16> >(sample, sourceBuf, fileSize);
			break;
		case unsignedPCM:	// 16-Bit / Stereo Interleaved / Unsigned / PCM
			bytesRead = CopyMonoSample<SC::DecodeInt16<0x8000u, littleEndian16> >(sample, sourceBuf, fileSize);
			break;
		case deltaPCM:		// 16-Bit / Stereo Interleaved / Delta / PCM
		case MT2:
			bytesRead = CopyMonoSample<SC::DecodeInt16Delta<littleEndian16> >(sample, sourceBuf, fileSize);
			break;
		}
	}

	//////////////////////////////////////////////////////
	// 16-Bit / Mono / Big Endian / PCM
	else if(GetBitDepth() == 16 && GetChannelFormat() == mono && GetEndianness() == bigEndian)
	{
		switch(GetEncoding())
		{
		case signedPCM:		// 16-Bit / Mono / Signed / PCM
			bytesRead = CopyMonoSample<SC::DecodeInt16<0, bigEndian16> >(sample, sourceBuf, fileSize);
			break;
		case unsignedPCM:	// 16-Bit / Mono / Unsigned / PCM
			bytesRead = CopyMonoSample<SC::DecodeInt16<0x8000u, bigEndian16> >(sample, sourceBuf, fileSize);
			break;
		case deltaPCM:		// 16-Bit / Mono / Delta / PCM
			bytesRead = CopyMonoSample<SC::DecodeInt16Delta<bigEndian16> >(sample, sourceBuf, fileSize);
			break;
		}
	}

	//////////////////////////////////////////////////////
	// 16-Bit / Stereo Split / Little Endian / PCM
	else if(GetBitDepth() == 16 && GetChannelFormat() == stereoSplit && GetEndianness() == littleEndian)
	{
		switch(GetEncoding())
		{
		case signedPCM:		// 16-Bit / Stereo Split / Signed / PCM
			bytesRead = CopyStereoSplitSample<SC::DecodeInt16<0, littleEndian16> >(sample, sourceBuf, fileSize);
			break;
		case unsignedPCM:	// 16-Bit / Stereo Split / Unsigned / PCM
			bytesRead = CopyStereoSplitSample<SC::DecodeInt16<0x8000u, littleEndian16> >(sample, sourceBuf, fileSize);
			break;
		case deltaPCM:		// 16-Bit / Stereo Split / Delta / PCM
		case MT2:
			bytesRead = CopyStereoSplitSample<SC::DecodeInt16Delta<littleEndian16> >(sample, sourceBuf, fileSize);
			break;
		}
	}

	//////////////////////////////////////////////////////
	// 16-Bit / Stereo Split / Big Endian / PCM
	else if(GetBitDepth() == 16 && GetChannelFormat() == stereoSplit && GetEndianness() == bigEndian)
	{
		switch(GetEncoding())
		{
		case signedPCM:		// 16-Bit / Stereo Split / Signed / PCM
			bytesRead = CopyStereoSplitSample<SC::DecodeInt16<0, bigEndian16> >(sample, sourceBuf, fileSize);
			break;
		case unsignedPCM:	// 16-Bit / Stereo Split / Unsigned / PCM
			bytesRead = CopyStereoSplitSample<SC::DecodeInt16<0x8000u, bigEndian16> >(sample, sourceBuf, fileSize);
			break;
		case deltaPCM:		// 16-Bit / Stereo Split / Delta / PCM
			bytesRead = CopyStereoSplitSample<SC::DecodeInt16Delta<bigEndian16> >(sample, sourceBuf, fileSize);
			break;
		}
	}

	//////////////////////////////////////////////////////
	// 16-Bit / Stereo Interleaved / Little Endian / PCM
	else if(GetBitDepth() == 16 && GetChannelFormat() == stereoInterleaved && GetEndianness() == littleEndian)
	{
		switch(GetEncoding())
		{
		case signedPCM:		// 16-Bit / Stereo Interleaved / Signed / PCM
			bytesRead = CopyStereoInterleavedSample<SC::DecodeInt16<0, littleEndian16> >(sample, sourceBuf, fileSize);
			break;
		case unsignedPCM:	// 16-Bit / Stereo Interleaved / Unsigned / PCM
			bytesRead = CopyStereoInterleavedSample<SC::DecodeInt16<0x8000u, littleEndian16> >(sample, sourceBuf, fileSize);
			break;
		case deltaPCM:		// 16-Bit / Stereo Interleaved / Delta / PCM
			bytesRead = CopyStereoInterleavedSample<SC::DecodeInt16Delta<littleEndian16> >(sample, sourceBuf, fileSize);
			break;
		}
	}

	//////////////////////////////////////////////////////
	// 16-Bit / Stereo Interleaved / Big Endian / PCM
	else if(GetBitDepth() == 16 && GetChannelFormat() == stereoInterleaved && GetEndianness() == bigEndian)
	{
		switch(GetEncoding())
		{
		case signedPCM:		// 16-Bit / Stereo Interleaved / Signed / PCM
			bytesRead = CopyStereoInterleavedSample<SC::DecodeInt16<0, bigEndian16> >(sample, sourceBuf, fileSize);
			break;
		case unsignedPCM:	// 16-Bit / Stereo Interleaved / Unsigned / PCM
			bytesRead = CopyStereoInterleavedSample<SC::DecodeInt16<0x8000u, bigEndian16> >(sample, sourceBuf, fileSize);
			break;
		case deltaPCM:		// 16-Bit / Stereo Interleaved / Delta / PCM
			bytesRead = CopyStereoInterleavedSample<SC::DecodeInt16Delta<bigEndian16> >(sample, sourceBuf, fileSize);
			break;
		}
	}

	//////////////////////////////////////////////////////
	// 24-Bit / Signed / Mono / PCM
	else if(GetBitDepth() == 24 && GetChannelFormat() == mono && GetEncoding() == signedPCM)
	{
		if(GetEndianness() == littleEndian)
		{
			bytesRead = CopyMonoSample<SC::ConversionChain<SC::Convert<int16, int32>, SC::DecodeInt24<0, littleEndian24> > >(sample, sourceBuf, fileSize);
		} else
		{
			bytesRead = CopyMonoSample<SC::ConversionChain<SC::Convert<int16, int32>, SC::DecodeInt24<0, bigEndian24> > >(sample, sourceBuf, fileSize);
		}
	}

	//////////////////////////////////////////////////////
	// 24-Bit / Signed / Stereo Interleaved / PCM
	else if(GetBitDepth() == 24 && GetChannelFormat() == stereoInterleaved && GetEncoding() == signedPCM)
	{
		if(GetEndianness() == littleEndian)
		{
			bytesRead = CopyStereoInterleavedSample<SC::ConversionChain<SC::Convert<int16, int32>, SC::DecodeInt24<0, littleEndian24> > >(sample, sourceBuf, fileSize);
		} else
		{
			bytesRead = CopyStereoInterleavedSample<SC::ConversionChain<SC::Convert<int16, int32>, SC::DecodeInt24<0, bigEndian24> > >(sample, sourceBuf, fileSize);
		}
	}

	//////////////////////////////////////////////////////
	// 32-Bit / Signed / Mono / PCM
	else if(GetBitDepth() == 32 && GetChannelFormat() == mono && GetEncoding() == signedPCM)
	{
		if(GetEndianness() == littleEndian)
		{
			bytesRead = CopyMonoSample<SC::ConversionChain<SC::Convert<int16, int32>, SC::DecodeInt32<0, littleEndian32> > >(sample, sourceBuf, fileSize);
		} else
		{
			bytesRead = CopyMonoSample<SC::ConversionChain<SC::Convert<int16, int32>, SC::DecodeInt32<0, bigEndian32> > >(sample, sourceBuf, fileSize);
		}
	}

	//////////////////////////////////////////////////////
	// 32-Bit / Signed / Stereo Interleaved / PCM
	else if(GetBitDepth() == 32 && GetChannelFormat() == stereoInterleaved && GetEncoding() == signedPCM)
	{
		if(GetEndianness() == littleEndian)
		{
			bytesRead = CopyStereoInterleavedSample<SC::ConversionChain<SC::Convert<int16, int32>, SC::DecodeInt32<0, littleEndian32> > >(sample, sourceBuf, fileSize);
		} else
		{
			bytesRead = CopyStereoInterleavedSample<SC::ConversionChain<SC::Convert<int16, int32>, SC::DecodeInt32<0, bigEndian32> > >(sample, sourceBuf, fileSize);
		}
	}

	//////////////////////////////////////////////////////
	// 32-Bit / Float / Mono / PCM
	else if(GetBitDepth() == 32 && GetChannelFormat() == mono && GetEncoding() == floatPCM)
	{
		if(GetEndianness() == littleEndian)
		{
			bytesRead = CopyMonoSample<SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeFloat32<littleEndian32> > >(sample, sourceBuf, fileSize);
		} else
		{
			bytesRead = CopyMonoSample<SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeFloat32<bigEndian32> > >(sample, sourceBuf, fileSize);
		}
	}

	//////////////////////////////////////////////////////
	// 32-Bit / Float / Stereo Interleaved / PCM
	else if(GetBitDepth() == 32 && GetChannelFormat() == stereoInterleaved && GetEncoding() == floatPCM)
	{
		if(GetEndianness() == littleEndian)
		{
			bytesRead = CopyStereoInterleavedSample<SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeFloat32<littleEndian32> > >(sample, sourceBuf, fileSize);
		} else
		{
			bytesRead = CopyStereoInterleavedSample<SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeFloat32<bigEndian32> > >(sample, sourceBuf, fileSize);
		}
	}

	//////////////////////////////////////////////////////
	// 24-Bit / Signed / Mono, Stereo Interleaved / PCM
	else if(GetBitDepth() == 24 && (GetChannelFormat() == mono || GetChannelFormat() == stereoInterleaved) && GetEncoding() == signedPCMnormalize)
	{
		// Normalize to 16-Bit
		uint32 srcPeak = uint32(1)<<31;
		if(GetEndianness() == littleEndian)
		{
			bytesRead = CopyAndNormalizeSample<SC::NormalizationChain<SC::Convert<int16, int32>, SC::DecodeInt24<0, littleEndian24> > >(sample, sourceBuf, fileSize, &srcPeak);
		} else
		{
			bytesRead = CopyAndNormalizeSample<SC::NormalizationChain<SC::Convert<int16, int32>, SC::DecodeInt24<0, bigEndian24> > >(sample, sourceBuf, fileSize, &srcPeak);
		}
		if(bytesRead)
		{
			// Adjust sample volume so we do not affect relative volume of the sample. Normalizing is only done to increase precision.
			sample.nGlobalVol = static_cast<uint16>(Clamp(Util::muldivr_unsigned(sample.nGlobalVol, srcPeak, uint32(1)<<31), uint32(1), uint32(64)));
		}
	}

	//////////////////////////////////////////////////////
	// 32-Bit / Signed / Mono, Stereo Interleaved / PCM
	else if(GetBitDepth() == 32 && (GetChannelFormat() == mono || GetChannelFormat() == stereoInterleaved) && GetEncoding() == signedPCMnormalize)
	{
		// Normalize to 16-Bit
		uint32 srcPeak = uint32(1)<<31;
		if(GetEndianness() == littleEndian)
		{
			bytesRead = CopyAndNormalizeSample<SC::NormalizationChain<SC::Convert<int16, int32>, SC::DecodeInt32<0, littleEndian32> > >(sample, sourceBuf, fileSize, &srcPeak);
		} else
		{
			bytesRead = CopyAndNormalizeSample<SC::NormalizationChain<SC::Convert<int16, int32>, SC::DecodeInt32<0, bigEndian32> > >(sample, sourceBuf, fileSize, &srcPeak);
		}
		if(bytesRead)
		{
			// Adjust sample volume so we do not affect relative volume of the sample. Normalizing is only done to increase precision.
			sample.nGlobalVol = static_cast<uint16>(Clamp(Util::muldivr_unsigned(sample.nGlobalVol, srcPeak, uint32(1)<<31), uint32(1), uint32(64)));
		}
	}

	//////////////////////////////////////////////////////
	// 32-Bit / Float / Mono, Stereo Interleaved / PCM
	else if(GetBitDepth() == 32 && (GetChannelFormat() == mono || GetChannelFormat() == stereoInterleaved) && GetEncoding() == floatPCMnormalize)
	{
		// Normalize to 16-Bit
		float32 srcPeak = 1.0f;
		if(GetEndianness() == littleEndian)
		{
			bytesRead = CopyAndNormalizeSample<SC::NormalizationChain<SC::Convert<int16, float32>, SC::DecodeFloat32<littleEndian32> > >(sample, sourceBuf, fileSize, &srcPeak);
		} else
		{
			bytesRead = CopyAndNormalizeSample<SC::NormalizationChain<SC::Convert<int16, float32>, SC::DecodeFloat32<bigEndian32> > >(sample, sourceBuf, fileSize, &srcPeak);
		}
		if(bytesRead)
		{
			// Adjust sample volume so we do not affect relative volume of the sample. Normalizing is only done to increase precision.
			sample.nGlobalVol = Util::Round<uint16>(Clamp(sample.nGlobalVol * srcPeak, 1.0f, 64.0f));
		}
	}

	//////////////////////////////////////////////////////
	// 32-Bit / Float / Mono / PCM / full scale 2^15
	else if(GetBitDepth() == 32 && GetChannelFormat() == mono && GetEncoding() == floatPCM15)
	{
		if(GetEndianness() == littleEndian)
		{
			bytesRead = CopyMonoSample
				(sample, sourceBuf, fileSize,
				SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeScaledFloat32<littleEndian32> >
				(SC::Convert<int16, float32>(), SC::DecodeScaledFloat32<littleEndian32>(1.0f / static_cast<float>(1<<15)))
				);
		} else
		{
			bytesRead = CopyMonoSample
				(sample, sourceBuf, fileSize,
				SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeScaledFloat32<bigEndian32> >
				(SC::Convert<int16, float32>(), SC::DecodeScaledFloat32<bigEndian32>(1.0f / static_cast<float>(1<<15)))
				);
		}
	}

	//////////////////////////////////////////////////////
	// 32-Bit / Float / Stereo Interleaved / PCM / full scale 2^15
	else if(GetBitDepth() == 32 && GetChannelFormat() == stereoInterleaved && GetEncoding() == floatPCM15)
	{
		if(GetEndianness() == littleEndian)
		{
			bytesRead = CopyStereoInterleavedSample
				(sample, sourceBuf, fileSize,
				SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeScaledFloat32<littleEndian32> >
				(SC::Convert<int16, float32>(), SC::DecodeScaledFloat32<littleEndian32>(1.0f / static_cast<float>(1<<15)))
				);
		} else
		{
			bytesRead = CopyStereoInterleavedSample
				(sample, sourceBuf, fileSize,
				SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeScaledFloat32<bigEndian32> >
				(SC::Convert<int16, float32>(), SC::DecodeScaledFloat32<bigEndian32>(1.0f / static_cast<float>(1<<15)))
				);
		}
	}

	//////////////////////////////////////////////////////
	// 32-Bit / Float / Stereo Interleaved / PCM / full scale 2^23
	else if(GetBitDepth() == 32 && GetChannelFormat() == mono && GetEncoding() == floatPCM23)
	{
		if(GetEndianness() == littleEndian)
		{
			bytesRead = CopyMonoSample
				(sample, sourceBuf, fileSize,
				SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeScaledFloat32<littleEndian32> >
				(SC::Convert<int16, float32>(), SC::DecodeScaledFloat32<littleEndian32>(1.0f / static_cast<float>(1<<23)))
				);
		} else
		{
			bytesRead = CopyMonoSample
				(sample, sourceBuf, fileSize,
				SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeScaledFloat32<bigEndian32> >
				(SC::Convert<int16, float32>(), SC::DecodeScaledFloat32<bigEndian32>(1.0f / static_cast<float>(1<<23)))
				);
		}
	}

	//////////////////////////////////////////////////////
	// 32-Bit / Float / Stereo Interleaved / PCM / full scale 2^23
	else if(GetBitDepth() == 32 && GetChannelFormat() == stereoInterleaved && GetEncoding() == floatPCM23)
	{
		if(GetEndianness() == littleEndian)
		{
			bytesRead = CopyStereoInterleavedSample
				(sample, sourceBuf, fileSize,
				SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeScaledFloat32<littleEndian32> >
				(SC::Convert<int16, float32>(), SC::DecodeScaledFloat32<littleEndian32>(1.0f / static_cast<float>(1<<23)))
				);
		} else
		{
			bytesRead = CopyStereoInterleavedSample
				(sample, sourceBuf, fileSize,
				SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeScaledFloat32<bigEndian32> >
				(SC::Convert<int16, float32>(), SC::DecodeScaledFloat32<bigEndian32>(1.0f / static_cast<float>(1<<23)))
				);
		}
	}

	//////////////////////////////////////////////////////
	// Compressed samples
	if(*this == SampleIO(_8bit, mono, littleEndian, ADPCM))
	{
		// 4-Bit ADPCM data
		int8 compressionTable[16];	// ADPCM Compression LUT
		if(file.ReadArray(compressionTable))
		{
			size_t readLength = (sample.nLength + 1) / 2;
			LimitMax(readLength, file.BytesLeft());

			const uint8 *inBuf = reinterpret_cast<const uint8*>(sourceBuf) + sizeof(compressionTable);
			int8 *outBuf = static_cast<int8 *>(sample.pSample);
			int8 delta = 0;

			for(size_t i = readLength; i != 0; i--)
			{
				delta += compressionTable[*inBuf & 0x0F];
				*(outBuf++) = delta;
				delta += compressionTable[(*inBuf >> 4) & 0x0F];
				*(outBuf++) = delta;
				inBuf++;
			}
			bytesRead = sizeof(compressionTable) + readLength;
		}
Esempio n. 15
0
// Simple 2-poles resonant filter
void CSoundFile::SetupChannelFilter(ModChannel *pChn, bool bReset, int flt_modifier) const
//----------------------------------------------------------------------------------------
{
	int cutoff = (int)pChn->nCutOff + (int)pChn->nCutSwing;
	int resonance = (int)(pChn->nResonance & 0x7F) + (int)pChn->nResSwing;

	Limit(cutoff, 0, 127);
	Limit(resonance, 0, 127);

	if(!GetModFlag(MSF_OLDVOLSWING))
	{
		pChn->nCutOff = (uint8)cutoff;
		pChn->nCutSwing = 0;
		pChn->nResonance = (uint8)resonance;
		pChn->nResSwing = 0;
	}

	float d, e;

	// flt_modifier is in [-256, 256], so cutoff is in [0, 127 * 2] after this calculation.
	const int computedCutoff = cutoff * (flt_modifier + 256) / 256;

	// Filtering is only ever done in IT if either cutoff is not full or if resonance is set.
	if(IsCompatibleMode(TRK_IMPULSETRACKER) && resonance == 0 && computedCutoff >= 254)
	{
		if(pChn->rowCommand.IsNote() && !pChn->dwFlags[CHN_PORTAMENTO] && !pChn->nMasterChn && m_SongFlags[SONG_FIRSTTICK])
		{
			// Z7F next to a note disables the filter, however in other cases this should not happen.
			// Test cases: filter-reset.it, filter-reset-carry.it, filter-nna.it
			pChn->dwFlags.reset(CHN_FILTER);
		}
		return;
	}

	pChn->dwFlags.set(CHN_FILTER);

	if(UseITFilterMode())
	{
		const float freqParameterMultiplier = 128.0f / (24.0f * 256.0f);

		// 2 ^ (i / 24 * 256)
		float frequency = 110.0f * pow(2.0f, 0.25f + (float)computedCutoff * freqParameterMultiplier);
		LimitMax(frequency, (float)(m_MixerSettings.gdwMixingFreq / 2));
		const float r = (float)m_MixerSettings.gdwMixingFreq / (2.0f * (float)M_PI * frequency);

		d = ITResonanceTable[resonance] * r + ITResonanceTable[resonance] - 1.0f;
		e = r * r;
	} else
	{
		float fc = (float)CutOffToFrequency(cutoff, flt_modifier);
		const float dmpfac = pow(10.0f, -((24.0f / 128.0f) * (float)resonance) / 20.0f);

		fc *= (float)(2.0f * (float)M_PI / (float)m_MixerSettings.gdwMixingFreq);

		d = (1.0f - 2.0f * dmpfac) * fc;
		LimitMax(d, 2.0f);
		d = (2.0f * dmpfac - d) / fc;
		e = pow(1.0f / fc, 2.0f);
	}

	float fg = 1.0f / (1.0f + d + e);
	float fb0 = (d + e + e) / (1 + d + e);
	float fb1 = -e / (1.0f + d + e);

	switch(pChn->nFilterMode)
	{
	case FLTMODE_HIGHPASS:
		pChn->nFilter_A0 = 1.0f - fg;
		pChn->nFilter_B0 = fb0;
		pChn->nFilter_B1 = fb1;
		pChn->nFilter_HP = -1;
		break;

	default:
		pChn->nFilter_A0 = fg;
		pChn->nFilter_B0 = fb0;
		pChn->nFilter_B1 = fb1;
		pChn->nFilter_HP = 0;
		break;
	}
	
	if (bReset)
	{
		pChn->nFilter_Y1 = pChn->nFilter_Y2 = 0;
		pChn->nFilter_Y3 = pChn->nFilter_Y4 = 0;
	}

}
Esempio n. 16
0
OPENMPT_NAMESPACE_BEGIN

// Version changelog:
// v1.03: - Relative unicode instrument paths instead of absolute ANSI paths
//        - Per-path variable string length
//        - Embedded samples are IT-compressed
//        (rev. 3249)
// v1.02: Explicitely updated format to use new instrument flags representation (rev. 483)
// v1.01: Added option to embed instrument headers


bool CSoundFile::ReadITProject(FileReader &file, ModLoadingFlags loadFlags)
//-------------------------------------------------------------------------
{
#ifndef MPT_EXTERNAL_SAMPLES
	// Doesn't really make sense to support this format when there's no support for external files...
	MPT_UNREFERENCED_PARAMETER(file);
	MPT_UNREFERENCED_PARAMETER(loadFlags);
	return false;
#else // MPT_EXTERNAL_SAMPLES
	
	enum ITPSongFlags
	{
		ITP_EMBEDMIDICFG	= 0x00001,	// Embed macros in file
		ITP_ITOLDEFFECTS	= 0x00004,	// Old Impulse Tracker effect implementations
		ITP_ITCOMPATGXX		= 0x00008,	// IT "Compatible Gxx" (IT's flag to behave more like other trackers w/r/t portamento effects)
		ITP_LINEARSLIDES	= 0x00010,	// Linear slides vs. Amiga slides
		ITP_EXFILTERRANGE	= 0x08000,	// Cutoff Filter has double frequency range (up to ~10Khz)
		ITP_ITPROJECT		= 0x20000,	// Is a project file
		ITP_ITPEMBEDIH		= 0x40000,	// Embed instrument headers in project file
	};

	uint32 version;
	FileReader::off_t size;

	file.Rewind();

	// Check file ID
	if(!file.CanRead(12 + 4 + 24 + 4)
		|| file.ReadUint32LE() != MAGIC4BE('.','i','t','p')	// Magic bytes
		|| (version = file.ReadUint32LE()) > 0x00000103		// Format version
		|| version < 0x00000100)
	{
		return false;
	} else if(loadFlags == onlyVerifyHeader)
	{
		return true;
	}

	InitializeGlobals(MOD_TYPE_IT);
	m_playBehaviour.reset();
	file.ReadString<mpt::String::maybeNullTerminated>(m_songName, file.ReadUint32LE());

	// Song comments
	m_songMessage.Read(file, file.ReadUint32LE(), SongMessage::leCR);

	// Song global config
	const uint32 songFlags = file.ReadUint32LE();
	if(!(songFlags & ITP_ITPROJECT))
	{
		return false;
	}
	if(songFlags & ITP_EMBEDMIDICFG)	m_SongFlags.set(SONG_EMBEDMIDICFG);
	if(songFlags & ITP_ITOLDEFFECTS)	m_SongFlags.set(SONG_ITOLDEFFECTS);
	if(songFlags & ITP_ITCOMPATGXX)		m_SongFlags.set(SONG_ITCOMPATGXX);
	if(songFlags & ITP_LINEARSLIDES)	m_SongFlags.set(SONG_LINEARSLIDES);
	if(songFlags & ITP_EXFILTERRANGE)	m_SongFlags.set(SONG_EXFILTERRANGE);

	m_nDefaultGlobalVolume = file.ReadUint32LE();
	m_nSamplePreAmp = file.ReadUint32LE();
	m_nDefaultSpeed = std::max(uint32(1), file.ReadUint32LE());
	m_nDefaultTempo.Set(std::max(uint32(32), file.ReadUint32LE()));
	m_nChannels = static_cast<CHANNELINDEX>(file.ReadUint32LE());
	if(m_nChannels == 0 || m_nChannels > MAX_BASECHANNELS)
	{
		return false;
	}

	// channel name string length (=MAX_CHANNELNAME)
	size = file.ReadUint32LE();

	// Channels' data
	for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++)
	{
		ChnSettings[chn].nPan = std::min(static_cast<uint16>(file.ReadUint32LE()), uint16(256));
		ChnSettings[chn].dwFlags.reset();
		uint32 flags = file.ReadUint32LE();
		if(flags & 0x100) ChnSettings[chn].dwFlags.set(CHN_MUTE);
		if(flags & 0x800) ChnSettings[chn].dwFlags.set(CHN_SURROUND);
		ChnSettings[chn].nVolume = std::min(static_cast<uint16>(file.ReadUint32LE()), uint16(64));
		file.ReadString<mpt::String::maybeNullTerminated>(ChnSettings[chn].szName, size);
	}

	// Song mix plugins
	{
		FileReader plugChunk = file.ReadChunk(file.ReadUint32LE());
		LoadMixPlugins(plugChunk);
	}

	// MIDI Macro config
	file.ReadStructPartial(m_MidiCfg, file.ReadUint32LE());
	m_MidiCfg.Sanitize();

	// Song Instruments
	m_nInstruments = static_cast<INSTRUMENTINDEX>(file.ReadUint32LE());
	if(m_nInstruments >= MAX_INSTRUMENTS)
	{
		return false;
	}

	// Instruments' paths
	if(version <= 0x00000102)
	{
		size = file.ReadUint32LE();	// path string length
	}

	std::vector<mpt::PathString> instrPaths(GetNumInstruments());
	for(INSTRUMENTINDEX ins = 0; ins < GetNumInstruments(); ins++)
	{
		if(version > 0x00000102)
		{
			size = file.ReadUint32LE();	// path string length
		}
		std::string path;
		file.ReadString<mpt::String::maybeNullTerminated>(path, size);
		if(version <= 0x00000102)
		{
			instrPaths[ins] = mpt::PathString::FromLocaleSilent(path);
		} else
		{
			instrPaths[ins] = mpt::PathString::FromUTF8(path);
		}
	}

	// Song Orders
	size = file.ReadUint32LE();
	Order.ReadAsByte(file, size, size, 0xFF, 0xFE);

	// Song Patterns
	const PATTERNINDEX numPats = static_cast<PATTERNINDEX>(file.ReadUint32LE());
	const PATTERNINDEX numNamedPats = static_cast<PATTERNINDEX>(file.ReadUint32LE());
	size_t patNameLen = file.ReadUint32LE();	// Size of each pattern name
	FileReader pattNames = file.ReadChunk(numNamedPats * patNameLen);

	// modcommand data length
	size = file.ReadUint32LE();
	if(size != 6)
	{
		return false;
	}

	for(PATTERNINDEX pat = 0; pat < numPats; pat++)
	{
		const ROWINDEX numRows = file.ReadUint32LE();
		FileReader patternChunk = file.ReadChunk(numRows * size * GetNumChannels());

		// Allocate pattern
		if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, numRows))
		{
			pattNames.Skip(patNameLen);
			continue;
		}

		if(pat < numNamedPats)
		{
			char patName[32];
			pattNames.ReadString<mpt::String::maybeNullTerminated>(patName, patNameLen);
			Patterns[pat].SetName(patName);
		}

		// Pattern data
		size_t numCommands = GetNumChannels() * numRows;

		if(patternChunk.CanRead(sizeof(MODCOMMAND_ORIGINAL) * numCommands))
		{
			ModCommand *target = Patterns[pat].GetpModCommand(0, 0);
			while(numCommands-- != 0)
			{
				STATIC_ASSERT(sizeof(MODCOMMAND_ORIGINAL) == 6);
				MODCOMMAND_ORIGINAL data;
				patternChunk.ReadStruct(data);
				if(data.command >= MAX_EFFECTS) data.command = CMD_NONE;
				if(data.volcmd >= MAX_VOLCMDS) data.volcmd = VOLCMD_NONE;
				if(data.note > NOTE_MAX && data.note < NOTE_MIN_SPECIAL) data.note = NOTE_NONE;
				*(target++) = data;
			}
		}
	}

	// Load embedded samples

	// Read original number of samples
	m_nSamples = static_cast<SAMPLEINDEX>(file.ReadUint32LE());
	LimitMax(m_nSamples, SAMPLEINDEX(MAX_SAMPLES - 1));

	// Read number of embedded samples
	uint32 embeddedSamples = file.ReadUint32LE();

	// Read samples
	for(uint32 smp = 0; smp < embeddedSamples; smp++)
	{
		SAMPLEINDEX realSample = static_cast<SAMPLEINDEX>(file.ReadUint32LE());
		ITSample sampleHeader;
		file.ReadConvertEndianness(sampleHeader);
		FileReader sampleData = file.ReadChunk(file.ReadUint32LE());

		if(realSample >= 1 && realSample <= GetNumSamples() && !memcmp(sampleHeader.id, "IMPS", 4) && (loadFlags & loadSampleData))
		{
			sampleHeader.ConvertToMPT(Samples[realSample]);
			mpt::String::Read<mpt::String::nullTerminated>(m_szNames[realSample], sampleHeader.name);

			// Read sample data
			sampleHeader.GetSampleFormat().ReadSample(Samples[realSample], sampleData);
		}
	}

	// Load instruments
	for(INSTRUMENTINDEX ins = 0; ins < GetNumInstruments(); ins++)
	{
		if(instrPaths[ins].empty())
			continue;

		if(!file.GetFileName().empty())
		{
			instrPaths[ins] = instrPaths[ins].RelativePathToAbsolute(file.GetFileName().GetPath());
		}
#ifdef MODPLUG_TRACKER
		else if(GetpModDoc() != nullptr)
		{
			instrPaths[ins] = instrPaths[ins].RelativePathToAbsolute(GetpModDoc()->GetPathNameMpt().GetPath());
		}
#endif // MODPLUG_TRACKER

		InputFile f(instrPaths[ins]);
		FileReader file = GetFileReader(f);
		if(!ReadInstrumentFromFile(ins + 1, file, true))
		{
			AddToLog(LogWarning, MPT_USTRING("Unable to open instrument: ") + instrPaths[ins].ToUnicode());
		}
	}

	// Extra info data
	uint32 code = file.ReadUint32LE();

	// Embed instruments' header [v1.01]
	if(version >= 0x00000101 && (songFlags & ITP_ITPEMBEDIH) && code == MAGIC4BE('E', 'B', 'I', 'H'))
	{
		code = file.ReadUint32LE();

		INSTRUMENTINDEX ins = 1;
		while(ins <= GetNumInstruments() && file.CanRead(4))
		{
			if(code == MAGIC4BE('M', 'P', 'T', 'S'))
			{
				break;
			} else if(code == MAGIC4BE('S', 'E', 'P', '@') || code == MAGIC4BE('M', 'P', 'T', 'X'))
			{
				// jump code - switch to next instrument
				ins++;
			} else
			{
				ReadExtendedInstrumentProperty(Instruments[ins], code, file);
			}

			code = file.ReadUint32LE();
		}
	}

	// Song extensions
	if(code == MAGIC4BE('M', 'P', 'T', 'S'))
	{
		file.SkipBack(4);
		LoadExtendedSongProperties(file);
	}

	m_nMaxPeriod = 0xF000;
	m_nMinPeriod = 8;

	// Before OpenMPT 1.20.01.09, the MIDI macros were always read from the file, even if the "embed" flag was not set.
	if(m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1,20,01,09) && !m_SongFlags[SONG_EMBEDMIDICFG])
	{
		m_MidiCfg.Reset();
	} else if(!m_MidiCfg.IsMacroDefaultSetupUsed())
	{
		m_SongFlags.set(SONG_EMBEDMIDICFG);
	}

	m_madeWithTracker = "OpenMPT " + MptVersion::ToStr(m_dwLastSavedWithVersion);

	return true;
#endif // MPT_EXTERNAL_SAMPLES
}
Esempio n. 17
0
	void operator() (ModCommand &m)
	{
		const CHANNELINDEX curChn = chn;
		chn++;
		if(chn >= sndFile.GetNumChannels())
		{
			chn = 0;
		}

		if(m.IsPcNote())
		{
			return;
		}

		if(sndFile.GetType() == MOD_TYPE_S3M)
		{
			// Out-of-range global volume commands should be ignored in S3M. Fixed in OpenMPT 1.19 (r831).
			// So for tracks made with older versions of OpenMPT, we limit invalid global volume commands.
			if(sndFile.m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 19, 00, 00) && m.command == CMD_GLOBALVOLUME)
			{
				LimitMax(m.param, ModCommand::PARAM(64));
			}
		}

		else if((sndFile.GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)))
		{
			if(sndFile.m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 03, 02) ||
				(!compatPlay && sndFile.m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 00, 00)))
			{
				if(m.command == CMD_GLOBALVOLUME)
				{
					// Out-of-range global volume commands should be ignored in IT.
					// OpenMPT 1.17.03.02 fixed this in compatible mode, OpenMPT 1.20 fixes it in normal mode as well.
					// So for tracks made with older versions than OpenMPT 1.17.03.02 or tracks made with 1.17.03.02 <= version < 1.20, we limit invalid global volume commands.
					LimitMax(m.param, ModCommand::PARAM(128));
				}

				// SC0 and SD0 should be interpreted as SC1 and SD1 in IT files.
				// OpenMPT 1.17.03.02 fixed this in compatible mode, OpenMPT 1.20 fixes it in normal mode as well.
				else if(m.command == CMD_S3MCMDEX)
				{
					if(m.param == 0xC0)
					{
						m.command = CMD_NONE;
						m.note = NOTE_NOTECUT;
					} else if(m.param == 0xD0)
					{
						m.command = CMD_NONE;
					}
				}
			}

			// In the IT format, slide commands with both nibbles set should be ignored.
			// For note volume slides, OpenMPT 1.18 fixes this in compatible mode, OpenMPT 1.20 fixes this in normal mode as well.
			const bool noteVolSlide =
				(sndFile.m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 18, 00, 00) ||
				(!compatPlay && sndFile.m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 00, 00)))
				&&
				(m.command == CMD_VOLUMESLIDE || m.command == CMD_VIBRATOVOL || m.command == CMD_TONEPORTAVOL || m.command == CMD_PANNINGSLIDE);

			// OpenMPT 1.20 also fixes this for global volume and channel volume slides.
			const bool chanVolSlide =
				(sndFile.m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 00, 00))
				&&
				(m.command == CMD_GLOBALVOLSLIDE || m.command == CMD_CHANNELVOLSLIDE);

			if(noteVolSlide || chanVolSlide)
			{
				if((m.param & 0x0F) != 0x00 && (m.param & 0x0F) != 0x0F && (m.param & 0xF0) != 0x00 && (m.param & 0xF0) != 0xF0)
				{
					m.param &= 0x0F;
				}
			}

			if(sndFile.m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 22, 01, 04)
				&& sndFile.m_dwLastSavedWithVersion != MAKE_VERSION_NUMERIC(1, 22, 00, 00))	// Ignore compatibility export
			{
				// OpenMPT 1.22.01.04 fixes illegal (out of range) instrument numbers; they should do nothing. In previous versions, they stopped the playing sample.
				if(sndFile.GetNumInstruments() && m.instr > sndFile.GetNumInstruments() && !compatPlay)
				{
					m.volcmd = VOLCMD_VOLUME;
					m.vol = 0;
				}
			}
		}

		else if(sndFile.GetType() == MOD_TYPE_XM)
		{
			// Something made be believe that out-of-range global volume commands are ignored in XM
			// just like they are ignored in IT, but apparently they are not. Aaaaaargh!
			if(((sndFile.m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 17, 03, 02) && compatPlay) || (sndFile.m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 20, 00, 00)))
				&& sndFile.m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 24, 02, 02)
				&& m.command == CMD_GLOBALVOLUME
				&& m.param > 64)
			{
				m.command = CMD_NONE;
			}

			if(sndFile.m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 19, 00, 00)
				|| (!compatPlay && sndFile.m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 00, 00)))
			{
				if(m.command == CMD_OFFSET && m.volcmd == VOLCMD_TONEPORTAMENTO)
				{
					// If there are both a portamento and an offset effect, the portamento should be preferred in XM files.
					// OpenMPT 1.19 fixed this in compatible mode, OpenMPT 1.20 fixes it in normal mode as well.
					m.command = CMD_NONE;
				}
			}

			if(sndFile.m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 01, 10)
				&& m.volcmd == VOLCMD_TONEPORTAMENTO && m.command == CMD_TONEPORTAMENTO
				&& (m.vol != 0 || compatPlay) && m.param != 0)
			{
				// Mx and 3xx on the same row does weird things in FT2: 3xx is completely ignored and the Mx parameter is doubled. Fixed in revision 1312 / OpenMPT 1.20.01.10
				// Previously the values were just added up, so let's fix this!
				m.volcmd = VOLCMD_NONE;
				const uint16 param = static_cast<uint16>(m.param) + static_cast<uint16>(m.vol << 4);
				m.param = mpt::saturate_cast<ModCommand::PARAM>(param);
			}

			if(sndFile.m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 22, 07, 09)
				&& m.command == CMD_SPEED && m.param == 0)
			{
				// OpenMPT can emulate FT2's F00 behaviour now.
				m.command = CMD_NONE;
			}
		}

		if(sndFile.m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 00, 00))
		{
			// Pattern Delay fixes

			const bool fixS6x = (m.command == CMD_S3MCMDEX && (m.param & 0xF0) == 0x60);
			// We also fix X6x commands in hacked XM files, since they are treated identically to the S6x command in IT/S3M files.
			// We don't treat them in files made with OpenMPT 1.18+ that have compatible play enabled, though, since they are ignored there anyway.
			const bool fixX6x = (m.command == CMD_XFINEPORTAUPDOWN && (m.param & 0xF0) == 0x60
				&& (!(compatPlay && sndFile.GetType() == MOD_TYPE_XM) || sndFile.m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 18, 00, 00)));

			if(fixS6x || fixX6x)
			{
				// OpenMPT 1.20 fixes multiple fine pattern delays on the same row. Previously, only the last command was considered,
				// but all commands should be added up. Since Scream Tracker 3 itself doesn't support S6x, we also use Impulse Tracker's behaviour here,
				// since we can assume that most S3Ms that make use of S6x were composed with Impulse Tracker.
				for(ModCommand *fixCmd = (&m) - curChn; fixCmd < &m; fixCmd++)
				{
					if((fixCmd->command == CMD_S3MCMDEX || fixCmd->command == CMD_XFINEPORTAUPDOWN) && (fixCmd->param & 0xF0) == 0x60)
					{
						fixCmd->command = CMD_NONE;
					}
				}
			}

			if(m.command == CMD_S3MCMDEX && (m.param & 0xF0) == 0xE0)
			{
				// OpenMPT 1.20 fixes multiple pattern delays on the same row. Previously, only the *last* command was considered,
				// but Scream Tracker 3 and Impulse Tracker only consider the *first* command.
				for(ModCommand *fixCmd = (&m) - curChn; fixCmd < &m; fixCmd++)
				{
					if(fixCmd->command == CMD_S3MCMDEX && (fixCmd->param & 0xF0) == 0xE0)
					{
						fixCmd->command = CMD_NONE;
					}
				}
			}
		}

		// Volume column offset in IT/XM is bad, mkay?
		if(sndFile.GetType() != MOD_TYPE_MPT && m.volcmd == VOLCMD_OFFSET && m.command == CMD_NONE)
		{
			m.command = CMD_OFFSET;
			m.param = m.vol << 3;
			m.volcmd = VOLCMD_NONE;
		}

	}