void VGMColl::UnpackSampColl(SynthFile& synthfile, VGMSampColl* sampColl, vector<VGMSamp*>& finalSamps) { assert(sampColl != NULL); size_t nSamples = sampColl->samples.size(); for (size_t i=0; i<nSamples; i++) { VGMSamp* samp = sampColl->samples[i]; uint32_t bufSize; if (samp->ulUncompressedSize) bufSize = samp->ulUncompressedSize; else bufSize = (uint32_t)ceil((double)samp->dataLength * samp->GetCompressionRatio()); //bool bOddBufSize = bufSize % 2; //if (bOddBufSize) //if the buffer size is odd, we must align it to be even for the RIFF format // bufSize++; uint8_t* uncompSampBuf = new uint8_t[bufSize]; //create a new memory space for the uncompressed wave samp->ConvertToStdWave(uncompSampBuf); //and uncompress into that space //if (bOddBufSize) // uncompSampBuf[bufSize] = 0; //set the last (should be unused) byte to 0; uint16_t blockAlign = samp->bps / 8*samp->channels; SynthWave* wave = synthfile.AddWave(1, samp->channels, samp->rate, samp->rate*blockAlign, blockAlign, samp->bps, bufSize, uncompSampBuf, wstring2string(samp->name)); finalSamps.push_back(samp); // If we don't have any loop information, then don't create a sampInfo structure for the Wave if (samp->loop.loopStatus == -1) return; SynthSampInfo* sampInfo = wave->AddSampInfo(); if (samp->bPSXLoopInfoPrioritizing) { if (samp->loop.loopStart != 0 || samp->loop.loopLength != 0) sampInfo->SetLoopInfo(samp->loop, samp); } else sampInfo->SetLoopInfo(samp->loop, samp); double attenuation = (samp->volume != -1) ? ConvertLogScaleValToAtten(samp->volume) : 0; uint8_t unityKey = (samp->unityKey != -1) ? samp->unityKey : 0x3C; short fineTune = samp->fineTune; sampInfo->SetPitchInfo(unityKey, fineTune, attenuation); } }
SynthFile *VGMColl::CreateSynthFile() { SynthFile *synthfile = new SynthFile("SynthFile"/**this->instrsets[0]->GetName()*/); vector<VGMSamp *> finalSamps; //we have to collect the finalSampColls in case sampcolls is empty but there are multiple instrSets with //embedded sampcolls vector<VGMSampColl *> finalSampColls; if (!instrsets.size() /*|| !sampcolls.size()*/) { delete synthfile; return NULL; } // if there are independent SampColl(s) in the collection if (sampcolls.size()) { for (uint32_t sam = 0; sam < sampcolls.size(); sam++) { finalSampColls.push_back(sampcolls[sam]); UnpackSampColl(*synthfile, sampcolls[sam], finalSamps); } } // otherwise, the SampColl(s) are children of the InstrSet(s) else { for (uint32_t i = 0; i < instrsets.size(); i++) { finalSampColls.push_back(instrsets[i]->sampColl); UnpackSampColl(*synthfile, instrsets[i]->sampColl, finalSamps); } } if (finalSamps.size() == 0) { delete synthfile; return NULL; } for (size_t inst = 0; inst < instrsets.size(); inst++) { VGMInstrSet *set = instrsets[inst]; size_t nInstrs = set->aInstrs.size(); for (size_t i = 0; i < nInstrs; i++) { VGMInstr *vgminstr = set->aInstrs[i]; size_t nRgns = vgminstr->aRgns.size(); if (nRgns == 0) //do not write an instrument if it has no regions continue; SynthInstr *newInstr = synthfile->AddInstr(vgminstr->bank, vgminstr->instrNum); for (uint32_t j = 0; j < nRgns; j++) { VGMRgn *rgn = vgminstr->aRgns[j]; // if (rgn->sampNum+1 > sampColl->samples.size()) //does thereferenced sample exist? // continue; // Determine the SampColl associated with this rgn. If there's an explicit pointer to it, use that. VGMSampColl *sampColl = rgn->sampCollPtr; if (!sampColl) { // If rgn is of an InstrSet with an embedded SampColl, use that SampColl. if (((VGMInstrSet *) rgn->vgmfile)->sampColl) sampColl = ((VGMInstrSet *) rgn->vgmfile)->sampColl; // If that does not exist, assume the first SampColl else sampColl = finalSampColls[0]; } // Determine the sample number within the rgn's associated SampColl size_t realSampNum; // If a sample offset is provided, then find the sample number based on this offset. // see sampOffset declaration in header file for more info. if (rgn->sampOffset != -1) { bool bFoundIt = false; for (uint32_t s = 0; s < sampColl->samples.size(); s++) { //for every sample if (rgn->sampOffset == sampColl->samples[s]->dwOffset - sampColl->dwOffset - sampColl->sampDataOffset) { realSampNum = s; //samples[m]->loop.loopStart = parInstrSet->aInstrs[i]->aRgns[k]->loop.loopStart; //samples[m]->loop.loopLength = (samples[m]->dataLength) - (parInstrSet->aInstrs[i]->aRgns[k]->loop.loopStart); //[aInstrs[i]->aRegions[k]->sample_num]->dwUncompSize/2) - ((aInstrs[i]->aRegions[k]->loop_point*28)/16); //to end of sample bFoundIt = true; break; } } if (!bFoundIt) { std::wstring message = FormatString<wstring>(L"Could not match rgn with sampOffset %X to a sample with that offset.\n (InstrSet %d, Instr %d, Rgn %d)", rgn->sampOffset, inst, i, j); pRoot->AddLogItem(new LogItem(std::wstring(message.c_str()), LOG_LEVEL_ERR, L"VGMColl")); realSampNum = 0; } } // Otherwise, the sample number should be explicitly defined in the rgn. else realSampNum = rgn->sampNum; // Determine the sampCollNum (index into our finalSampColls vector) unsigned int sampCollNum = finalSampColls.size(); for (uint32_t i = 0; i < finalSampColls.size(); i++) { if (finalSampColls[i] == sampColl) sampCollNum = i; } if (sampCollNum == finalSampColls.size()) { pRoot->AddLogItem(new LogItem(L"SampColl does not exist.", LOG_LEVEL_ERR, L"VGMColl")); return NULL; } // now we add the number of samples from the preceding SampColls to the value to get the real sampNum // in the final DLS file. for (uint32_t k = 0; k < sampCollNum; k++) realSampNum += finalSampColls[k]->samples.size(); SynthRgn *newRgn = newInstr->AddRgn(); newRgn->SetRanges(rgn->keyLow, rgn->keyHigh, rgn->velLow, rgn->velHigh); newRgn->SetWaveLinkInfo(0, 0, 1, (uint32_t) realSampNum); if (realSampNum >= finalSamps.size()) { wchar_t log[256]; swprintf(log, 256, L"Sample %u does not exist.", realSampNum); pRoot->AddLogItem(new LogItem(log, LOG_LEVEL_ERR, L"VGMColl")); realSampNum = finalSamps.size() - 1; } VGMSamp *samp = finalSamps[realSampNum];//sampColl->samples[rgn->sampNum]; SynthSampInfo *sampInfo = newRgn->AddSampInfo(); // This is a really loopy way of determining the loop information, pardon the pun. However, it works. // There might be a way to simplify this, but I don't want to test out whether another method breaks anything just yet // Use the sample's loopStatus to determine if a loop occurs. If it does, see if the sample provides loop info // (gathered during ADPCM > PCM conversion. If the sample doesn't provide loop offset info, then use the region's // loop info. if (samp->bPSXLoopInfoPrioritizing) { if (samp->loop.loopStatus != -1) { if (samp->loop.loopStart != 0 || samp->loop.loopLength != 0) sampInfo->SetLoopInfo(samp->loop, samp); else { rgn->loop.loopStatus = samp->loop.loopStatus; sampInfo->SetLoopInfo(rgn->loop, samp); } } else { delete synthfile; throw; } } // The normal method: First, we check if the rgn has loop info defined. // If it doesn't, then use the sample's loop info. else if (rgn->loop.loopStatus == -1) { if (samp->loop.loopStatus != -1) sampInfo->SetLoopInfo(samp->loop, samp); else { delete synthfile; throw; } } else sampInfo->SetLoopInfo(rgn->loop, samp); uint8_t realUnityKey; if (rgn->unityKey == -1) realUnityKey = samp->unityKey; else realUnityKey = rgn->unityKey; if (realUnityKey == -1) realUnityKey = 0x3C; short realFineTune; if (rgn->fineTune == 0) realFineTune = samp->fineTune; else realFineTune = rgn->fineTune; double attenuation; if (rgn->volume != -1) attenuation = ConvertLogScaleValToAtten(rgn->volume); else if (samp->volume != -1) attenuation = ConvertLogScaleValToAtten(samp->volume); else attenuation = 0; double sustainLevAttenDb; if (rgn->sustain_level == -1) sustainLevAttenDb = 0.0; else sustainLevAttenDb = ConvertPercentAmplitudeToAttenDB_SF2(rgn->sustain_level); SynthArt *newArt = newRgn->AddArt(); newArt->AddPan(rgn->pan); newArt->AddADSR(rgn->attack_time, (Transform) rgn->attack_transform, rgn->decay_time, sustainLevAttenDb, rgn->sustain_time, rgn->release_time, (Transform) rgn->release_transform); sampInfo->SetPitchInfo(realUnityKey, realFineTune, attenuation); } } } return synthfile; }