bool CModDoc::IsSampleUsed(SAMPLEINDEX sample, bool searchInMutedChannels) const //------------------------------------------------------------------------------ { if(!sample || sample > GetNumSamples()) return false; if(GetNumInstruments()) { for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) { if(m_SndFile.IsSampleReferencedByInstrument(sample, i)) { return true; } } } else { for(PATTERNINDEX i = 0; i < m_SndFile.Patterns.Size(); i++) if (m_SndFile.Patterns.IsValidPat(i)) { const CPattern &pattern = m_SndFile.Patterns[i]; const ModCommand *m = pattern.Begin(); for(ROWINDEX row = 0; row < pattern.GetNumRows(); row++) { for(CHANNELINDEX chn = 0; chn < pattern.GetNumChannels(); chn++, m++) { if(searchInMutedChannels || !m_SndFile.ChnSettings[chn].dwFlags[CHN_MUTE]) { if(m->instr == sample && !m->IsPcNote()) return true; } } } } } return false; }
CEmphasisSample *CSentence::GetSample( int index ) { if ( index < 0 || index >= GetNumSamples() ) return NULL; return &m_EmphasisSamples[ index ]; }
const uint32 IDataProvider::AdvanceFrame( const float DeltaTimeMS ) { if( !bHasAddedSecondStartMarker ) { bHasAddedSecondStartMarker = true; // Add the default values. FrameCounters.Add( LastSecondFrameCounter ); AccumulatedFrameCounters.Add( GetNumFrames() ); } ElapsedTimeMS += DeltaTimeMS; LastSecondFrameTimeMS += DeltaTimeMS; LastSecondFrameCounter ++; const uint32 SampleEndIndex = GetNumSamples(); FrameIndices.Add( FIntPoint( FrameIndex, SampleEndIndex ) ); FrameTimes.Add( DeltaTimeMS ); ElapsedFrameTimes.Add( ElapsedTimeMS ); // Update the values. FrameCounters.Last() = LastSecondFrameCounter; AccumulatedFrameCounters.Last() = GetNumFrames(); int NumLongFrames = 0; while( LastSecondFrameTimeMS > 1000.0f ) { if( NumLongFrames > 0 ) { // Add the default values. FrameCounters.Add( LastSecondFrameCounter ); AccumulatedFrameCounters.Add( GetNumFrames() ); } LastSecondFrameTimeMS -= 1000.0f; bHasAddedSecondStartMarker = false; LastSecondFrameCounter = 0; NumLongFrames ++; } FrameIndex = SampleEndIndex; return FrameIndex; }
void ProcessDiscardInvalidateTilesBE(DRAW_CONTEXT *pDC, uint32_t workerId, uint32_t macroTile, void *pData) { DISCARD_INVALIDATE_TILES_DESC *pDesc = (DISCARD_INVALIDATE_TILES_DESC *)pData; SWR_CONTEXT *pContext = pDC->pContext; const int32_t numSamples = GetNumSamples(pDC->pState->state.rastState.sampleCount); for (uint32_t i = 0; i < SWR_NUM_ATTACHMENTS; ++i) { if (pDesc->attachmentMask & (1 << i)) { HOTTILE *pHotTile = pContext->pHotTileMgr->GetHotTileNoLoad( pContext, pDC, macroTile, (SWR_RENDERTARGET_ATTACHMENT)i, pDesc->createNewTiles, numSamples); if (pHotTile) { pHotTile->state = (HOTTILE_STATE)pDesc->newTileState; } } } }
//----------------------------------------------------------------------------- // Purpose: // Input : number - // Output : CEmphasisSample //----------------------------------------------------------------------------- CEmphasisSample *CSentence::GetBoundedSample( int number, float endtime ) { // Search for two samples which span time f static CEmphasisSample nullstart; nullstart.time = 0.0f; nullstart.value = 0.5f; static CEmphasisSample nullend; nullend.time = endtime; nullend.value = 0.5f; if ( number < 0 ) { return &nullstart; } else if ( number >= GetNumSamples() ) { return &nullend; } return GetSample( number ); }
bool CSoundFile::ReadWav(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { WAVReader wavFile(file); if(!wavFile.IsValid() || wavFile.GetNumChannels() == 0 || wavFile.GetNumChannels() > MAX_BASECHANNELS || wavFile.GetBitsPerSample() == 0 || wavFile.GetBitsPerSample() > 32 || (wavFile.GetSampleFormat() != WAVFormatChunk::fmtPCM && wavFile.GetSampleFormat() != WAVFormatChunk::fmtFloat)) { return false; } else if(loadFlags == onlyVerifyHeader) { return true; } InitializeGlobals(); m_nChannels = std::max(wavFile.GetNumChannels(), uint16(2)); if(Patterns.Insert(0, 64) || Patterns.Insert(1, 64)) { return false; } const SmpLength sampleLength = wavFile.GetSampleLength(); // Setting up module length // Calculate sample length in ticks at tempo 125 const uint32 sampleTicks = mpt::saturate_cast<uint32>(((sampleLength * 50) / wavFile.GetSampleRate()) + 1); uint32 ticksPerRow = std::max<uint32>((sampleTicks + 63u) / 63u, 1u); Order.clear(); Order.Append(0); ORDERINDEX numOrders = 1; while(ticksPerRow >= 32) { Order.Append(1); numOrders++; ticksPerRow = (sampleTicks + (64 * numOrders - 1)) / (64 * numOrders); if(numOrders == MAX_ORDERS) { break; } } m_nType = MOD_TYPE_WAV; m_nSamples = wavFile.GetNumChannels(); m_nInstruments = 0; m_nDefaultSpeed = ticksPerRow; m_nDefaultTempo = 125; m_SongFlags = SONG_LINEARSLIDES; for(CHANNELINDEX channel = 0; channel < wavFile.GetNumChannels(); channel++) { ChnSettings[channel].Reset(); ChnSettings[channel].nPan = (channel % 2u) ? 256 : 0; } // Setting up pattern PatternRow pattern = Patterns[0].GetRow(0); pattern[0].note = pattern[1].note = NOTE_MIDDLEC; pattern[0].instr = pattern[1].instr = 1; const FileReader sampleChunk = wavFile.GetSampleData(); // Read every channel into its own sample lot. for(SAMPLEINDEX channel = 0; channel < GetNumSamples(); channel++) { pattern[channel].note = pattern[0].note; pattern[channel].instr = static_cast<ModCommand::INSTR>(channel + 1); ModSample &sample = Samples[channel + 1]; sample.Initialize(); sample.uFlags = CHN_PANNING; sample.nLength = sampleLength; sample.nC5Speed = wavFile.GetSampleRate(); strcpy(m_szNames[channel + 1], ""); wavFile.ApplySampleSettings(sample, m_szNames[channel + 1]); if(wavFile.GetNumChannels() > 1) { // Pan all samples appropriately switch(channel) { case 0: sample.nPan = 0; break; case 1: sample.nPan = 256; break; case 2: sample.nPan = (wavFile.GetNumChannels() == 3 ? 128u : 64u); pattern[channel].command = CMD_S3MCMDEX; pattern[channel].param = 0x91; break; case 3: sample.nPan = 192; pattern[channel].command = CMD_S3MCMDEX; pattern[channel].param = 0x91; break; default: sample.nPan = 128; break; } } if(wavFile.GetBitsPerSample() > 8) { sample.uFlags |= CHN_16BIT; } if(wavFile.GetSampleFormat() == WAVFormatChunk::fmtFloat) { CopyWavChannel<SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeFloat32<littleEndian32> > >(sample, sampleChunk, channel, wavFile.GetNumChannels()); } else { if(wavFile.GetBitsPerSample() <= 8) { CopyWavChannel<SC::DecodeUint8>(sample, sampleChunk, channel, wavFile.GetNumChannels()); } else if(wavFile.GetBitsPerSample() <= 16) { CopyWavChannel<SC::DecodeInt16<0, littleEndian16> >(sample, sampleChunk, channel, wavFile.GetNumChannels()); } else if(wavFile.GetBitsPerSample() <= 24) { CopyWavChannel<SC::ConversionChain<SC::Convert<int16, int32>, SC::DecodeInt24<0, littleEndian24> > >(sample, sampleChunk, channel, wavFile.GetNumChannels()); } else if(wavFile.GetBitsPerSample() <= 32) { CopyWavChannel<SC::ConversionChain<SC::Convert<int16, int32>, SC::DecodeInt32<0, littleEndian32> > >(sample, sampleChunk, channel, wavFile.GetNumChannels()); } } sample.PrecomputeLoops(*this, false); } return true; }
// Base code for adding, removing, moving and duplicating samples. Returns new number of samples on success, SAMPLEINDEX_INVALID otherwise. // The new sample vector can contain SAMPLEINDEX_INVALID for adding new (empty) samples. // newOrder indices are zero-based, i.e. newOrder[0] will define the contents of the first sample slot. SAMPLEINDEX CModDoc::ReArrangeSamples(const std::vector<SAMPLEINDEX> &newOrder) //----------------------------------------------------------------------------- { if(newOrder.size() > m_SndFile.GetModSpecifications().samplesMax) { return SAMPLEINDEX_INVALID; } CriticalSection cs; const SAMPLEINDEX oldNumSamples = m_SndFile.GetNumSamples(), newNumSamples = static_cast<SAMPLEINDEX>(newOrder.size()); std::vector<int> sampleCount(oldNumSamples + 1, 0); std::vector<ModSample> sampleHeaders(oldNumSamples + 1); std::vector<SAMPLEINDEX> newIndex(oldNumSamples + 1, 0); // One of the new indexes for the old sample std::vector<std::string> sampleNames(oldNumSamples + 1); std::vector<mpt::PathString> samplePaths(oldNumSamples + 1); for(SAMPLEINDEX i = 0; i < newNumSamples; i++) { const SAMPLEINDEX origSlot = newOrder[i]; if(origSlot > 0 && origSlot <= oldNumSamples) { sampleCount[origSlot]++; sampleHeaders[origSlot] = m_SndFile.GetSample(origSlot); if(!newIndex[origSlot]) newIndex[origSlot] = i + 1; } } // First, delete all samples that will be removed anyway. for(SAMPLEINDEX i = 1; i < sampleCount.size(); i++) { if(sampleCount[i] == 0) { m_SndFile.DestroySample(i); GetSampleUndo().ClearUndo(i); } sampleNames[i] = m_SndFile.m_szNames[i]; samplePaths[i] = m_SndFile.GetSamplePath(i); } // Remove sample data references from now unused slots. for(SAMPLEINDEX i = newNumSamples + 1; i <= oldNumSamples; i++) { m_SndFile.GetSample(i).pSample = nullptr; m_SndFile.GetSample(i).nLength = 0; strcpy(m_SndFile.m_szNames[i], ""); } // Now, create new sample list. m_SndFile.m_nSamples = std::max(m_SndFile.m_nSamples, newNumSamples); // Avoid assertions when using GetSample()... for(SAMPLEINDEX i = 0; i < newNumSamples; i++) { const SAMPLEINDEX origSlot = newOrder[i]; ModSample &target = m_SndFile.GetSample(i + 1); if(origSlot > 0 && origSlot <= oldNumSamples) { // Copy an original sample. target = sampleHeaders[origSlot]; if(--sampleCount[origSlot] > 0 && sampleHeaders[origSlot].pSample != nullptr) { // This sample slot is referenced multiple times, so we have to copy the actual sample. target.pSample = ModSample::AllocateSample(target.nLength, target.GetBytesPerSample()); if(target.pSample != nullptr) { memcpy(target.pSample, sampleHeaders[origSlot].pSample, target.GetSampleSizeInBytes()); target.PrecomputeLoops(m_SndFile, false); } else { Reporting::Error("Cannot duplicate sample - out of memory!"); } } strcpy(m_SndFile.m_szNames[i + 1], sampleNames[origSlot].c_str()); m_SndFile.SetSamplePath(i + 1, samplePaths[origSlot]); } else { // Invalid sample reference. target.Initialize(m_SndFile.GetType()); target.pSample = nullptr; strcpy(m_SndFile.m_szNames[i + 1], ""); m_SndFile.ResetSamplePath(i + 1); } } GetSampleUndo().RearrangeSamples(newIndex); for(CHANNELINDEX c = 0; c < CountOf(m_SndFile.m_PlayState.Chn); c++) { ModChannel &chn = m_SndFile.m_PlayState.Chn[c]; for(SAMPLEINDEX i = 1; i <= oldNumSamples; i++) { if(chn.pModSample == &m_SndFile.GetSample(i)) { chn.pModSample = &m_SndFile.GetSample(newIndex[i]); if(i == 0 || i > newNumSamples) { chn.Reset(ModChannel::resetTotal, m_SndFile, c); } break; } } } m_SndFile.m_nSamples = newNumSamples; if(m_SndFile.GetNumInstruments()) { // Instrument mode: Update sample maps. for(INSTRUMENTINDEX i = 0; i <= m_SndFile.GetNumInstruments(); i++) { ModInstrument *ins = m_SndFile.Instruments[i]; if(ins == nullptr) { continue; } GetInstrumentUndo().RearrangeSamples(i, newIndex); for(auto &sample : ins->Keyboard) { if(sample < newIndex.size()) sample = newIndex[sample]; else sample = 0; } } } else { PrepareUndoForAllPatterns(false, "Rearrange Samples"); std::vector<ModCommand::INSTR> indices(newIndex.size(), 0); for(size_t i = 0; i < newIndex.size(); i++) { indices[i] = newIndex[i]; } m_SndFile.Patterns.ForEachModCommand(RewriteInstrumentReferencesInPatterns(indices)); } return GetNumSamples(); }
INLINE void ClearMacroTile(DRAW_CONTEXT* pDC, HANDLE hWorkerPrivateData, SWR_RENDERTARGET_ATTACHMENT rt, uint32_t macroTile, uint32_t renderTargetArrayIndex, uint32_t clear[4], const SWR_RECT& rect) { // convert clear color to hottile format // clear color is in RGBA float/uint32 simd16vector vClear; for (uint32_t comp = 0; comp < FormatTraits<format>::numComps; ++comp) { simd16scalar vComp = _simd16_load1_ps((const float*)&clear[comp]); if (FormatTraits<format>::isNormalized(comp)) { vComp = _simd16_mul_ps(vComp, _simd16_set1_ps(FormatTraits<format>::fromFloat(comp))); vComp = _simd16_castsi_ps(_simd16_cvtps_epi32(vComp)); } vComp = FormatTraits<format>::pack(comp, vComp); vClear.v[FormatTraits<format>::swizzle(comp)] = vComp; } uint32_t tileX, tileY; MacroTileMgr::getTileIndices(macroTile, tileX, tileY); // Init to full macrotile SWR_RECT clearTile = { KNOB_MACROTILE_X_DIM * int32_t(tileX), KNOB_MACROTILE_Y_DIM * int32_t(tileY), KNOB_MACROTILE_X_DIM * int32_t(tileX + 1), KNOB_MACROTILE_Y_DIM * int32_t(tileY + 1), }; // intersect with clear rect clearTile &= rect; // translate to local hottile origin clearTile.Translate(-int32_t(tileX) * KNOB_MACROTILE_X_DIM, -int32_t(tileY) * KNOB_MACROTILE_Y_DIM); // Make maximums inclusive (needed for convert to raster tiles) clearTile.xmax -= 1; clearTile.ymax -= 1; // convert to raster tiles clearTile.ymin >>= (KNOB_TILE_Y_DIM_SHIFT); clearTile.ymax >>= (KNOB_TILE_Y_DIM_SHIFT); clearTile.xmin >>= (KNOB_TILE_X_DIM_SHIFT); clearTile.xmax >>= (KNOB_TILE_X_DIM_SHIFT); const int32_t numSamples = GetNumSamples(pDC->pState->state.rastState.sampleCount); // compute steps between raster tile samples / raster tiles / macro tile rows const uint32_t rasterTileSampleStep = KNOB_TILE_X_DIM * KNOB_TILE_Y_DIM * FormatTraits<format>::bpp / 8; const uint32_t rasterTileStep = (KNOB_TILE_X_DIM * KNOB_TILE_Y_DIM * (FormatTraits<format>::bpp / 8)) * numSamples; const uint32_t macroTileRowStep = (KNOB_MACROTILE_X_DIM / KNOB_TILE_X_DIM) * rasterTileStep; const uint32_t pitch = (FormatTraits<format>::bpp * KNOB_MACROTILE_X_DIM / 8); HOTTILE* pHotTile = pDC->pContext->pHotTileMgr->GetHotTile(pDC->pContext, pDC, hWorkerPrivateData, macroTile, rt, true, numSamples, renderTargetArrayIndex); uint32_t rasterTileStartOffset = (ComputeTileOffset2D<TilingTraits<SWR_TILE_SWRZ, FormatTraits<format>::bpp>>( pitch, clearTile.xmin, clearTile.ymin)) * numSamples; uint8_t* pRasterTileRow = pHotTile->pBuffer + rasterTileStartOffset; //(ComputeTileOffset2D< TilingTraits<SWR_TILE_SWRZ, // FormatTraits<format>::bpp > >(pitch, x, y)) * numSamples; // loop over all raster tiles in the current hot tile for (int32_t y = clearTile.ymin; y <= clearTile.ymax; ++y) { uint8_t* pRasterTile = pRasterTileRow; for (int32_t x = clearTile.xmin; x <= clearTile.xmax; ++x) { for (int32_t sampleNum = 0; sampleNum < numSamples; sampleNum++) { ClearRasterTile<format>(pRasterTile, vClear); pRasterTile += rasterTileSampleStep; } } pRasterTileRow += macroTileRowStep; } pHotTile->state = HOTTILE_DIRTY; }
void ProcessClearBE(DRAW_CONTEXT* pDC, uint32_t workerId, uint32_t macroTile, void* pUserData) { SWR_CONTEXT* pContext = pDC->pContext; HANDLE hWorkerPrivateData = pContext->threadPool.pThreadData[workerId].pWorkerPrivateData; if (KNOB_FAST_CLEAR) { CLEAR_DESC* pClear = (CLEAR_DESC*)pUserData; SWR_MULTISAMPLE_COUNT sampleCount = pDC->pState->state.rastState.sampleCount; uint32_t numSamples = GetNumSamples(sampleCount); SWR_ASSERT(pClear->attachmentMask != 0); // shouldn't be here without a reason. RDTSC_BEGIN(BEClear, pDC->drawId); if (pClear->attachmentMask & SWR_ATTACHMENT_MASK_COLOR) { unsigned long rt = 0; uint32_t mask = pClear->attachmentMask & SWR_ATTACHMENT_MASK_COLOR; while (_BitScanForward(&rt, mask)) { mask &= ~(1 << rt); HOTTILE* pHotTile = pContext->pHotTileMgr->GetHotTile(pContext, pDC, hWorkerPrivateData, macroTile, (SWR_RENDERTARGET_ATTACHMENT)rt, true, numSamples, pClear->renderTargetArrayIndex); // All we want to do here is to mark the hot tile as being in a "needs clear" state. pHotTile->clearData[0] = *(uint32_t*)&(pClear->clearRTColor[0]); pHotTile->clearData[1] = *(uint32_t*)&(pClear->clearRTColor[1]); pHotTile->clearData[2] = *(uint32_t*)&(pClear->clearRTColor[2]); pHotTile->clearData[3] = *(uint32_t*)&(pClear->clearRTColor[3]); pHotTile->state = HOTTILE_CLEAR; } } if (pClear->attachmentMask & SWR_ATTACHMENT_DEPTH_BIT) { HOTTILE* pHotTile = pContext->pHotTileMgr->GetHotTile(pContext, pDC, hWorkerPrivateData, macroTile, SWR_ATTACHMENT_DEPTH, true, numSamples, pClear->renderTargetArrayIndex); pHotTile->clearData[0] = *(uint32_t*)&pClear->clearDepth; pHotTile->state = HOTTILE_CLEAR; } if (pClear->attachmentMask & SWR_ATTACHMENT_STENCIL_BIT) { HOTTILE* pHotTile = pContext->pHotTileMgr->GetHotTile(pContext, pDC, hWorkerPrivateData, macroTile, SWR_ATTACHMENT_STENCIL, true, numSamples, pClear->renderTargetArrayIndex); pHotTile->clearData[0] = pClear->clearStencil; pHotTile->state = HOTTILE_CLEAR; } RDTSC_END(BEClear, 1); } else { // Legacy clear CLEAR_DESC* pClear = (CLEAR_DESC*)pUserData; RDTSC_BEGIN(BEClear, pDC->drawId); if (pClear->attachmentMask & SWR_ATTACHMENT_MASK_COLOR) { uint32_t clearData[4]; clearData[0] = *(uint32_t*)&(pClear->clearRTColor[0]); clearData[1] = *(uint32_t*)&(pClear->clearRTColor[1]); clearData[2] = *(uint32_t*)&(pClear->clearRTColor[2]); clearData[3] = *(uint32_t*)&(pClear->clearRTColor[3]); PFN_CLEAR_TILES pfnClearTiles = gClearTilesTable[KNOB_COLOR_HOT_TILE_FORMAT]; SWR_ASSERT(pfnClearTiles != nullptr); unsigned long rt = 0; uint32_t mask = pClear->attachmentMask & SWR_ATTACHMENT_MASK_COLOR; while (_BitScanForward(&rt, mask)) { mask &= ~(1 << rt); pfnClearTiles(pDC, hWorkerPrivateData, (SWR_RENDERTARGET_ATTACHMENT)rt, macroTile, pClear->renderTargetArrayIndex, clearData, pClear->rect); } } if (pClear->attachmentMask & SWR_ATTACHMENT_DEPTH_BIT) { uint32_t clearData[4]; clearData[0] = *(uint32_t*)&pClear->clearDepth; PFN_CLEAR_TILES pfnClearTiles = gClearTilesTable[KNOB_DEPTH_HOT_TILE_FORMAT]; SWR_ASSERT(pfnClearTiles != nullptr); pfnClearTiles(pDC, hWorkerPrivateData, SWR_ATTACHMENT_DEPTH, macroTile, pClear->renderTargetArrayIndex, clearData, pClear->rect); } if (pClear->attachmentMask & SWR_ATTACHMENT_STENCIL_BIT) { uint32_t clearData[4]; clearData[0] = pClear->clearStencil; PFN_CLEAR_TILES pfnClearTiles = gClearTilesTable[KNOB_STENCIL_HOT_TILE_FORMAT]; pfnClearTiles(pDC, hWorkerPrivateData, SWR_ATTACHMENT_STENCIL, macroTile, pClear->renderTargetArrayIndex, clearData, pClear->rect); } RDTSC_END(BEClear, 1); } }
////////////////////////////////////////////////////////////////////////// /// @brief InitializeHotTiles /// for draw calls, we initialize the active hot tiles and perform deferred /// load on them if tile is in invalid state. we do this in the outer thread /// loop instead of inside the draw routine itself mainly for performance, /// to avoid unnecessary setup every triangle /// @todo support deferred clear /// @param pCreateInfo - pointer to creation info. void HotTileMgr::InitializeHotTiles(SWR_CONTEXT* pContext, DRAW_CONTEXT* pDC, uint32_t workerId, uint32_t macroID) { const API_STATE& state = GetApiState(pDC); HANDLE hWorkerPrivateData = pDC->pContext->threadPool.pThreadData[workerId].pWorkerPrivateData; uint32_t x, y; MacroTileMgr::getTileIndices(macroID, x, y); x *= KNOB_MACROTILE_X_DIM; y *= KNOB_MACROTILE_Y_DIM; uint32_t numSamples = GetNumSamples(state.rastState.sampleCount); // check RT if enabled unsigned long rtSlot = 0; uint32_t colorHottileEnableMask = state.colorHottileEnable; while (_BitScanForward(&rtSlot, colorHottileEnableMask)) { HOTTILE* pHotTile = GetHotTile(pContext, pDC, hWorkerPrivateData, macroID, (SWR_RENDERTARGET_ATTACHMENT)(SWR_ATTACHMENT_COLOR0 + rtSlot), true, numSamples); if (pHotTile->state == HOTTILE_INVALID) { RDTSC_BEGIN(BELoadTiles, pDC->drawId); // invalid hottile before draw requires a load from surface before we can draw to it pContext->pfnLoadTile(GetPrivateState(pDC), hWorkerPrivateData, KNOB_COLOR_HOT_TILE_FORMAT, (SWR_RENDERTARGET_ATTACHMENT)(SWR_ATTACHMENT_COLOR0 + rtSlot), x, y, pHotTile->renderTargetArrayIndex, pHotTile->pBuffer); pHotTile->state = HOTTILE_DIRTY; RDTSC_END(BELoadTiles, 0); } else if (pHotTile->state == HOTTILE_CLEAR) { RDTSC_BEGIN(BELoadTiles, pDC->drawId); // Clear the tile. ClearColorHotTile(pHotTile); pHotTile->state = HOTTILE_DIRTY; RDTSC_END(BELoadTiles, 0); } colorHottileEnableMask &= ~(1 << rtSlot); } // check depth if enabled if (state.depthHottileEnable) { HOTTILE* pHotTile = GetHotTile( pContext, pDC, hWorkerPrivateData, macroID, SWR_ATTACHMENT_DEPTH, true, numSamples); if (pHotTile->state == HOTTILE_INVALID) { RDTSC_BEGIN(BELoadTiles, pDC->drawId); // invalid hottile before draw requires a load from surface before we can draw to it pContext->pfnLoadTile(GetPrivateState(pDC), hWorkerPrivateData, KNOB_DEPTH_HOT_TILE_FORMAT, SWR_ATTACHMENT_DEPTH, x, y, pHotTile->renderTargetArrayIndex, pHotTile->pBuffer); pHotTile->state = HOTTILE_DIRTY; RDTSC_END(BELoadTiles, 0); } else if (pHotTile->state == HOTTILE_CLEAR) { RDTSC_BEGIN(BELoadTiles, pDC->drawId); // Clear the tile. ClearDepthHotTile(pHotTile); pHotTile->state = HOTTILE_DIRTY; RDTSC_END(BELoadTiles, 0); } } // check stencil if enabled if (state.stencilHottileEnable) { HOTTILE* pHotTile = GetHotTile( pContext, pDC, hWorkerPrivateData, macroID, SWR_ATTACHMENT_STENCIL, true, numSamples); if (pHotTile->state == HOTTILE_INVALID) { RDTSC_BEGIN(BELoadTiles, pDC->drawId); // invalid hottile before draw requires a load from surface before we can draw to it pContext->pfnLoadTile(GetPrivateState(pDC), hWorkerPrivateData, KNOB_STENCIL_HOT_TILE_FORMAT, SWR_ATTACHMENT_STENCIL, x, y, pHotTile->renderTargetArrayIndex, pHotTile->pBuffer); pHotTile->state = HOTTILE_DIRTY; RDTSC_END(BELoadTiles, 0); } else if (pHotTile->state == HOTTILE_CLEAR) { RDTSC_BEGIN(BELoadTiles, pDC->drawId); // Clear the tile. ClearStencilHotTile(pHotTile); pHotTile->state = HOTTILE_DIRTY; RDTSC_END(BELoadTiles, 0); } } }
void CSoundFile::UpgradeModule() //------------------------------ { if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 02, 46) && m_dwLastSavedWithVersion != MAKE_VERSION_NUMERIC(1, 17, 00, 00)) { // Compatible playback mode didn't exist in earlier versions, so definitely disable it. m_playBehaviour.reset(MSF_COMPATIBLE_PLAY); } const bool compatModeIT = m_playBehaviour[MSF_COMPATIBLE_PLAY] && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)); const bool compatModeXM = m_playBehaviour[MSF_COMPATIBLE_PLAY] && GetType() == MOD_TYPE_XM; if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 00, 00)) { for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) if(Instruments[i] != nullptr) { ModInstrument *ins = Instruments[i]; // Previously, volume swing values ranged from 0 to 64. They should reach from 0 to 100 instead. ins->nVolSwing = static_cast<uint8>(std::min<uint32>(ins->nVolSwing * 100 / 64, 100)); if(!compatModeIT || m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 18, 00, 00)) { // Previously, Pitch/Pan Separation was only half depth. // This was corrected in compatible mode in OpenMPT 1.18, and in OpenMPT 1.20 it is corrected in normal mode as well. ins->nPPS = (ins->nPPS + (ins->nPPS >= 0 ? 1 : -1)) / 2; } if(!compatModeIT || m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 03, 02)) { // IT compatibility 24. Short envelope loops // Previously, the pitch / filter envelope loop handling was broken, the loop was shortened by a tick (like in XM). // This was corrected in compatible mode in OpenMPT 1.17.03.02, and in OpenMPT 1.20 it is corrected in normal mode as well. ins->GetEnvelope(ENV_PITCH).Convert(MOD_TYPE_XM, GetType()); } if(m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 17, 00, 00) && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 02, 50)) { // If there are any plugins that can receive volume commands, enable volume bug emulation. if(ins->nMixPlug && ins->HasValidMIDIChannel()) { m_playBehaviour.set(kMIDICCBugEmulation); } } if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 02, 50) && (ins->nVolSwing | ins->nPanSwing | ins->nCutSwing | ins->nResSwing)) { // If there are any instruments with random variation, enable the old random variation behaviour. m_playBehaviour.set(kMPTOldSwingBehaviour); break; } } if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && (m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 03, 02) || !compatModeIT)) { // In the IT format, a sweep value of 0 shouldn't apply vibrato at all. Previously, a value of 0 was treated as "no sweep". // In OpenMPT 1.17.03.02, this was corrected in compatible mode, in OpenMPT 1.20 it is corrected in normal mode as well, // so we have to fix the setting while loading. for(SAMPLEINDEX i = 1; i <= GetNumSamples(); i++) { if(Samples[i].nVibSweep == 0 && (Samples[i].nVibDepth | Samples[i].nVibRate)) { Samples[i].nVibSweep = 255; } } } // Fix old nasty broken (non-standard) MIDI configs in files. m_MidiCfg.UpgradeMacros(); } if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 02, 10) && m_dwLastSavedWithVersion != MAKE_VERSION_NUMERIC(1, 20, 00, 00) && (GetType() & (MOD_TYPE_XM | MOD_TYPE_IT | MOD_TYPE_MPT))) { bool instrPlugs = false; // Old pitch wheel commands were closest to sample pitch bend commands if the PWD is 13. for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) { if(Instruments[i] != nullptr && Instruments[i]->nMidiChannel != MidiNoChannel) { Instruments[i]->midiPWD = 13; instrPlugs = true; } } if(instrPlugs) { m_playBehaviour.set(kOldMIDIPitchBends); } } if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 22, 03, 12) && m_dwLastSavedWithVersion != MAKE_VERSION_NUMERIC(1, 22, 00, 00) && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && (m_playBehaviour[MSF_COMPATIBLE_PLAY] || m_playBehaviour[kMPTOldSwingBehaviour])) { // The "correct" pan swing implementation did nothing if the instrument also had a pan envelope. // If there's a pan envelope, disable pan swing for such modules. for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) { if(Instruments[i] != nullptr && Instruments[i]->nPanSwing != 0 && Instruments[i]->PanEnv.dwFlags[ENV_ENABLED]) { Instruments[i]->nPanSwing = 0; } } } #ifndef NO_PLUGINS if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 22, 07, 01)) { // Convert ANSI plugin path names to UTF-8 (irrelevant in probably 99% of all cases anyway, I think I've never seen a VST plugin with a non-ASCII file name) for(PLUGINDEX i = 0; i < MAX_MIXPLUGINS; i++) { #if defined(MODPLUG_TRACKER) const std::string name = mpt::ToCharset(mpt::CharsetUTF8, mpt::CharsetLocale, m_MixPlugins[i].Info.szLibraryName); #else const std::string name = mpt::ToCharset(mpt::CharsetUTF8, mpt::CharsetWindows1252, m_MixPlugins[i].Info.szLibraryName); #endif mpt::String::Copy(m_MixPlugins[i].Info.szLibraryName, name); } } #endif // NO_PLUGINS // Starting from OpenMPT 1.22.07.19, FT2-style panning was applied in compatible mix mode. // Starting from OpenMPT 1.23.01.04, FT2-style panning has its own mix mode instead. if(GetType() == MOD_TYPE_XM) { if(m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 22, 07, 19) && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 23, 01, 04) && GetMixLevels() == mixLevelsCompatible) { SetMixLevels(mixLevelsCompatibleFT2); } } if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 25, 00, 07) && m_dwLastSavedWithVersion != MAKE_VERSION_NUMERIC(1, 25, 00, 00)) { // Instrument plugins can now receive random volume variation. // For old instruments, disable volume swing in case there was no sample associated. for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) { if(Instruments[i] != nullptr && Instruments[i]->nVolSwing != 0 && Instruments[i]->nMidiChannel != MidiNoChannel) { bool hasSample = false; for(size_t k = 0; k < CountOf(Instruments[k]->Keyboard); k++) { if(Instruments[i]->Keyboard[k] != 0) { hasSample = true; break; } } if(!hasSample) { Instruments[i]->nVolSwing = 0; } } } } if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 26, 00, 00)) { for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) if(Instruments[i] != nullptr) { ModInstrument *ins = Instruments[i]; // Even after fixing it in OpenMPT 1.18, instrument PPS was only half the depth. ins->nPPS = (ins->nPPS + (ins->nPPS >= 0 ? 1 : -1)) / 2; // OpenMPT 1.18 fixed the depth of random pan in compatible mode. // OpenMPT 1.26 fixes it in normal mode too. if(!compatModeIT || m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 18, 00, 00)) { ins->nPanSwing = (ins->nPanSwing + 3) / 4u; } } } Patterns.ForEachModCommand(UpgradePatternData(*this)); // Convert compatibility flags // NOTE: Some of these version numbers are just approximations. // Sometimes a quirk flag is shared by several code locations which might have been fixed at different times. // Sometimes the quirk behaviour has been revised over time, in which case the first version that emulated the quirk enables it. struct PlayBehaviourVersion { PlayBehaviour behaviour; MptVersion::VersionNum version; }; if(compatModeIT && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 26, 00, 00)) { // Pre-1.26: Detailed compatibility flags did not exist. static const PlayBehaviourVersion behaviours[] = { { kTempoClamp, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, { kPerChannelGlobalVolSlide, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, { kPanOverride, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, { kITInstrWithoutNote, MAKE_VERSION_NUMERIC(1, 17, 02, 46) }, { kITVolColFinePortamento, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, { kITArpeggio, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, { kITOutOfRangeDelay, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, { kITPortaMemoryShare, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, { kITPatternLoopTargetReset, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, { kITFT2PatternLoop, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, { kITPingPongNoReset, MAKE_VERSION_NUMERIC(1, 17, 02, 51) }, { kITEnvelopeReset, MAKE_VERSION_NUMERIC(1, 17, 02, 51) }, { kITClearOldNoteAfterCut, MAKE_VERSION_NUMERIC(1, 17, 02, 52) }, { kITVibratoTremoloPanbrello, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, { kITTremor, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, { kITRetrigger, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, { kITMultiSampleBehaviour, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, { kITPortaTargetReached, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, { kITPatternLoopBreak, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, { kITOffset, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, { kITSwingBehaviour, MAKE_VERSION_NUMERIC(1, 18, 00, 00) }, { kITNNAReset, MAKE_VERSION_NUMERIC(1, 18, 00, 00) }, { kITSCxStopsSample, MAKE_VERSION_NUMERIC(1, 18, 00, 01) }, { kITEnvelopePositionHandling, MAKE_VERSION_NUMERIC(1, 18, 01, 00) }, { kITPortamentoInstrument, MAKE_VERSION_NUMERIC(1, 19, 00, 01) }, { kITPingPongMode, MAKE_VERSION_NUMERIC(1, 19, 00, 21) }, { kITRealNoteMapping, MAKE_VERSION_NUMERIC(1, 19, 00, 30) }, { kITHighOffsetNoRetrig, MAKE_VERSION_NUMERIC(1, 20, 00, 14) }, { kITFilterBehaviour, MAKE_VERSION_NUMERIC(1, 20, 00, 35) }, { kITNoSurroundPan, MAKE_VERSION_NUMERIC(1, 20, 00, 53) }, { kITShortSampleRetrig, MAKE_VERSION_NUMERIC(1, 20, 00, 54) }, { kITPortaNoNote, MAKE_VERSION_NUMERIC(1, 20, 00, 56) }, { kITDontResetNoteOffOnPorta, MAKE_VERSION_NUMERIC(1, 20, 02, 06) }, { kITVolColMemory, MAKE_VERSION_NUMERIC(1, 21, 01, 16) }, { kITPortamentoSwapResetsPos, MAKE_VERSION_NUMERIC(1, 21, 01, 25) }, { kITEmptyNoteMapSlot, MAKE_VERSION_NUMERIC(1, 21, 01, 25) }, { kITFirstTickHandling, MAKE_VERSION_NUMERIC(1, 22, 07, 09) }, { kITSampleAndHoldPanbrello, MAKE_VERSION_NUMERIC(1, 22, 07, 19) }, { kITClearPortaTarget, MAKE_VERSION_NUMERIC(1, 23, 04, 03) }, { kITPanbrelloHold, MAKE_VERSION_NUMERIC(1, 24, 01, 06) }, { kITPanningReset, MAKE_VERSION_NUMERIC(1, 24, 01, 06) }, { kITPatternLoopWithJumps, MAKE_VERSION_NUMERIC(1, 25, 00, 19) }, }; for(size_t i = 0; i < CountOf(behaviours); i++) { m_playBehaviour.set(behaviours[i].behaviour, (m_dwLastSavedWithVersion >= behaviours[i].version || m_dwLastSavedWithVersion == (behaviours[i].version & 0xFFFF0000))); } } else if(compatModeXM && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 26, 00, 00)) { // Pre-1.26: Detailed compatibility flags did not exist. static const PlayBehaviourVersion behaviours[] = { { kTempoClamp, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, { kPerChannelGlobalVolSlide, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, { kPanOverride, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, { kITFT2PatternLoop, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, { kFT2Arpeggio, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, { kFT2Retrigger, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, { kFT2VolColVibrato, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, { kFT2PortaNoNote, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, { kFT2KeyOff, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, { kFT2PanSlide, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, { kFT2OffsetOutOfRange, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, { kFT2RestrictXCommand, MAKE_VERSION_NUMERIC(1, 18, 00, 00) }, { kFT2RetrigWithNoteDelay, MAKE_VERSION_NUMERIC(1, 18, 00, 00) }, { kFT2SetPanEnvPos, MAKE_VERSION_NUMERIC(1, 18, 00, 00) }, { kFT2PortaIgnoreInstr, MAKE_VERSION_NUMERIC(1, 18, 00, 01) }, { kFT2VolColMemory, MAKE_VERSION_NUMERIC(1, 18, 01, 00) }, { kFT2LoopE60Restart, MAKE_VERSION_NUMERIC(1, 18, 02, 01) }, { kFT2ProcessSilentChannels, MAKE_VERSION_NUMERIC(1, 18, 02, 01) }, { kFT2ReloadSampleSettings, MAKE_VERSION_NUMERIC(1, 20, 00, 36) }, { kFT2PortaDelay, MAKE_VERSION_NUMERIC(1, 20, 00, 40) }, { kFT2Transpose, MAKE_VERSION_NUMERIC(1, 20, 00, 62) }, { kFT2PatternLoopWithJumps, MAKE_VERSION_NUMERIC(1, 20, 00, 69) }, { kFT2PortaTargetNoReset, MAKE_VERSION_NUMERIC(1, 20, 00, 69) }, { kFT2EnvelopeEscape, MAKE_VERSION_NUMERIC(1, 20, 00, 77) }, { kFT2Tremor, MAKE_VERSION_NUMERIC(1, 20, 01, 11) }, { kFT2OutOfRangeDelay, MAKE_VERSION_NUMERIC(1, 20, 02, 02) }, { kFT2Periods, MAKE_VERSION_NUMERIC(1, 22, 03, 01) }, { kFT2PanWithDelayedNoteOff, MAKE_VERSION_NUMERIC(1, 22, 03, 02) }, { kFT2VolColDelay, MAKE_VERSION_NUMERIC(1, 22, 07, 19) }, { kFT2FinetunePrecision, MAKE_VERSION_NUMERIC(1, 22, 07, 19) }, }; for(size_t i = 0; i < CountOf(behaviours); i++) { m_playBehaviour.set(behaviours[i].behaviour, m_dwLastSavedWithVersion >= behaviours[i].version); } } if(GetType() == MOD_TYPE_IT) { // The following behaviours were added in/after OpenMPT 1.26, so are not affected by the upgrade mechanism above. static const PlayBehaviourVersion behaviours[] = { { kITInstrWithNoteOff, MAKE_VERSION_NUMERIC(1, 26, 00, 01) }, { kITMultiSampleInstrumentNumber, MAKE_VERSION_NUMERIC(1, 27, 00, 27) }, }; for(size_t i = 0; i < CountOf(behaviours); i++) { if(m_dwLastSavedWithVersion < (behaviours[i].version & 0xFFFF0000)) m_playBehaviour.reset(behaviours[i].behaviour); // Full version information available, i.e. not compatibility-exported. if(m_dwLastSavedWithVersion > (behaviours[i].version & 0xFFFF0000) && m_dwLastSavedWithVersion < behaviours[i].version) m_playBehaviour.reset(behaviours[i].behaviour); } } else if(GetType() == MOD_TYPE_XM) { // The following behaviours were added after OpenMPT 1.26, so are not affected by the upgrade mechanism above. static const PlayBehaviourVersion behaviours[] = { { kFT2NoteOffFlags, MAKE_VERSION_NUMERIC(1, 27, 00, 27) }, }; for(size_t i = 0; i < CountOf(behaviours); i++) { if(m_dwLastSavedWithVersion < behaviours[i].version) m_playBehaviour.reset(behaviours[i].behaviour); } } else if(GetType() == MOD_TYPE_S3M) { if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 18, 00, 00)) m_playBehaviour.reset(kST3NoMutedChannels); if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 00, 00)) m_playBehaviour.reset(kST3EffectMemory); if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 22, 00, 00)) m_playBehaviour.reset(kST3PortaSampleChange); if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 26, 00, 00)) m_playBehaviour.reset(kST3VibratoMemory); if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 26, 00, 00)) m_playBehaviour.reset(kITPanbrelloHold); } if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 00, 00)) { // MPT 1.16 has a maximum tempo of 255. m_playBehaviour.set(kTempoClamp); } else if(m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 17, 00, 00) && m_dwLastSavedWithVersion <= MAKE_VERSION_NUMERIC(1, 20, 01, 03) && m_dwLastSavedWithVersion != MAKE_VERSION_NUMERIC(1, 20, 00, 00)) { // OpenMPT introduced some "fixes" that execute regular portamentos also at speed 1. m_playBehaviour.set(kSlidesAtSpeed1); } if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 24, 00, 00)) { // No frequency slides in Hz before OpenMPT 1.24 m_playBehaviour.reset(kHertzInLinearMode); } else if(m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 24, 00, 00) && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 26, 00, 00) && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))) { // Frequency slides were always in Hz rather than periods in this version range. m_playBehaviour.set(kHertzInLinearMode); } }
//----------------------------------------------------------------------------- // Purpose: // Input : time - // type - // Output : float //----------------------------------------------------------------------------- float CSentence::GetIntensity( float time, float endtime ) { float zeroValue = 0.5f; int c = GetNumSamples(); if ( c <= 0 ) { return zeroValue; } int i; for ( i = -1 ; i < c; i++ ) { CEmphasisSample *s = GetBoundedSample( i, endtime ); CEmphasisSample *n = GetBoundedSample( i + 1, endtime ); if ( !s || !n ) continue; if ( time >= s->time && time <= n->time ) { break; } } int prev = i - 1; int start = i; int end = i + 1; int next = i + 2; prev = max( -1, prev ); start = max( -1, start ); end = min( end, GetNumSamples() ); next = min( next, GetNumSamples() ); CEmphasisSample *esPre = GetBoundedSample( prev, endtime ); CEmphasisSample *esStart = GetBoundedSample( start, endtime ); CEmphasisSample *esEnd = GetBoundedSample( end, endtime ); CEmphasisSample *esNext = GetBoundedSample( next, endtime ); float dt = esEnd->time - esStart->time; dt = clamp( dt, 0.01f, 1.0f ); Vector vPre( esPre->time, esPre->value, 0 ); Vector vStart( esStart->time, esStart->value, 0 ); Vector vEnd( esEnd->time, esEnd->value, 0 ); Vector vNext( esNext->time, esNext->value, 0 ); float f2 = ( time - esStart->time ) / ( dt ); f2 = clamp( f2, 0.0f, 1.0f ); Vector vOut; Catmull_Rom_Spline( vPre, vStart, vEnd, vNext, f2, vOut ); float retval = clamp( vOut.y, 0.0f, 1.0f ); return retval; }
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 }
// Render count * number of channels samples void CSoundFile::CreateStereoMix(int count) { mixsample_t *pOfsL, *pOfsR; if (!count) return; // Resetting sound buffer StereoFill(MixSoundBuffer, count, gnDryROfsVol, gnDryLOfsVol); if(m_MixerSettings.gnChannels > 2) InitMixBuffer(MixRearBuffer, count*2); CHANNELINDEX nchmixed = 0; const bool ITPingPongMode = m_playBehaviour[kITPingPongMode]; for(uint32 nChn = 0; nChn < m_nMixChannels; nChn++) { ModChannel &chn = m_PlayState.Chn[m_PlayState.ChnMix[nChn]]; if(!chn.pCurrentSample) continue; pOfsR = &gnDryROfsVol; pOfsL = &gnDryLOfsVol; uint32 functionNdx = MixFuncTable::ResamplingModeToMixFlags(static_cast<ResamplingMode>(chn.resamplingMode)); if(chn.dwFlags[CHN_16BIT]) functionNdx |= MixFuncTable::ndx16Bit; if(chn.dwFlags[CHN_STEREO]) functionNdx |= MixFuncTable::ndxStereo; #ifndef NO_FILTER if(chn.dwFlags[CHN_FILTER]) functionNdx |= MixFuncTable::ndxFilter; #endif mixsample_t *pbuffer = MixSoundBuffer; #ifndef NO_REVERB if(((m_MixerSettings.DSPMask & SNDDSP_REVERB) && !chn.dwFlags[CHN_NOREVERB]) || chn.dwFlags[CHN_REVERB]) { pbuffer = m_Reverb.GetReverbSendBuffer(count); pOfsR = &m_Reverb.gnRvbROfsVol; pOfsL = &m_Reverb.gnRvbLOfsVol; } #endif if(chn.dwFlags[CHN_SURROUND] && m_MixerSettings.gnChannels > 2) pbuffer = MixRearBuffer; //Look for plugins associated with this implicit tracker channel. #ifndef NO_PLUGINS PLUGINDEX nMixPlugin = GetBestPlugin(m_PlayState.ChnMix[nChn], PrioritiseInstrument, RespectMutes); if ((nMixPlugin > 0) && (nMixPlugin <= MAX_MIXPLUGINS) && m_MixPlugins[nMixPlugin - 1].pMixPlugin != nullptr) { // Render into plugin buffer instead of global buffer SNDMIXPLUGINSTATE &mixState = m_MixPlugins[nMixPlugin - 1].pMixPlugin->m_MixState; if (mixState.pMixBuffer) { pbuffer = mixState.pMixBuffer; pOfsR = &mixState.nVolDecayR; pOfsL = &mixState.nVolDecayL; if (!(mixState.dwFlags & SNDMIXPLUGINSTATE::psfMixReady)) { StereoFill(pbuffer, count, *pOfsR, *pOfsL); mixState.dwFlags |= SNDMIXPLUGINSTATE::psfMixReady; } } } #endif // NO_PLUGINS MixLoopState mixLoopState(chn); //////////////////////////////////////////////////// CHANNELINDEX naddmix = 0; int nsamples = count; // Keep mixing this sample until the buffer is filled. do { uint32 nrampsamples = nsamples; int32 nSmpCount; if(chn.nRampLength > 0) { if (nrampsamples > chn.nRampLength) nrampsamples = chn.nRampLength; } if((nSmpCount = mixLoopState.GetSampleCount(chn, nrampsamples, ITPingPongMode)) <= 0) { // Stopping the channel chn.pCurrentSample = nullptr; chn.nLength = 0; chn.position.Set(0); chn.nRampLength = 0; EndChannelOfs(chn, pbuffer, nsamples); *pOfsR += chn.nROfs; *pOfsL += chn.nLOfs; chn.nROfs = chn.nLOfs = 0; chn.dwFlags.reset(CHN_PINGPONGFLAG); break; } // Should we mix this channel ? if((nchmixed >= m_MixerSettings.m_nMaxMixChannels) // Too many channels || (!chn.nRampLength && !(chn.leftVol | chn.rightVol))) // Channel is completely silent { chn.position += chn.increment * nSmpCount; chn.nROfs = chn.nLOfs = 0; pbuffer += nSmpCount * 2; naddmix = 0; } else { // Do mixing mixsample_t *pbufmax = pbuffer + (nSmpCount * 2); chn.nROfs = - *(pbufmax-2); chn.nLOfs = - *(pbufmax-1); #ifdef _DEBUG SamplePosition targetpos = chn.position + chn.increment * nSmpCount; #endif MixFuncTable::Functions[functionNdx | (chn.nRampLength ? MixFuncTable::ndxRamp : 0)](chn, m_Resampler, pbuffer, nSmpCount); #ifdef _DEBUG MPT_ASSERT(chn.position.GetUInt() == targetpos.GetUInt()); #endif chn.nROfs += *(pbufmax-2); chn.nLOfs += *(pbufmax-1); pbuffer = pbufmax; naddmix = 1; } nsamples -= nSmpCount; if (chn.nRampLength) { if (chn.nRampLength <= static_cast<uint32>(nSmpCount)) { // Ramping is done chn.nRampLength = 0; chn.leftVol = chn.newLeftVol; chn.rightVol = chn.newRightVol; chn.rightRamp = chn.leftRamp = 0; if(chn.dwFlags[CHN_NOTEFADE] && !chn.nFadeOutVol) { chn.nLength = 0; chn.pCurrentSample = nullptr; } } else { chn.nRampLength -= nSmpCount; } } if(chn.position.GetUInt() >= chn.nLoopEnd && chn.dwFlags[CHN_LOOP]) { if(m_playBehaviour[kMODSampleSwap] && chn.nNewIns && chn.nNewIns <= GetNumSamples() && chn.pModSample != &Samples[chn.nNewIns]) { // ProTracker compatibility: Instrument changes without a note do not happen instantly, but rather when the sample loop has finished playing. // Test case: PTInstrSwap.mod const ModSample &smp = Samples[chn.nNewIns]; chn.pModSample = &smp; chn.pCurrentSample = smp.pSample; chn.dwFlags = (chn.dwFlags & CHN_CHANNELFLAGS) | smp.uFlags; chn.nLength = smp.uFlags[CHN_LOOP] ? smp.nLoopEnd : smp.nLength; chn.nLoopStart = smp.nLoopStart; chn.nLoopEnd = smp.nLoopEnd; chn.position.SetInt(chn.nLoopStart); mixLoopState.UpdateLookaheadPointers(chn); if(!chn.pCurrentSample) { break; } } else if(m_playBehaviour[kMODOneShotLoops] && chn.nLoopStart == 0) { // ProTracker "oneshot" loops (if loop start is 0, play the whole sample once and then repeat until loop end) chn.position.SetInt(0); chn.nLoopEnd = chn.nLength = chn.pModSample->nLoopEnd; } } } while(nsamples > 0); // Restore sample pointer in case it got changed through loop wrap-around chn.pCurrentSample = mixLoopState.samplePointer; nchmixed += naddmix; #ifndef NO_PLUGINS if(naddmix && nMixPlugin > 0 && nMixPlugin <= MAX_MIXPLUGINS && m_MixPlugins[nMixPlugin - 1].pMixPlugin) { m_MixPlugins[nMixPlugin - 1].pMixPlugin->ResetSilence(); } #endif // NO_PLUGINS } m_nMixStat = std::max<CHANNELINDEX>(m_nMixStat, nchmixed); }
bool CSoundFile::ReadITQ(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------ { file.Rewind(); ITFileHeader fileHeader; if(!file.ReadConvertEndianness(fileHeader) || (memcmp(fileHeader.id, "ITQM", 4)) || fileHeader.insnum > 0xFF || fileHeader.smpnum >= MAX_SAMPLES || !file.CanRead(fileHeader.ordnum + (fileHeader.insnum + fileHeader.smpnum + fileHeader.patnum) * 4)) { return false; } else if(loadFlags == onlyVerifyHeader) { return true; } InitializeGlobals(); bool interpretModPlugMade = false; // OpenMPT crap at the end of file file.Seek(file.GetLength() - 4); size_t mptStartPos = file.ReadUint32LE(); if(mptStartPos >= file.GetLength() || mptStartPos < 0x100) { mptStartPos = file.GetLength(); } if(!memcmp(fileHeader.id, "tpm.", 4)) { // Legacy MPTM files (old 1.17.02.xx releases) ChangeModTypeTo(MOD_TYPE_MPT); } else { if(mptStartPos <= file.GetLength() - 3 && fileHeader.cwtv > 0x888 && fileHeader.cwtv <= 0xFFF) { file.Seek(mptStartPos); ChangeModTypeTo(file.ReadMagic("228") ? MOD_TYPE_MPT : MOD_TYPE_IT); } else { ChangeModTypeTo(MOD_TYPE_IT); } if(GetType() == MOD_TYPE_IT) { // Which tracker was used to made this? if((fileHeader.cwtv & 0xF000) == 0x5000) { // OpenMPT Version number (Major.Minor) // This will only be interpreted as "made with ModPlug" (i.e. disable compatible playback etc) if the "reserved" field is set to "OMPT" - else, compatibility was used. m_dwLastSavedWithVersion = (fileHeader.cwtv & 0x0FFF) << 16; if(!memcmp(fileHeader.reserved, "OMPT", 4)) interpretModPlugMade = true; } else if(fileHeader.cmwt == 0x888 || fileHeader.cwtv == 0x888) { // OpenMPT 1.17 and 1.18 (raped IT format) // Exact version number will be determined later. interpretModPlugMade = true; } else if(fileHeader.cwtv == 0x0217 && fileHeader.cmwt == 0x0200 && !memcmp(fileHeader.reserved, "\0\0\0\0", 4)) { if(memchr(fileHeader.chnpan, 0xFF, sizeof(fileHeader.chnpan)) != NULL) { // ModPlug Tracker 1.16 (semi-raped IT format) m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 16, 00, 00); madeWithTracker = "ModPlug tracker 1.09 - 1.16"; } else { // OpenMPT 1.17 disguised as this in compatible mode, // but never writes 0xFF in the pan map for unused channels (which is an invalid value). m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 17, 00, 00); madeWithTracker = "OpenMPT 1.17 (compatibility export)"; } interpretModPlugMade = true; } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0202 && !memcmp(fileHeader.reserved, "\0\0\0\0", 4)) { // ModPlug Tracker b3.3 - 1.09, instruments 557 bytes apart m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 09, 00, 00); madeWithTracker = "ModPlug tracker b3.3 - 1.09"; interpretModPlugMade = true; } } else // case: type == MOD_TYPE_MPT { if (fileHeader.cwtv >= verMptFileVerLoadLimit) { AddToLog(str_LoadingIncompatibleVersion); return false; } else if (fileHeader.cwtv > verMptFileVer) { AddToLog(str_LoadingMoreRecentVersion); } } } if(GetType() == MOD_TYPE_IT) mptStartPos = file.GetLength(); // Read row highlights if((fileHeader.special & ITFileHeader::embedPatternHighlights)) { // MPT 1.09, 1.07 and most likely other old MPT versions leave this blank (0/0), but have the "special" flag set. // Newer versions of MPT and OpenMPT 1.17 *always* write 4/16 here. // Thus, we will just ignore those old versions. if(m_dwLastSavedWithVersion == 0 || m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 17, 03, 02)) { m_nDefaultRowsPerBeat = fileHeader.highlight_minor; m_nDefaultRowsPerMeasure = fileHeader.highlight_major; } #ifdef _DEBUG if((fileHeader.highlight_minor | fileHeader.highlight_major) == 0) { Log("IT Header: Row highlight is 0"); } #endif } m_SongFlags.set(SONG_LINEARSLIDES, (fileHeader.flags & ITFileHeader::linearSlides) != 0); m_SongFlags.set(SONG_ITOLDEFFECTS, (fileHeader.flags & ITFileHeader::itOldEffects) != 0); m_SongFlags.set(SONG_ITCOMPATGXX, (fileHeader.flags & ITFileHeader::itCompatGxx) != 0); m_SongFlags.set(SONG_EMBEDMIDICFG, (fileHeader.flags & ITFileHeader::reqEmbeddedMIDIConfig) || (fileHeader.special & ITFileHeader::embedMIDIConfiguration)); m_SongFlags.set(SONG_EXFILTERRANGE, (fileHeader.flags & ITFileHeader::extendedFilterRange) != 0); mpt::String::Read<mpt::String::spacePadded>(songName, fileHeader.songname); // Global Volume m_nDefaultGlobalVolume = fileHeader.globalvol << 1; if(m_nDefaultGlobalVolume > MAX_GLOBAL_VOLUME) m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME; if(fileHeader.speed) m_nDefaultSpeed = fileHeader.speed; m_nDefaultTempo = std::max(uint8(32), fileHeader.tempo); // Tempo 31 is possible. due to conflicts with the rest of the engine, let's just clamp it to 32. m_nSamplePreAmp = std::min(fileHeader.mv, uint8(128)); // Reading Channels Pan Positions for(CHANNELINDEX i = 0; i < 64; i++) if(fileHeader.chnpan[i] != 0xFF) { ChnSettings[i].Reset(); ChnSettings[i].nVolume = Clamp(fileHeader.chnvol[i], uint8(0), uint8(64)); if(fileHeader.chnpan[i] & 0x80) ChnSettings[i].dwFlags.set(CHN_MUTE); uint8 n = fileHeader.chnpan[i] & 0x7F; if(n <= 64) ChnSettings[i].nPan = n * 4; if(n == 100) ChnSettings[i].dwFlags.set(CHN_SURROUND); } // Reading orders file.Seek(sizeof(ITFileHeader)); if(GetType() == MOD_TYPE_IT) { Order.ReadAsByte(file, fileHeader.ordnum); } else { if(fileHeader.cwtv > 0x88A && fileHeader.cwtv <= 0x88D) { Order.Deserialize(file); } else { Order.ReadAsByte(file, fileHeader.ordnum); // Replacing 0xFF and 0xFE with new corresponding indexes Order.Replace(0xFE, Order.GetIgnoreIndex()); Order.Replace(0xFF, Order.GetInvalidPatIndex()); } } // Reading instrument, sample and pattern offsets std::vector<uint32> insPos, smpPos, patPos; file.ReadVectorLE(insPos, fileHeader.insnum); file.ReadVectorLE(smpPos, fileHeader.smpnum); file.ReadVectorLE(patPos, fileHeader.patnum); // Find the first parapointer. // This is used for finding out whether the edit history is actually stored in the file or not, // as some early versions of Schism Tracker set the history flag, but didn't save anything. // We will consider the history invalid if it ends after the first parapointer. uint32 minPtr = Util::MaxValueOfType(minPtr); for(uint16 n = 0; n < fileHeader.insnum; n++) { if(insPos[n] > 0) { minPtr = std::min(minPtr, insPos[n]); } } for(uint16 n = 0; n < fileHeader.smpnum; n++) { if(smpPos[n] > 0) { minPtr = std::min(minPtr, smpPos[n]); } } for(uint16 n = 0; n < fileHeader.patnum; n++) { if(patPos[n] > 0) { minPtr = std::min(minPtr, patPos[n]); } } if(fileHeader.special & ITFileHeader::embedSongMessage) { minPtr = std::min(minPtr, fileHeader.msgoffset); } // Reading IT Edit History Info // This is only supposed to be present if bit 1 of the special flags is set. // However, old versions of Schism and probably other trackers always set this bit // even if they don't write the edit history count. So we have to filter this out... // This is done by looking at the parapointers. If the history data end after // the first parapointer, we assume that it's actually no history data. if(fileHeader.special & ITFileHeader::embedEditHistory) { const uint16 nflt = file.ReadUint16LE(); if(file.CanRead(nflt * sizeof(ITHistoryStruct)) && file.GetPosition() + nflt * sizeof(ITHistoryStruct) <= minPtr) { m_FileHistory.reserve(nflt); for(size_t n = 0; n < nflt; n++) { FileHistory mptHistory; ITHistoryStruct itHistory; file.ReadConvertEndianness(itHistory); itHistory.ConvertToMPT(mptHistory); m_FileHistory.push_back(mptHistory); } } else { // Oops, we were not supposed to read this. file.SkipBack(2); } } else if(fileHeader.highlight_major == 0 && fileHeader.highlight_minor == 0 && fileHeader.cmwt == 0x0214 && fileHeader.cwtv == 0x0214 && !memcmp(fileHeader.reserved, "\0\0\0\0", 4) && (fileHeader.special & (ITFileHeader::embedEditHistory | ITFileHeader::embedPatternHighlights)) == 0) { // Another non-conforming application is unmo3 < v2.4.0.1, which doesn't set the special bit // at all, but still writes the two edit history length bytes (zeroes)... if(file.ReadUint16LE() != 0) { // These were not zero bytes -> We're in the wrong place! file.SkipBack(2); madeWithTracker = "UNMO3"; } } // Reading MIDI Output & Macros if(m_SongFlags[SONG_EMBEDMIDICFG] && file.Read(m_MidiCfg)) { m_MidiCfg.Sanitize(); } // Ignore MIDI data. Fixes some files like denonde.it that were made with old versions of Impulse Tracker (which didn't support Zxx filters) and have Zxx effects in the patterns. if(fileHeader.cwtv < 0x0214) { MemsetZero(m_MidiCfg.szMidiSFXExt); MemsetZero(m_MidiCfg.szMidiZXXExt); m_SongFlags.set(SONG_EMBEDMIDICFG); } if(file.ReadMagic("MODU")) { madeWithTracker = "BeRoTracker"; } // Read pattern names: "PNAM" FileReader patNames; if(file.ReadMagic("PNAM")) { patNames = file.GetChunk(file.ReadUint32LE()); } m_nChannels = GetModSpecifications().channelsMin; // Read channel names: "CNAM" if(file.ReadMagic("CNAM")) { FileReader chnNames = file.GetChunk(file.ReadUint32LE()); const CHANNELINDEX readChns = std::min(MAX_BASECHANNELS, static_cast<CHANNELINDEX>(chnNames.GetLength() / MAX_CHANNELNAME)); m_nChannels = readChns; for(CHANNELINDEX i = 0; i < readChns; i++) { chnNames.ReadString<mpt::String::maybeNullTerminated>(ChnSettings[i].szName, MAX_CHANNELNAME); } } // Read mix plugins information if(file.CanRead(9)) { LoadMixPlugins(file); } // Read Song Message if(fileHeader.special & ITFileHeader::embedSongMessage) { if(fileHeader.msglength > 0 && file.Seek(fileHeader.msgoffset)) { // Generally, IT files should use CR for line endings. However, ChibiTracker uses LF. One could do... // if(itHeader.cwtv == 0x0214 && itHeader.cmwt == 0x0214 && itHeader.reserved == ITFileHeader::chibiMagic) --> Chibi detected. // But we'll just use autodetection here: songMessage.Read(file, fileHeader.msglength, SongMessage::leAutodetect); } } // Reading Instruments m_nInstruments = 0; if(fileHeader.flags & ITFileHeader::instrumentMode) { m_nInstruments = std::min(fileHeader.insnum, INSTRUMENTINDEX(MAX_INSTRUMENTS - 1)); } for(INSTRUMENTINDEX i = 0; i < GetNumInstruments(); i++) { if(insPos[i] > 0 && file.Seek(insPos[i]) && file.CanRead(fileHeader.cmwt < 0x200 ? sizeof(ITOldInstrument) : sizeof(ITInstrument))) { ModInstrument *instrument = AllocateInstrument(i + 1); if(instrument != nullptr) { ITInstrToMPT(file, *instrument, fileHeader.cmwt); // MIDI Pitch Wheel Depth is a global setting in IT. Apply it to all instruments. instrument->midiPWD = fileHeader.pwd; } } } // In order to properly compute the position, in file, of eventual extended settings // such as "attack" we need to keep the "real" size of the last sample as those extra // setting will follow this sample in the file FileReader::off_t lastSampleOffset = 0; if(fileHeader.smpnum > 0) { lastSampleOffset = smpPos[fileHeader.smpnum - 1] + sizeof(ITSample); } //// #ITQ // Reading Samples m_nSamples = std::min(fileHeader.smpnum, SAMPLEINDEX(MAX_SAMPLES - 1)); size_t nbytes = 0; // size of sample data in file for(SAMPLEINDEX i = 0; i < GetNumSamples(); i++) { ITQSample sampleHeader; if(smpPos[i] > 0 && file.Seek(smpPos[i]) && file.ReadConvertEndianness(sampleHeader)) { if(!memcmp(sampleHeader.id, "ITQS", 4)) { size_t sampleOffset = sampleHeader.ConvertToMPT(Samples[i + 1]); mpt::String::Read<mpt::String::spacePadded>(m_szNames[i + 1], sampleHeader.name); if((loadFlags & loadSampleData) && file.Seek(sampleOffset)) { Samples[i+1].originalSize = sampleHeader.nbytes; sampleHeader.GetSampleFormatITQ(fileHeader.cwtv).ReadSample(Samples[i + 1], file); lastSampleOffset = std::max(lastSampleOffset, file.GetPosition()); } } } } m_nSamples = std::max(SAMPLEINDEX(1), GetNumSamples()); m_nMinPeriod = 8; m_nMaxPeriod = 0xF000; PATTERNINDEX numPats = std::min(static_cast<PATTERNINDEX>(patPos.size()), GetModSpecifications().patternsMax); if(numPats != patPos.size()) { // Hack: Notify user here if file contains more patterns than what can be read. AddToLog(mpt::String::Print(str_PatternSetTruncationNote, patPos.size(), numPats)); } if(!(loadFlags & loadPatternData)) { numPats = 0; } // Checking for number of used channels, which is not explicitely specified in the file. for(PATTERNINDEX pat = 0; pat < numPats; pat++) { if(patPos[pat] == 0 || !file.Seek(patPos[pat])) continue; uint16 len = file.ReadUint16LE(); ROWINDEX numRows = file.ReadUint16LE(); if(numRows < GetModSpecifications().patternRowsMin || numRows > GetModSpecifications().patternRowsMax || !file.Skip(4)) continue; FileReader patternData = file.GetChunk(len); ROWINDEX row = 0; std::vector<uint8> chnMask(GetNumChannels()); while(row < numRows && patternData.AreBytesLeft()) { uint8 b = patternData.ReadUint8(); if(!b) { row++; continue; } CHANNELINDEX ch = (b & IT_bitmask_patternChanField_c); // 0x7f We have some data grab a byte keeping only 7 bits if(ch) { ch = (ch - 1);// & IT_bitmask_patternChanMask_c; // 0x3f mask of the byte again, keeping only 6 bits } if(ch >= chnMask.size()) { chnMask.resize(ch + 1, 0); } if(b & IT_bitmask_patternChanEnabled_c) // 0x80 check if the upper bit is enabled. { chnMask[ch] = patternData.ReadUint8(); // set the channel mask for this channel. } // Channel used if(chnMask[ch] & 0x0F) // if this channel is used set m_nChannels { if(ch >= GetNumChannels() && ch < MAX_BASECHANNELS) { m_nChannels = ch + 1; } } // Now we actually update the pattern-row entry the note,instrument etc. // Note if(chnMask[ch] & 1) patternData.Skip(1); // Instrument if(chnMask[ch] & 2) patternData.Skip(1); // Volume if(chnMask[ch] & 4) patternData.Skip(1); // Effect if(chnMask[ch] & 8) patternData.Skip(2); } } // Compute extra instruments settings position if(lastSampleOffset > 0) { file.Seek(lastSampleOffset); } // Load instrument and song extensions. LoadExtendedInstrumentProperties(file, &interpretModPlugMade); if(interpretModPlugMade) { m_nMixLevels = mixLevels_original; } // We need to do this here, because if there no samples (so lastSampleOffset = 0), we need to look after the last pattern (sample data normally follows pattern data). // And we need to do this before reading the patterns because m_nChannels might be modified by LoadExtendedSongProperties. *sigh* LoadExtendedSongProperties(GetType(), file, &interpretModPlugMade); m_nTempoMode = tempo_mode_modern; // Reading Patterns Patterns.ResizeArray(std::max(MAX_PATTERNS, numPats)); for(PATTERNINDEX pat = 0; pat < numPats; pat++) { if(patPos[pat] == 0 || !file.Seek(patPos[pat])) { // Empty 64-row pattern if(Patterns.Insert(pat, 64)) { AddToLog(mpt::String::Print("Allocating patterns failed starting from pattern %1", pat)); break; } // Now (after the Insert() call), we can read the pattern name. CopyPatternName(Patterns[pat], patNames); continue; } uint16 len = file.ReadUint16LE(); ROWINDEX numRows = file.ReadUint16LE(); if(numRows < GetModSpecifications().patternRowsMin || numRows > GetModSpecifications().patternRowsMax || !file.Skip(4) || Patterns.Insert(pat, numRows)) continue; FileReader patternData = file.GetChunk(len); // Now (after the Insert() call), we can read the pattern name. CopyPatternName(Patterns[pat], patNames); std::vector<uint8> chnMask(GetNumChannels()); std::vector<ModCommand> lastValue(GetNumChannels(), ModCommand::Empty()); ModCommand *m = Patterns[pat]; ROWINDEX row = 0; while(row < numRows && patternData.AreBytesLeft()) { uint8 b = patternData.ReadUint8(); if(!b) { row++; m += GetNumChannels(); continue; } CHANNELINDEX ch = b & IT_bitmask_patternChanField_c; // 0x7f if(ch) { ch = (ch - 1); //& IT_bitmask_patternChanMask_c; // 0x3f } if(ch >= chnMask.size()) { chnMask.resize(ch + 1, 0); lastValue.resize(ch + 1, ModCommand::Empty()); ASSERT(chnMask.size() <= GetNumChannels()); } if(b & IT_bitmask_patternChanEnabled_c) // 0x80 { chnMask[ch] = patternData.ReadUint8(); } // Now we grab the data for this particular row/channel. if((chnMask[ch] & 0x10) && (ch < m_nChannels)) { m[ch].note = lastValue[ch].note; } if((chnMask[ch] & 0x20) && (ch < m_nChannels)) { m[ch].instr = lastValue[ch].instr; } if((chnMask[ch] & 0x40) && (ch < m_nChannels)) { m[ch].volcmd = lastValue[ch].volcmd; m[ch].vol = lastValue[ch].vol; } if((chnMask[ch] & 0x80) && (ch < m_nChannels)) { m[ch].command = lastValue[ch].command; m[ch].param = lastValue[ch].param; } if(chnMask[ch] & 1) // Note { uint8 note = patternData.ReadUint8(); if(ch < m_nChannels) { if(note < 0x80) note++; if(!(GetType() & MOD_TYPE_MPT)) { if(note > NOTE_MAX && note < 0xFD) note = NOTE_FADE; else if(note == 0xFD) note = NOTE_NONE; } m[ch].note = note; lastValue[ch].note = note; } } if(chnMask[ch] & 2) { uint8 instr = patternData.ReadUint8(); if(ch < m_nChannels) { m[ch].instr = instr; lastValue[ch].instr = instr; } } if(chnMask[ch] & 4) { uint8 vol = patternData.ReadUint8(); if(ch < m_nChannels) { // 0-64: Set Volume if(vol <= 64) { m[ch].volcmd = VOLCMD_VOLUME; m[ch].vol = vol; } else // 128-192: Set Panning if(vol >= 128 && vol <= 192) { m[ch].volcmd = VOLCMD_PANNING; m[ch].vol = vol - 128; } else // 65-74: Fine Volume Up if(vol < 75) { m[ch].volcmd = VOLCMD_FINEVOLUP; m[ch].vol = vol - 65; } else // 75-84: Fine Volume Down if(vol < 85) { m[ch].volcmd = VOLCMD_FINEVOLDOWN; m[ch].vol = vol - 75; } else // 85-94: Volume Slide Up if(vol < 95) { m[ch].volcmd = VOLCMD_VOLSLIDEUP; m[ch].vol = vol - 85; } else // 95-104: Volume Slide Down if(vol < 105) { m[ch].volcmd = VOLCMD_VOLSLIDEDOWN; m[ch].vol = vol - 95; } else // 105-114: Pitch Slide Up if(vol < 115) { m[ch].volcmd = VOLCMD_PORTADOWN; m[ch].vol = vol - 105; } else // 115-124: Pitch Slide Down if(vol < 125) { m[ch].volcmd = VOLCMD_PORTAUP; m[ch].vol = vol - 115; } else // 193-202: Portamento To if(vol >= 193 && vol <= 202) { m[ch].volcmd = VOLCMD_TONEPORTAMENTO; m[ch].vol = vol - 193; } else // 203-212: Vibrato depth if(vol >= 203 && vol <= 212) { m[ch].volcmd = VOLCMD_VIBRATODEPTH; m[ch].vol = vol - 203; // Old versions of ModPlug saved this as vibrato speed instead, so let's fix that. if(m[ch].vol && m_dwLastSavedWithVersion && m_dwLastSavedWithVersion <= MAKE_VERSION_NUMERIC(1, 17, 02, 54)) m[ch].volcmd = VOLCMD_VIBRATOSPEED; } else // 213-222: Unused (was velocity) // 223-232: Offset if(vol >= 223 && vol <= 232) { m[ch].volcmd = VOLCMD_OFFSET; m[ch].vol = vol - 223; } lastValue[ch].volcmd = m[ch].volcmd; lastValue[ch].vol = m[ch].vol; } } // Reading command/param if(chnMask[ch] & 8) { uint8 cmd = patternData.ReadUint8(); uint8 param = patternData.ReadUint8(); if(ch < m_nChannels) { if(cmd) { m[ch].command = cmd; m[ch].param = param; S3MConvert(m[ch], true); lastValue[ch].command = m[ch].command; lastValue[ch].param = m[ch].param; } } } } } UpgradeModFlags(); if(!m_dwLastSavedWithVersion && fileHeader.cwtv == 0x0888) { // There are some files with OpenMPT extensions, but the "last saved with" field contains 0. // Was there an OpenMPT version that wrote 0 there, or are they hacked? m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 17, 00, 00); } if(m_dwLastSavedWithVersion && madeWithTracker.empty()) { madeWithTracker = "OpenMPT " + MptVersion::ToStr(m_dwLastSavedWithVersion); if(memcmp(fileHeader.reserved, "OMPT", 4) && (fileHeader.cwtv & 0xF000) == 0x5000) { madeWithTracker += " (compatibility export)"; } else if(MptVersion::IsTestBuild(m_dwLastSavedWithVersion)) { madeWithTracker += " (test build)"; } } else { switch(fileHeader.cwtv >> 12) { case 0: if(!madeWithTracker.empty()) { // BeRoTracker has been detected above. } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0200 && fileHeader.flags == 9 && fileHeader.special == 0 && fileHeader.highlight_major == 0 && fileHeader.highlight_minor == 0 && fileHeader.insnum == 0 && fileHeader.patnum + 1 == fileHeader.ordnum && fileHeader.globalvol == 128 && fileHeader.mv == 100 && fileHeader.speed == 1 && fileHeader.sep == 128 && fileHeader.pwd == 0 && fileHeader.msglength == 0 && fileHeader.msgoffset == 0 && !memcmp(fileHeader.reserved, "\0\0\0\0", 4)) { madeWithTracker = "OpenSPC conversion"; } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0200 && !memcmp(fileHeader.reserved, "\0\0\0\0", 4)) { // ModPlug Tracker 1.00a5, instruments 560 bytes apart m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 00, 00, A5); madeWithTracker = "ModPlug tracker 1.00a5"; interpretModPlugMade = true; } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0214 && !memcmp(fileHeader.reserved, "CHBI", 4)) { madeWithTracker = "ChibiTracker"; } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0214 && !(fileHeader.special & 3) && !memcmp(fileHeader.reserved, "\0\0\0\0", 4) && !strcmp(Samples[1].filename, "XXXXXXXX.YYY")) { madeWithTracker = "CheeseTracker"; } else { if(fileHeader.cmwt > 0x0214) { madeWithTracker = "Impulse Tracker 2.15"; } else if(fileHeader.cwtv > 0x0214) { // Patched update of IT 2.14 (0x0215 - 0x0217 == p1 - p3) // p4 (as found on modland) adds the ITVSOUND driver, but doesn't seem to change // anything as far as file saving is concerned. madeWithTracker = mpt::String::Print("Impulse Tracker 2.14p%1", fileHeader.cwtv - 0x0214); } else { madeWithTracker = mpt::String::Print("Impulse Tracker %1.%2", (fileHeader.cwtv & 0x0F00) >> 8, mpt::fmt::hex0<2>((fileHeader.cwtv & 0xFF))); } } break; case 1: madeWithTracker = GetSchismTrackerVersion(fileHeader.cwtv); break; case 6: madeWithTracker = "BeRoTracker"; break; case 7: madeWithTracker = mpt::String::Print("ITMCK %1.%2.%3", (fileHeader.cwtv >> 8) & 0x0F, (fileHeader.cwtv >> 4) & 0x0F, fileHeader.cwtv & 0x0F); break; } } if(GetType() == MOD_TYPE_IT) { // Set appropriate mod flags if the file was not made with MPT. if(!interpretModPlugMade) { SetModFlag(MSF_MIDICC_BUGEMULATION, false); SetModFlag(MSF_OLDVOLSWING, false); SetModFlag(MSF_COMPATIBLE_PLAY, true); } } else { //START - mpt specific: //Using member cwtv on pifh as the version number. const uint16 version = fileHeader.cwtv; if(version > 0x889 && file.Seek(mptStartPos)) { std::istringstream iStrm(std::string(file.GetRawData(), file.BytesLeft())); if(version >= 0x88D) { srlztn::SsbRead ssb(iStrm); ssb.BeginRead("mptm", MptVersion::num); ssb.ReadItem(GetTuneSpecificTunings(), "0", 1, &ReadTuningCollection); ssb.ReadItem(*this, "1", 1, &ReadTuningMap); ssb.ReadItem(Order, "2", 1, &ReadModSequenceOld); ssb.ReadItem(Patterns, FileIdPatterns, strlen(FileIdPatterns), &ReadModPatterns); ssb.ReadItem(Order, FileIdSequences, strlen(FileIdSequences), &ReadModSequences); if(ssb.m_Status & srlztn::SNT_FAILURE) { AddToLog("Unknown error occured while deserializing file."); } } else //Loading for older files. { if(GetTuneSpecificTunings().Deserialize(iStrm)) { AddToLog("Error occured - loading failed while trying to load tune specific tunings."); } else { ReadTuningMap(iStrm, *this); } } } //version condition(MPT) } return true; }
// for draw calls, we initialize the active hot tiles and perform deferred // load on them if tile is in invalid state. we do this in the outer thread loop instead of inside // the draw routine itself mainly for performance, to avoid unnecessary setup // every triangle // @todo support deferred clear INLINE void InitializeHotTiles(SWR_CONTEXT* pContext, DRAW_CONTEXT* pDC, uint32_t macroID, const TRIANGLE_WORK_DESC* pWork) { const API_STATE& state = GetApiState(pDC); HotTileMgr *pHotTileMgr = pContext->pHotTileMgr; const SWR_PS_STATE& psState = state.psState; uint32_t numRTs = psState.maxRTSlotUsed + 1; uint32_t x, y; MacroTileMgr::getTileIndices(macroID, x, y); x *= KNOB_MACROTILE_X_DIM; y *= KNOB_MACROTILE_Y_DIM; uint32_t numSamples = GetNumSamples(state.rastState.sampleCount); // check RT if enabled if (state.psState.pfnPixelShader != nullptr) { for (uint32_t rt = 0; rt < numRTs; ++rt) { HOTTILE* pHotTile = pHotTileMgr->GetHotTile(pContext, pDC, macroID, (SWR_RENDERTARGET_ATTACHMENT)(SWR_ATTACHMENT_COLOR0 + rt), true, numSamples); if (pHotTile->state == HOTTILE_INVALID) { RDTSC_START(BELoadTiles); // invalid hottile before draw requires a load from surface before we can draw to it pContext->pfnLoadTile(GetPrivateState(pDC), KNOB_COLOR_HOT_TILE_FORMAT, (SWR_RENDERTARGET_ATTACHMENT)(SWR_ATTACHMENT_COLOR0 + rt), x, y, pHotTile->renderTargetArrayIndex, pHotTile->pBuffer); pHotTile->state = HOTTILE_DIRTY; RDTSC_STOP(BELoadTiles, 0, 0); } else if (pHotTile->state == HOTTILE_CLEAR) { RDTSC_START(BELoadTiles); // Clear the tile. ClearColorHotTile(pHotTile); pHotTile->state = HOTTILE_DIRTY; RDTSC_STOP(BELoadTiles, 0, 0); } } } // check depth if enabled if (state.depthStencilState.depthTestEnable || state.depthStencilState.depthWriteEnable) { HOTTILE* pHotTile = pHotTileMgr->GetHotTile(pContext, pDC, macroID, SWR_ATTACHMENT_DEPTH, true, numSamples); if (pHotTile->state == HOTTILE_INVALID) { RDTSC_START(BELoadTiles); // invalid hottile before draw requires a load from surface before we can draw to it pContext->pfnLoadTile(GetPrivateState(pDC), KNOB_DEPTH_HOT_TILE_FORMAT, SWR_ATTACHMENT_DEPTH, x, y, pHotTile->renderTargetArrayIndex, pHotTile->pBuffer); pHotTile->state = HOTTILE_DIRTY; RDTSC_STOP(BELoadTiles, 0, 0); } else if (pHotTile->state == HOTTILE_CLEAR) { RDTSC_START(BELoadTiles); // Clear the tile. ClearDepthHotTile(pHotTile); pHotTile->state = HOTTILE_DIRTY; RDTSC_STOP(BELoadTiles, 0, 0); } } // check stencil if enabled if (state.depthStencilState.stencilTestEnable || state.depthStencilState.stencilWriteEnable) { HOTTILE* pHotTile = pHotTileMgr->GetHotTile(pContext, pDC, macroID, SWR_ATTACHMENT_STENCIL, true, numSamples); if (pHotTile->state == HOTTILE_INVALID) { RDTSC_START(BELoadTiles); // invalid hottile before draw requires a load from surface before we can draw to it pContext->pfnLoadTile(GetPrivateState(pDC), KNOB_STENCIL_HOT_TILE_FORMAT, SWR_ATTACHMENT_STENCIL, x, y, pHotTile->renderTargetArrayIndex, pHotTile->pBuffer); pHotTile->state = HOTTILE_DIRTY; RDTSC_STOP(BELoadTiles, 0, 0); } else if (pHotTile->state == HOTTILE_CLEAR) { RDTSC_START(BELoadTiles); // Clear the tile. ClearStencilHotTile(pHotTile); pHotTile->state = HOTTILE_DIRTY; RDTSC_STOP(BELoadTiles, 0, 0); } } }