From: <sag...@us...> - 2011-11-18 22:30:55
|
Revision: 1137 http://modplug.svn.sourceforge.net/modplug/?rev=1137&view=rev Author: saga-games Date: 2011-11-18 22:30:48 +0000 (Fri, 18 Nov 2011) Log Message: ----------- [Fix] Loading instruments from other modules is no longer limited to 32 samples [Imp] Added more consistency checks / conversions when loading samples / instruments from files. Modified Paths: -------------- trunk/OpenMPT/mptrack/ModConvert.cpp trunk/OpenMPT/soundlib/Sampleio.cpp trunk/OpenMPT/soundlib/Sndfile.h Modified: trunk/OpenMPT/mptrack/ModConvert.cpp =================================================================== --- trunk/OpenMPT/mptrack/ModConvert.cpp 2011-11-18 22:21:47 UTC (rev 1136) +++ trunk/OpenMPT/mptrack/ModConvert.cpp 2011-11-18 22:30:48 UTC (rev 1137) @@ -97,18 +97,17 @@ if(nNewType == nOldType) return true; - const bool oldTypeIsMOD = (nOldType == MOD_TYPE_MOD), oldTypeIsXM = (nOldType == MOD_TYPE_XM), - oldTypeIsS3M = (nOldType == MOD_TYPE_S3M), oldTypeIsIT = (nOldType == MOD_TYPE_IT), - oldTypeIsMPT = (nOldType == MOD_TYPE_MPT), oldTypeIsMOD_XM = (oldTypeIsMOD || oldTypeIsXM), - oldTypeIsS3M_IT_MPT = (oldTypeIsS3M || oldTypeIsIT || oldTypeIsMPT), - oldTypeIsIT_MPT = (oldTypeIsIT || oldTypeIsMPT); + const bool oldTypeIsXM = (nOldType == MOD_TYPE_XM), + oldTypeIsS3M = (nOldType == MOD_TYPE_S3M), oldTypeIsIT = (nOldType == MOD_TYPE_IT), + oldTypeIsMPT = (nOldType == MOD_TYPE_MPT), + oldTypeIsS3M_IT_MPT = (oldTypeIsS3M || oldTypeIsIT || oldTypeIsMPT), + oldTypeIsIT_MPT = (oldTypeIsIT || oldTypeIsMPT); const bool newTypeIsMOD = (nNewType == MOD_TYPE_MOD), newTypeIsXM = (nNewType == MOD_TYPE_XM), - newTypeIsS3M = (nNewType == MOD_TYPE_S3M), newTypeIsIT = (nNewType == MOD_TYPE_IT), - newTypeIsMPT = (nNewType == MOD_TYPE_MPT), newTypeIsMOD_XM = (newTypeIsMOD || newTypeIsXM), - newTypeIsS3M_IT_MPT = (newTypeIsS3M || newTypeIsIT || newTypeIsMPT), - newTypeIsXM_IT_MPT = (newTypeIsXM || newTypeIsIT || newTypeIsMPT), - newTypeIsIT_MPT = (newTypeIsIT || newTypeIsMPT); + newTypeIsS3M = (nNewType == MOD_TYPE_S3M), newTypeIsIT = (nNewType == MOD_TYPE_IT), + newTypeIsMPT = (nNewType == MOD_TYPE_MPT), newTypeIsMOD_XM = (newTypeIsMOD || newTypeIsXM), + newTypeIsXM_IT_MPT = (newTypeIsXM || newTypeIsIT || newTypeIsMPT), + newTypeIsIT_MPT = (newTypeIsIT || newTypeIsMPT); const CModSpecifications& specs = m_SndFile.GetModSpecifications(nNewType); @@ -271,14 +270,12 @@ // Bidi loops if((sample.uFlags & CHN_PINGPONGLOOP) != 0) { - sample.uFlags &= ~CHN_PINGPONGLOOP; CHANGEMODTYPE_WARNING(wSampleBidiLoops); } // Autovibrato if(sample.nVibDepth || sample.nVibRate || sample.nVibSweep) { - sample.nVibDepth = sample.nVibRate = sample.nVibSweep = sample.nVibType = 0; CHANGEMODTYPE_WARNING(wSampleAutoVibrato); } } @@ -289,71 +286,17 @@ // Sustain loops - convert to normal loops if((sample.uFlags & CHN_SUSTAINLOOP) != 0) { - // We probably overwrite a normal loop here, but since sustain loops are evaluated before normal loops, this is just correct. - sample.nLoopStart = sample.nSustainStart; - sample.nLoopEnd = sample.nSustainEnd; - sample.uFlags |= CHN_LOOP; - if(sample.uFlags & CHN_PINGPONGSUSTAIN) - { - sample.uFlags |= CHN_PINGPONGLOOP; - } else - { - sample.uFlags &= ~CHN_PINGPONGLOOP; - } CHANGEMODTYPE_WARNING(wSampleSustainLoops); } - sample.nSustainStart = sample.nSustainEnd = 0; - sample.uFlags &= ~(CHN_SUSTAINLOOP|CHN_PINGPONGSUSTAIN); } - // Transpose to Frequency (MOD/XM to S3M/IT/MPT) - if(oldTypeIsMOD_XM && newTypeIsS3M_IT_MPT) + // TODO: Pattern notes could be transposed based on the previous relative tone? + if(newTypeIsMOD && sample.RelativeTone != 0) { - sample.nC5Speed = CSoundFile::TransposeToFrequency(sample.RelativeTone, sample.nFineTune); - sample.RelativeTone = 0; - sample.nFineTune = 0; + CHANGEMODTYPE_WARNING(wMODSampleFrequency); } - // Frequency to Transpose (S3M/IT/MPT to MOD/XM) - if(oldTypeIsS3M_IT_MPT && newTypeIsMOD_XM) - { - CSoundFile::FrequencyToTranspose(&sample); - // No relative note for MOD files - // TODO: Pattern notes could be transposed based on the previous relative tone? - if(newTypeIsMOD && sample.RelativeTone != 0) - { - sample.RelativeTone = 0; - CHANGEMODTYPE_WARNING(wMODSampleFrequency); - } - } - - // All XM samples have default panning, and XM's autovibrato settings are rather limited. - if(newTypeIsXM) - { - if(!(sample.uFlags & CHN_PANNING)) - { - sample.uFlags |= CHN_PANNING; - sample.nPan = 128; - } - - LimitMax(sample.nVibDepth, BYTE(15)); - LimitMax(sample.nVibRate, BYTE(63)); - } - - // S3M / MOD samples don't have panning. - if(newTypeIsMOD || newTypeIsS3M) - { - sample.uFlags &= ~CHN_PANNING; - } - - if((oldTypeIsXM && newTypeIsIT_MPT) || (oldTypeIsIT_MPT && newTypeIsXM)) - { - // Autovibrato sweep setting is inverse in XM (0 = "no sweep") and IT (0 = "no vibrato") - if(sample.nVibRate != 0 && sample.nVibDepth != 0) - { - sample.nVibSweep = 255 - sample.nVibSweep; - } - } + m_SndFile.ConvertSample(nSmp, nOldType, nNewType); } for(INSTRUMENTINDEX nIns = 1; nIns <= m_SndFile.GetNumInstruments(); nIns++) @@ -367,9 +310,9 @@ // Convert IT/MPT to XM (fix instruments) if(oldTypeIsIT_MPT && newTypeIsXM) { - for (UINT k = 0; k < NOTE_MAX; k++) + for (size_t i = 0; i < CountOf(pIns->NoteMap); i++) { - if ((pIns->NoteMap[k]) && (pIns->NoteMap[k] != (BYTE)(k+1))) + if ((pIns->NoteMap[i]) && (pIns->NoteMap[i] != (BYTE)(i + 1))) { CHANGEMODTYPE_WARNING(wBrokenNoteMap); break; @@ -379,52 +322,29 @@ if(pIns->VolEnv.nSustainStart != pIns->VolEnv.nSustainEnd) { CHANGEMODTYPE_WARNING(wInstrumentSustainLoops); - pIns->VolEnv.nSustainEnd = pIns->VolEnv.nSustainStart; } if(pIns->PanEnv.nSustainStart != pIns->PanEnv.nSustainEnd) { CHANGEMODTYPE_WARNING(wInstrumentSustainLoops); - pIns->PanEnv.nSustainEnd = pIns->PanEnv.nSustainStart; } - pIns->VolEnv.dwFlags &= ~ENV_CARRY; - pIns->PanEnv.dwFlags &= ~ENV_CARRY; - pIns->PitchEnv.dwFlags &= ~(ENV_CARRY|ENV_ENABLED|ENV_FILTER); - pIns->dwFlags &= ~INS_SETPANNING; - pIns->SetCutoff(pIns->GetCutoff(), false); - pIns->SetResonance(pIns->GetResonance(), false); - pIns->nFilterMode = FLTMODE_UNCHANGED; - - pIns->nCutSwing = pIns->nPanSwing = pIns->nResSwing = pIns->nVolSwing = 0; - - pIns->nPPC = NOTE_MIDDLEC - 1; - pIns->nPPS = 0; - - pIns->nGlobalVol = 64; - pIns->nPan = 128; } - // Convert XM to IT/MPTM - fix fadeout length - if(oldTypeIsXM && newTypeIsIT_MPT) - { - LimitMax(pIns->nFadeOut, 8192u); - } - // Convert MPT to anything - remove instrument tunings, Pitch/Tempo Lock if(oldTypeIsMPT) { if(pIns->pTuning != nullptr) { - pIns->SetTuning(nullptr); CHANGEMODTYPE_WARNING(wInstrumentTuning); } if(pIns->wPitchToTempoLock != 0) { - pIns->wPitchToTempoLock = 0; CHANGEMODTYPE_WARNING(wPitchToTempoLock); } } + + m_SndFile.ConvertInstrument(nIns, nOldType, nNewType); } if(newTypeIsMOD) @@ -566,7 +486,7 @@ CHANGEMODTYPE_CHECK(wInstrumentSustainLoops, "Sustain loops were converted to sustain points.\n"); CHANGEMODTYPE_CHECK(wInstrumentTuning, "Instrument tunings will be lost.\n"); CHANGEMODTYPE_CHECK(wPitchToTempoLock, "Pitch / Tempo Lock instrument property is not supported by the new format.\n"); - CHANGEMODTYPE_CHECK(wBrokenNoteMap, "Note Mapping will be lost when saving as XM.\n"); + CHANGEMODTYPE_CHECK(wBrokenNoteMap, "Instrument Note Mapping is not supported by the new format.\n"); CHANGEMODTYPE_CHECK(wReleaseNode, "Instrument envelope release nodes are not supported by the new format.\n"); // General warnings Modified: trunk/OpenMPT/soundlib/Sampleio.cpp =================================================================== --- trunk/OpenMPT/soundlib/Sampleio.cpp 2011-11-18 22:21:47 UTC (rev 1136) +++ trunk/OpenMPT/soundlib/Sampleio.cpp 2011-11-18 22:30:48 UTC (rev 1137) @@ -61,7 +61,7 @@ || (psig[76/4] == LittleEndian(0x53524353)) // S3I signature || ((psig[0] == LittleEndian(0x4D524F46)) && (psig[2] == LittleEndian(0x46464941))) // AIFF signature || ((psig[0] == LittleEndian(0x4D524F46)) && (psig[2] == LittleEndian(0x58565338))) // 8SVX signature - || (psig[0] == LittleEndian(LittleEndian(IT_IMPS))) // ITS signature + || (psig[0] == LittleEndian(IT_IMPS)) // ITS signature ) { // Loading Instrument @@ -91,8 +91,6 @@ DestroyInstrument(nInstr, deleteAssociatedSamples); Instruments[nInstr] = pIns; - // Default values - pIns->nFadeOut = 1024; if (nSample) ReadSampleFromFile(nSample, lpMemFile, dwFileLength); return true; } @@ -188,12 +186,15 @@ // I/O From another song // -bool CSoundFile::ReadInstrumentFromSong(INSTRUMENTINDEX nInstr, CSoundFile *pSrcSong, UINT nSrcInstr) -//--------------------------------------------------------------------------------------------------- +bool CSoundFile::ReadInstrumentFromSong(INSTRUMENTINDEX targetInstr, const CSoundFile *pSrcSong, INSTRUMENTINDEX sourceInstr) +//--------------------------------------------------------------------------------------------------------------------------- { - if ((!pSrcSong) || (!nSrcInstr) || (nSrcInstr > pSrcSong->m_nInstruments) - || (nInstr >= MAX_INSTRUMENTS) || (!pSrcSong->Instruments[nSrcInstr])) return false; - if (m_nInstruments < nInstr) m_nInstruments = nInstr; + if ((!pSrcSong) || (!sourceInstr) || (sourceInstr > pSrcSong->GetNumInstruments()) + || (targetInstr >= MAX_INSTRUMENTS) || (!pSrcSong->Instruments[sourceInstr])) + { + return false; + } + if (m_nInstruments < targetInstr) m_nInstruments = targetInstr; MODINSTRUMENT *pIns; @@ -205,95 +206,88 @@ return false; } - DestroyInstrument(nInstr, deleteAssociatedSamples); + DestroyInstrument(targetInstr, deleteAssociatedSamples); - Instruments[nInstr] = pIns; + Instruments[targetInstr] = pIns; + *pIns = *pSrcSong->Instruments[sourceInstr]; - // TODO we want to copy ALL samples, not just 32! Use vectors here. - WORD samplemap[32]; - WORD samplesrc[32]; - UINT nSamples = 0; - UINT nsmp = 1; - *pIns = *pSrcSong->Instruments[nSrcInstr]; - for (UINT i=0; i<128; i++) + vector<SAMPLEINDEX> sourceSample; // Sample index in source song + vector<SAMPLEINDEX> targetSample; // Sample index in target song + SAMPLEINDEX targetIndex = 1; // Next index for inserting sample + + for(size_t i = 0; i < CountOf(pIns->Keyboard); i++) { - UINT n = pIns->Keyboard[i]; - if ((n) && (n <= pSrcSong->m_nSamples) && (i < NOTE_MAX)) + const SAMPLEINDEX sourceIndex = pIns->Keyboard[i]; + if(sourceIndex > 0 && sourceIndex <= pSrcSong->GetNumSamples()) { - UINT j = 0; - for (j=0; j<nSamples; j++) + const vector<SAMPLEINDEX>::const_iterator entry = std::find(sourceSample.begin(), sourceSample.end(), sourceIndex); + if(entry == sourceSample.end()) { - if (samplesrc[j] == n) break; - } - if (j >= nSamples) - { - while ((nsmp < MAX_SAMPLES) && ((Samples[nsmp].pSample) || (m_szNames[nsmp][0]))) nsmp++; - if ((nSamples < 32) && (nsmp < MAX_SAMPLES)) + // Didn't consider this sample yet, so add it to our map. + while((targetIndex < MAX_SAMPLES) && ((Samples[targetIndex].pSample) || (m_szNames[targetIndex][0]))) targetIndex++; + if(targetIndex <= GetModSpecifications().samplesMax) { - samplesrc[nSamples] = (WORD)n; - samplemap[nSamples] = (WORD)nsmp; - nSamples++; - pIns->Keyboard[i] = (SAMPLEINDEX)nsmp; - if (m_nSamples < nsmp) m_nSamples = nsmp; - nsmp++; + sourceSample.push_back(sourceIndex); + targetSample.push_back(targetIndex); + pIns->Keyboard[i] = targetIndex++; } else { pIns->Keyboard[i] = 0; } } else { - pIns->Keyboard[i] = samplemap[j]; + // Sample reference has already been created, so only need to update the sample map. + pIns->Keyboard[i] = *(entry - sourceSample.begin() + targetSample.begin()); } } else { + // Invalid or no source sample pIns->Keyboard[i] = 0; } } - // Load Samples - for (UINT k=0; k<nSamples; k++) + + ConvertInstrument(targetInstr, pSrcSong->GetType()); + + // Copy all referenced samples over + for(size_t i = 0; i < targetSample.size(); i++) { - ReadSampleFromSong(samplemap[k], pSrcSong, samplesrc[k]); + ReadSampleFromSong(targetSample[i], pSrcSong, sourceSample[i]); } return true; } -bool CSoundFile::ReadSampleFromSong(SAMPLEINDEX nSample, CSoundFile *pSrcSong, UINT nSrcSample) -//--------------------------------------------------------------------------------------------- +bool CSoundFile::ReadSampleFromSong(SAMPLEINDEX targetSample, const CSoundFile *pSrcSong, SAMPLEINDEX sourceSample) +//----------------------------------------------------------------------------------------------------------------- { - if ((!pSrcSong) || (!nSrcSample) || (nSrcSample > pSrcSong->m_nSamples) || (nSample >= MAX_SAMPLES)) return false; - MODSAMPLE *psmp = &pSrcSong->Samples[nSrcSample]; - UINT nSize = psmp->nLength; - if (psmp->uFlags & CHN_16BIT) nSize *= 2; - if (psmp->uFlags & CHN_STEREO) nSize *= 2; - if (m_nSamples < nSample) m_nSamples = nSample; - if (Samples[nSample].pSample) + if ((!pSrcSong) || (!sourceSample) || (sourceSample > pSrcSong->m_nSamples) || (targetSample >= GetModSpecifications().samplesMax)) { - Samples[nSample].nLength = 0; - FreeSample(Samples[nSample].pSample); + return false; } - Samples[nSample] = *psmp; - if (psmp->pSample) + + const MODSAMPLE *pSourceSample = &pSrcSong->Samples[sourceSample]; + + if (m_nSamples < targetSample) m_nSamples = targetSample; + if (Samples[targetSample].pSample) { - Samples[nSample].pSample = AllocateSample(nSize+8); - if (Samples[nSample].pSample) + Samples[targetSample].nLength = 0; + FreeSample(Samples[targetSample].pSample); + } + Samples[targetSample] = *pSourceSample; + if (pSourceSample->pSample) + { + UINT nSize = pSourceSample->GetSampleSizeInBytes(); + Samples[targetSample].pSample = AllocateSample(nSize + 8); + if (Samples[targetSample].pSample) { - memcpy(Samples[nSample].pSample, psmp->pSample, nSize); - AdjustSampleLoop(&Samples[nSample]); + memcpy(Samples[targetSample].pSample, pSourceSample->pSample, nSize); + AdjustSampleLoop(&Samples[targetSample]); } } - if ((!(m_nType & (MOD_TYPE_MOD|MOD_TYPE_XM))) && (pSrcSong->m_nType & (MOD_TYPE_MOD|MOD_TYPE_XM))) - { - MODSAMPLE *pSmp = &Samples[nSample]; - pSmp->nC5Speed = TransposeToFrequency(pSmp->RelativeTone, pSmp->nFineTune); - pSmp->RelativeTone = 0; - pSmp->nFineTune = 0; - } else - if ((m_nType & (MOD_TYPE_MOD|MOD_TYPE_XM)) && (!(pSrcSong->m_nType & (MOD_TYPE_MOD|MOD_TYPE_XM)))) - { - FrequencyToTranspose(&Samples[nSample]); - } + + ConvertSample(targetSample, pSrcSong->GetType()); + return true; } @@ -443,7 +437,7 @@ if (m_nType & MOD_TYPE_XM) FrequencyToTranspose(pSmp); pSmp->nVibType = pSmp->nVibSweep = pSmp->nVibDepth = pSmp->nVibRate = 0; pSmp->filename[0] = 0; - memset(m_szNames[nSample], 0, 32); + MemsetZero(m_szNames[nSample]); if (pSmp->nLength > MAX_SAMPLE_LENGTH) pSmp->nLength = MAX_SAMPLE_LENGTH; // IMA ADPCM 4:1 if (pfmtpk) @@ -1029,7 +1023,9 @@ pSmp->nC5Speed = pss->nC5Speed; pSmp->RelativeTone = 0; pSmp->nFineTune = 0; - if (m_nType & MOD_TYPE_XM) FrequencyToTranspose(pSmp); + + ConvertSample(nSample, MOD_TYPE_S3M); + if (pss->flags & 0x01) pSmp->uFlags |= CHN_LOOP; flags = (pss->flags & 0x04) ? RS_PCM16U : RS_PCM8U; if (pss->flags & 0x02) flags |= RSF_STEREO; @@ -1254,6 +1250,8 @@ memcpy(m_szNames[samplemap[ismp]], psh->name, 22); memcpy(pSmp->filename, psh->name, 22); pSmp->filename[21] = 0; + + ConvertSample(samplemap[ismp], MOD_TYPE_XM); } // Reading sample data for (UINT dsmp=0; dsmp<nsamples; dsmp++) @@ -1264,6 +1262,8 @@ dwMemPos += samplesize[dsmp]; } + ConvertInstrument(nInstr, MOD_TYPE_XM); + // -> CODE#0027 // -> DESC="per-instrument volume ramping setup (refered as attack)" @@ -1340,6 +1340,11 @@ xih.vibsweep = min(Samples[n].nVibSweep, 255); xih.vibdepth = min(Samples[n].nVibDepth, 15); xih.vibrate = min(Samples[n].nVibRate, 63); + if((xih.vibdepth | xih.vibrate) != 0 && !(GetType() & MOD_TYPE_XM)) + { + // Sweep is upside down in XM + xih.vibsweep = 255 - xih.vibsweep; + } } if (nsamples < 32) smptable[nsamples++] = n; k = nsamples - 1; @@ -1476,12 +1481,6 @@ pSmp->nFineTune = psh->finetune; pSmp->nC5Speed = 8363; pSmp->RelativeTone = (int)psh->relnote; - if (m_nType != MOD_TYPE_XM) - { - pSmp->nC5Speed = TransposeToFrequency(pSmp->RelativeTone, pSmp->nFineTune); - pSmp->RelativeTone = 0; - pSmp->nFineTune = 0; - } pSmp->nPan = psh->pan; pSmp->uFlags |= CHN_PANNING; pSmp->nVibType = pih->vibtype; @@ -1491,6 +1490,7 @@ memcpy(pSmp->filename, psh->name, 22); pSmp->filename[21] = 0; } + ConvertSample(nSample, MOD_TYPE_XM); if (dwMemPos >= dwFileLength) return true; ReadSample(pSmp, sampleflags, (LPSTR)(lpMemFile+dwMemPos), dwFileLength-dwMemPos); return true; @@ -1657,7 +1657,6 @@ if (pis->C5Speed < 256) pSmp->nC5Speed = 256; pSmp->RelativeTone = 0; pSmp->nFineTune = 0; - if (GetType() == MOD_TYPE_XM) FrequencyToTranspose(pSmp); pSmp->nVolume = pis->vol << 2; if (pSmp->nVolume > 256) pSmp->nVolume = 256; pSmp->nGlobalVol = pis->gvl; @@ -1696,10 +1695,14 @@ // IT 2.14 8-bit packed sample ? if (pis->flags & 8) flags = RS_IT2148; } + + ConvertSample(nSample, MOD_TYPE_IT); + // -> CODE#0027 // -> DESC="per-instrument volume ramping setup (refered as attack)" // ReadSample(pSmp, flags, (LPSTR)(lpMemFile+dwMemPos), dwFileLength + dwOffset - dwMemPos); // return TRUE; + return ReadSample(pSmp, flags, (LPSTR)(lpMemFile+dwMemPos), dwFileLength + dwOffset - dwMemPos); // -! NEW_FEATURE#0027 } @@ -1788,6 +1791,8 @@ // Leave if no extra instrument settings are available (end of file reached) if(dwMemPos >= dwFileLength) return true; + ConvertInstrument(nInstr, MOD_TYPE_IT); + ReadExtendedInstrumentProperties(pIns, lpMemFile + dwMemPos, dwFileLength - dwMemPos); // -! NEW_FEATURE#0027 @@ -1931,6 +1936,11 @@ itss.dfp = psmp->nPan >> 2; itss.vit = autovibxm2it[psmp->nVibType & 7]; itss.vir = min(psmp->nVibSweep, 255); + if((itss.vid | itss.vis) && (GetType() & MOD_TYPE_XM)) + { + // Sweep is upside down in XM + itss.vir = 255 - itss.vir; + } itss.vid = min(psmp->nVibDepth, 32); itss.vis = min(psmp->nVibRate, 64); if (psmp->uFlags & CHN_PANNING) itss.dfp |= 0x80; @@ -2188,3 +2198,158 @@ return (pSmp->pSample != nullptr); } + +// Translate sample properties between two given formats. +void CSoundFile::ConvertSample(SAMPLEINDEX sample, MODTYPE fromType, MODTYPE toType) +//---------------------------------------------------------------------------------- +{ + if(toType == MOD_TYPE_NONE) + { + toType = GetType(); + } + + if(sample < 1 || sample > GetNumSamples()) + { + return; + } + + MODSAMPLE &smp = GetSample(sample); + // Convert between frequency and transpose values if necessary. + if ((!(toType & (MOD_TYPE_MOD | MOD_TYPE_XM))) && (fromType & (MOD_TYPE_MOD | MOD_TYPE_XM))) + { + smp.nC5Speed = TransposeToFrequency(smp.RelativeTone, smp.nFineTune); + smp.RelativeTone = 0; + smp.nFineTune = 0; + } else if((toType & (MOD_TYPE_MOD | MOD_TYPE_XM)) && (!(fromType & (MOD_TYPE_MOD | MOD_TYPE_XM)))) + { + FrequencyToTranspose(&smp); + if(toType & MOD_TYPE_MOD) + { + smp.RelativeTone = 0; + } + } + + // No ping-pong loop, panning and auto-vibrato for MOD / S3M samples + if(toType & (MOD_TYPE_MOD | MOD_TYPE_S3M)) + { + smp.uFlags &= ~(CHN_PINGPONGLOOP | CHN_PANNING); + + smp.nVibDepth = 0; + smp.nVibRate = 0; + smp.nVibSweep = 0; + smp.nVibType = VIB_SINE; + } + + // No sustain loops for MOD/S3M/XM + if(toType & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_S3M)) + { + // Sustain loops - convert to normal loops + if((smp.uFlags & CHN_SUSTAINLOOP) != 0) + { + // We probably overwrite a normal loop here, but since sustain loops are evaluated before normal loops, this is just correct. + smp.nLoopStart = smp.nSustainStart; + smp.nLoopEnd = smp.nSustainEnd; + smp.uFlags |= CHN_LOOP; + if(smp.uFlags & CHN_PINGPONGSUSTAIN) + { + smp.uFlags |= CHN_PINGPONGLOOP; + } else + { + smp.uFlags &= ~CHN_PINGPONGLOOP; + } + } + smp.nSustainStart = smp.nSustainEnd = 0; + smp.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(!(smp.uFlags & CHN_PANNING)) + { + smp.uFlags |= CHN_PANNING; + smp.nPan = 128; + } + + LimitMax(smp.nVibDepth, BYTE(15)); + LimitMax(smp.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(smp.nVibRate != 0 && smp.nVibDepth != 0) + { + smp.nVibSweep = 255 - smp.nVibSweep; + } + } +} + + +// Translate instrument properties between two given formats. +void CSoundFile::ConvertInstrument(INSTRUMENTINDEX instr, MODTYPE fromType, MODTYPE toType) +//----------------------------------------------------------------------------------------- +{ + UNREFERENCED_PARAMETER(fromType); + + if(toType == MOD_TYPE_NONE) + { + toType = GetType(); + } + + if(instr < 1 || instr > GetNumInstruments() || Instruments[instr] == nullptr) + { + return; + } + + MODINSTRUMENT *pIns = Instruments[instr]; + + if(toType & MOD_TYPE_XM) + { + for(size_t i = 0; i < CountOf(pIns->NoteMap); i++) + { + pIns->NoteMap[i] = static_cast<BYTE>(i + 1); + } + + // Convert sustain loops to sustain "points" + pIns->VolEnv.nSustainEnd = pIns->VolEnv.nSustainStart; + pIns->PanEnv.nSustainEnd = pIns->PanEnv.nSustainStart; + + pIns->VolEnv.dwFlags &= ~ENV_CARRY; + pIns->PanEnv.dwFlags &= ~ENV_CARRY; + pIns->PitchEnv.dwFlags &= ~(ENV_CARRY|ENV_ENABLED|ENV_FILTER); + + pIns->dwFlags &= ~INS_SETPANNING; + pIns->SetCutoff(pIns->GetCutoff(), false); + pIns->SetResonance(pIns->GetResonance(), false); + pIns->nFilterMode = FLTMODE_UNCHANGED; + + pIns->nCutSwing = pIns->nPanSwing = pIns->nResSwing = pIns->nVolSwing = 0; + + pIns->nPPC = NOTE_MIDDLEC - 1; + pIns->nPPS = 0; + + pIns->nNNA = NNA_NOTECUT; + pIns->nDCT = DCT_NONE; + pIns->nDNA = DNA_NOTECUT; + + pIns->nGlobalVol = 64; + pIns->nPan = 128; + + LimitMax(pIns->nFadeOut, 32767u); + } + + // Limit fadeout length for IT / MPT + if(toType & (MOD_TYPE_IT | MOD_TYPE_MPT)) + { + LimitMax(pIns->nFadeOut, 8192u); + } + + // MPT-specific features - remove instrument tunings, Pitch/Tempo Lock for other formats + if(!(toType & MOD_TYPE_MPT)) + { + pIns->SetTuning(nullptr); + pIns->wPitchToTempoLock = 0; + } +} Modified: trunk/OpenMPT/soundlib/Sndfile.h =================================================================== --- trunk/OpenMPT/soundlib/Sndfile.h 2011-11-18 22:21:47 UTC (rev 1136) +++ trunk/OpenMPT/soundlib/Sndfile.h 2011-11-18 22:30:48 UTC (rev 1137) @@ -896,6 +896,11 @@ static void S3MSxx2MODExx(MODCOMMAND *m); // Convert Sxx to Exx void SetupMODPanning(bool bForceSetup = false); // Setup LRRL panning, max channel volume + // Translate sample properties between two given formats. + void ConvertSample(SAMPLEINDEX sample, MODTYPE fromType, MODTYPE toType = MOD_TYPE_NONE); + // Translate instrument properties between two given formats. + void ConvertInstrument(INSTRUMENTINDEX instr, MODTYPE fromType, MODTYPE toType = MOD_TYPE_NONE); + public: // Real-time sound functions void SuspendPlugins(); //rewbs.VSTCompliance @@ -1090,8 +1095,8 @@ bool SaveXIInstrument(INSTRUMENTINDEX nInstr, LPCSTR lpszFileName); bool SaveITIInstrument(INSTRUMENTINDEX nInstr, LPCSTR lpszFileName); // I/O from another sound file - bool ReadInstrumentFromSong(INSTRUMENTINDEX nInstr, CSoundFile *pSrcSong, UINT nSrcInstrument); - bool ReadSampleFromSong(SAMPLEINDEX nSample, CSoundFile *pSrcSong, UINT nSrcSample); + bool ReadInstrumentFromSong(INSTRUMENTINDEX targetInstr, const CSoundFile *pSrcSong, INSTRUMENTINDEX sourceInstr); + bool ReadSampleFromSong(SAMPLEINDEX targetSample, const CSoundFile *pSrcSong, SAMPLEINDEX sourceSample); // Period/Note functions UINT GetNoteFromPeriod(UINT period) const; UINT GetPeriodFromNote(UINT note, int nFineTune, UINT nC5Speed) const; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |