bool CZipArchive::ExtractFile(std::size_t index) //---------------------------------------------- { mz_zip_archive *zip = static_cast<mz_zip_archive*>(zipFile); if(index >= contents.size()) { return false; } mz_uint bestFile = index; mz_zip_archive_file_stat stat; MemsetZero(stat); mz_zip_reader_file_stat(zip, bestFile, &stat); if(stat.m_uncomp_size >= std::numeric_limits<std::size_t>::max()) { return false; } try { data.resize(static_cast<std::size_t>(stat.m_uncomp_size)); } catch(...) { return false; } if(!mz_zip_reader_extract_to_mem(zip, bestFile, &data[0], static_cast<std::size_t>(stat.m_uncomp_size), 0)) { return false; } comment = mpt::ToWide(mpt::CharsetCP437, std::string(stat.m_comment, stat.m_comment + stat.m_comment_size)); return true; }
bool COwnerVstEditor::OpenEditor(CWnd *parent) //-------------------------------------------- { Create(IDD_PLUGINEDITOR, parent); // Some plugins (e.g. ProteusVX) need to be planted into another control or else they will break our window proc, making the window unusable. m_plugWindow.Create(nullptr, WS_CHILD | WS_VISIBLE, CRect(0, 0, 100, 100), this); // Set editor window size ERect rect; MemsetZero(rect); ERect *pRect = nullptr; CVstPlugin &vstPlug = static_cast<CVstPlugin &>(m_VstPlugin); vstPlug.Dispatch(effEditGetRect, 0, 0, &pRect, 0); if(pRect) rect = *pRect; vstPlug.Dispatch(effEditOpen, 0, 0, m_plugWindow.m_hWnd, 0); vstPlug.Dispatch(effEditGetRect, 0, 0, &pRect, 0); if(pRect) rect = *pRect; if(rect.right > rect.left && rect.bottom > rect.top) { // Plugin provided valid window size. SetSize(rect.right - rect.left, rect.bottom - rect.top); } vstPlug.Dispatch(effEditTop, 0,0, NULL, 0); vstPlug.Dispatch(effEditIdle, 0,0, NULL, 0); // Set knob mode to linear (2) instead of circular (0) for those plugins that support it (e.g. Steinberg VB-1) vstPlug.Dispatch(effSetEditKnobMode, 0, 2, nullptr, 0.0f); return CAbstractVstEditor::OpenEditor(parent); }
SoundDevice::DynamicCaps CWaveDevice::GetDeviceDynamicCaps(const std::vector<uint32> & /*baseSampleRates*/ ) //-------------------------------------------------------------------------------------------------------- { MPT_TRACE(); SoundDevice::DynamicCaps caps; WAVEOUTCAPSW woc; MemsetZero(woc); if(GetDeviceIndex() > 0) { if(waveOutGetDevCapsW(GetDeviceIndex() - 1, &woc, sizeof(woc)) == MMSYSERR_NOERROR) { if(woc.dwFormats & (WAVE_FORMAT_96M08 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S08 | WAVE_FORMAT_96S16)) { caps.supportedExclusiveSampleRates.push_back(96000); } if(woc.dwFormats & (WAVE_FORMAT_48M08 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S08 | WAVE_FORMAT_48S16)) { caps.supportedExclusiveSampleRates.push_back(48000); } if(woc.dwFormats & (WAVE_FORMAT_4M08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4S16)) { caps.supportedExclusiveSampleRates.push_back(44100); } if(woc.dwFormats & (WAVE_FORMAT_2M08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2S16)) { caps.supportedExclusiveSampleRates.push_back(22050); } if(woc.dwFormats & (WAVE_FORMAT_1M08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1S16)) { caps.supportedExclusiveSampleRates.push_back(11025); } } } return caps; }
bool VSTPresets::SaveFile(std::ostream &f, CVstPlugin &plugin, bool bank) //----------------------------------------------------------------------- { if(!bank) { SaveProgram(f, plugin); } else { bool writeChunk = plugin.ProgramsAreChunks(); ChunkHeader header; header.chunkMagic = cMagic; header.version = 2; header.fxID = plugin.GetUID(); header.fxVersion = plugin.GetVersion(); // Write unfinished header... We need to update the size once we're done writing. Write(header, f); uint32 numProgs = std::max(plugin.GetNumPrograms(), VstInt32(1)), curProg = plugin.GetCurrentProgram(); WriteBE(numProgs, f); WriteBE(curProg, f); char reserved[124]; MemsetZero(reserved); Write(reserved, f); if(writeChunk) { char *chunk = nullptr; uint32 chunkSize = mpt::saturate_cast<uint32>(plugin.Dispatch(effGetChunk, 0, 0, &chunk, 0)); if(chunkSize && chunk) { WriteBE(chunkSize, f); f.write(chunk, chunkSize); } else { // The plugin returned no chunk! Gracefully go back and save parameters instead... writeChunk = false; } } if(!writeChunk) { for(uint32 p = 0; p < numProgs; p++) { plugin.SetCurrentProgram(p); SaveProgram(f, plugin); } plugin.SetCurrentProgram(curProg); } // Now we know the correct chunk size. std::streamoff end = f.tellp(); header.byteSize = static_cast<VstInt32>(end - 8); header.fxMagic = writeChunk ? chunkBankMagic : bankMagic; header.ConvertEndianness(); f.seekp(0); Write(header, f); } return true; }
bool FillWaveFormatExtensible(WAVEFORMATEXTENSIBLE &WaveFormat, const SoundDevice::Settings &m_Settings) //------------------------------------------------------------------------------------------------------ { MemsetZero(WaveFormat); if(!m_Settings.sampleFormat.IsValid()) return false; WaveFormat.Format.wFormatTag = m_Settings.sampleFormat.IsFloat() ? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM; WaveFormat.Format.nChannels = (WORD)m_Settings.Channels; WaveFormat.Format.nSamplesPerSec = m_Settings.Samplerate; WaveFormat.Format.nAvgBytesPerSec = (DWORD)m_Settings.GetBytesPerSecond(); WaveFormat.Format.nBlockAlign = (WORD)m_Settings.GetBytesPerFrame(); WaveFormat.Format.wBitsPerSample = (WORD)m_Settings.sampleFormat.GetBitsPerSample(); WaveFormat.Format.cbSize = 0; if((WaveFormat.Format.wBitsPerSample > 16 && m_Settings.sampleFormat.IsInt()) || (WaveFormat.Format.nChannels > 2)) { WaveFormat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; WaveFormat.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); WaveFormat.Samples.wValidBitsPerSample = WaveFormat.Format.wBitsPerSample; switch(WaveFormat.Format.nChannels) { case 1: WaveFormat.dwChannelMask = SPEAKER_FRONT_CENTER; break; case 2: WaveFormat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break; case 3: WaveFormat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_CENTER; break; case 4: WaveFormat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break; default: WaveFormat.dwChannelMask = 0; return false; break; } const GUID guid_MEDIASUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x0, 0xAA, 0x0, 0x38, 0x9B, 0x71}; const GUID guid_MEDIASUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71}; WaveFormat.SubFormat = m_Settings.sampleFormat.IsFloat() ? guid_MEDIASUBTYPE_IEEE_FLOAT : guid_MEDIASUBTYPE_PCM; } return true; }
CWaveConvert::CWaveConvert(CWnd *parent, modplug::tracker::orderindex_t nMinOrder, modplug::tracker::orderindex_t nMaxOrder): CDialog(IDD_WAVECONVERT, parent) //----------------------------------------------------------------------------------- { m_bGivePlugsIdleTime = false; m_bNormalize = false; m_bHighQuality = false; m_bSelectPlay = false; if(nMinOrder != modplug::tracker::OrderIndexInvalid && nMaxOrder != modplug::tracker::OrderIndexInvalid) { // render selection m_nMinOrder = nMinOrder; m_nMaxOrder = nMaxOrder; m_bSelectPlay = true; } else { m_nMinOrder = m_nMaxOrder = 0; } m_dwFileLimit = 0; m_dwSongLimit = 0; MemsetZero(WaveFormat); WaveFormat.Format.wFormatTag = WAVE_FORMAT_PCM; WaveFormat.Format.nChannels = 2; WaveFormat.Format.nSamplesPerSec = 44100; WaveFormat.Format.wBitsPerSample = 16; WaveFormat.Format.nBlockAlign = (WaveFormat.Format.wBitsPerSample * WaveFormat.Format.nChannels) / 8; WaveFormat.Format.nAvgBytesPerSec = WaveFormat.Format.nSamplesPerSec * WaveFormat.Format.nBlockAlign; }
// Reset MIDI macro config to default values. void MIDIMacroConfig::Reset() //--------------------------- { MemsetZero(szMidiGlb); MemsetZero(szMidiSFXExt); MemsetZero(szMidiZXXExt); strcpy(szMidiGlb[MIDIOUT_START], "FF"); strcpy(szMidiGlb[MIDIOUT_STOP], "FC"); strcpy(szMidiGlb[MIDIOUT_NOTEON], "9c n v"); strcpy(szMidiGlb[MIDIOUT_NOTEOFF], "9c n 0"); strcpy(szMidiGlb[MIDIOUT_PROGRAM], "Cc p"); // SF0: Z00-Z7F controls cutoff CreateParameteredMacro(0, sfx_cutoff); // Z80-Z8F controls resonance CreateFixedMacro(zxx_reso4Bit); }
ModInstrument::ModInstrument(SAMPLEINDEX sample) //---------------------------------------------- { nFadeOut = 256; dwFlags.reset(); nGlobalVol = 64; nPan = 32 * 4; nNNA = NNA_NOTECUT; nDCT = DCT_NONE; nDNA = DNA_NOTECUT; nPanSwing = 0; nVolSwing = 0; SetCutoff(0, false); SetResonance(0, false); wMidiBank = 0; nMidiProgram = 0; nMidiChannel = 0; nMidiDrumKey = 0; midiPWD = 2; nPPC = NOTE_MIDDLEC - 1; nPPS = 0; nMixPlug = 0; nVolRampUp = 0; nResampling = SRCMODE_DEFAULT; nCutSwing = 0; nResSwing = 0; nFilterMode = FLTMODE_UNCHANGED; wPitchToTempoLock = 0; nPluginVelocityHandling = PLUGIN_VELOCITYHANDLING_CHANNEL; nPluginVolumeHandling = PLUGIN_VOLUMEHANDLING_IGNORE; pTuning = CSoundFile::GetDefaultTuning(); AssignSample(sample); ResetNoteMap(); MemsetZero(name); MemsetZero(filename); }
CZipArchive::CZipArchive(FileReader &file) : ArchiveBase(file) //------------------------------------------------------------ { zipFile = new mz_zip_archive(); mz_zip_archive *zip = static_cast<mz_zip_archive*>(zipFile); MemsetZero(*zip); if(!mz_zip_reader_init_mem(zip, file.GetRawData(), file.GetLength(), 0)) { delete zip; zip = nullptr; zipFile = nullptr; } if(!zip) { return; } for(mz_uint i = 0; i < mz_zip_reader_get_num_files(zip); ++i) { ArchiveFileInfo info; info.type = ArchiveFileInvalid; mz_zip_archive_file_stat stat; MemsetZero(stat); if(mz_zip_reader_file_stat(zip, i, &stat)) { info.type = ArchiveFileNormal; info.name = mpt::PathString::FromWide(mpt::ToWide(mpt::CharsetCP437, stat.m_filename)); info.size = stat.m_uncomp_size; } if(mz_zip_reader_is_file_a_directory(zip, i)) { info.type = ArchiveFileSpecial; } else if(mz_zip_reader_is_file_encrypted(zip, i)) { info.type = ArchiveFileSpecial; } contents.push_back(info); } }
void CWaveDevice::StopFromSoundThread() //------------------------------------- { MPT_TRACE(); if(m_hWaveOut) { CheckResult(waveOutPause(m_hWaveOut)); m_JustStarted = false; { MPT_LOCK_GUARD<mpt::mutex> guard(m_PositionWraparoundMutex); MemsetZero(m_PositionLast); m_PositionWrappedCount = 0; } } }
void CWaveDevice::StartFromSoundThread() //-------------------------------------- { MPT_TRACE(); if(m_hWaveOut) { { MPT_LOCK_GUARD<mpt::mutex> guard(m_PositionWraparoundMutex); MemsetZero(m_PositionLast); m_PositionWrappedCount = 0; } m_JustStarted = true; // Actual starting is done in InternalFillAudioBuffer to avoid crackling with tiny buffers. } }
// Convert OpenMPT's internal sample representation to an XMSample. void XMSample::ConvertToXM(const ModSample &mptSmp, MODTYPE fromType, bool compatibilityExport) //--------------------------------------------------------------------------------------------- { MemsetZero(*this); // Volume / Panning vol = static_cast<uint8>(std::min(mptSmp.nVolume / 4u, 64u)); pan = static_cast<uint8>(std::min(mptSmp.nPan, uint16(255))); // Sample Frequency if((fromType & (MOD_TYPE_MOD | MOD_TYPE_XM))) { finetune = mptSmp.nFineTune; relnote = mptSmp.RelativeTone; } else { int f2t = ModSample::FrequencyToTranspose(mptSmp.nC5Speed); relnote = (int8)(f2t >> 7); finetune = (int8)(f2t & 0x7F); } flags = 0; if(mptSmp.uFlags[CHN_LOOP]) { flags |= mptSmp.uFlags[CHN_PINGPONGLOOP] ? XMSample::sampleBidiLoop : XMSample::sampleLoop; } // Sample Length and Loops length = mpt::saturate_cast<uint32>(mptSmp.nLength); loopStart = mpt::saturate_cast<uint32>(mptSmp.nLoopStart); loopLength = mpt::saturate_cast<uint32>(mptSmp.nLoopEnd - mptSmp.nLoopStart); if(mptSmp.uFlags[CHN_16BIT]) { flags |= XMSample::sample16Bit; length *= 2; loopStart *= 2; loopLength *= 2; } if(mptSmp.uFlags[CHN_STEREO] && !compatibilityExport) { flags |= XMSample::sampleStereo; length *= 2; loopStart *= 2; loopLength *= 2; } }
bool COwnerVstEditor::SetSize(int contentWidth, int contentHeight) //---------------------------------------------------------------- { if(contentWidth < 0 || contentHeight < 0 || !m_hWnd) { return false; } CRect rcWnd, rcClient; // Get border / menu size. GetWindowRect(&rcWnd); GetClientRect(&rcClient); MENUBARINFO mbi; MemsetZero(mbi); mbi.cbSize = sizeof(mbi); GetMenuBarInfo(m_hWnd, OBJID_MENU, 0, &mbi); int menuHeight = mbi.rcBar.bottom - mbi.rcBar.top; // Preliminary setup, which might have to be adjusted for small (narrow) plugin GUIs again, // since the menu might be two lines high... const int windowWidth = rcWnd.Width() - rcClient.Width() + contentWidth; const int windowHeight = rcWnd.Height() - rcClient.Height() + contentHeight; SetWindowPos(NULL, 0, 0, windowWidth, windowHeight, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE); m_plugWindow.SetWindowPos(NULL, 0, 0, contentWidth, contentHeight, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE); // Check if the height of the menu bar has changed. GetMenuBarInfo(m_hWnd, OBJID_MENU, 0, &mbi); const int menuHeightDiff = (mbi.rcBar.bottom - mbi.rcBar.top) - menuHeight; if(menuHeightDiff != 0) { // Menu height changed, resize window so that the whole content area can be viewed again. SetWindowPos(NULL, 0, 0, windowWidth, windowHeight + menuHeightDiff, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE); } return true; }
// Create a DIB for the current device from our PNG. bool PNG::Bitmap::ToDIB(CBitmap &bitmap, CDC *dc) const //----------------------------------------------------- { BITMAPINFOHEADER bi; MemsetZero(bi); bi.biSize = sizeof(BITMAPINFOHEADER); bi.biWidth = width; bi.biHeight = -(int32_t)height; bi.biPlanes = 1; bi.biBitCount = 32; bi.biCompression = BI_RGB; bi.biSizeImage = width * height * 4; if(dc == nullptr) dc = CDC::FromHandle(GetDC(NULL)); return bitmap.CreateCompatibleBitmap(dc, width, height) && SetDIBits(dc->GetSafeHdc(), bitmap, 0, height, GetPixels(), reinterpret_cast<BITMAPINFO *>(&bi), DIB_RGB_COLORS); }
bool CWaveDevice::CheckResult(MMRESULT result) //-------------------------------------------- { if(result == MMSYSERR_NOERROR) { return true; } if(!m_Failed) { // only show the first error m_Failed = true; WCHAR errortext[MAXERRORLENGTH + 1]; MemsetZero(errortext); waveOutGetErrorTextW(result, errortext, MAXERRORLENGTH); SendDeviceMessage(LogError, mpt::format(MPT_USTRING("WaveOut error: 0x%1: %2"))(mpt::ufmt::hex0<8>(result), mpt::ToUnicode(errortext))); } RequestClose(); return false; }
void MultimediaClock::SetPeriod(uint32 ms) { TIMECAPS caps; MemsetZero(caps); if(timeGetDevCaps(&caps, sizeof(caps)) != MMSYSERR_NOERROR) { return; } if((caps.wPeriodMax == 0) || (caps.wPeriodMin > caps.wPeriodMax)) { return; } ms = Clamp<uint32>(ms, caps.wPeriodMin, caps.wPeriodMax); if(timeBeginPeriod(ms) != MMSYSERR_NOERROR) { return; } m_CurrentPeriod = ms; }
std::vector<SoundDevice::Info> CWaveDevice::EnumerateDevices(SoundDevice::SysInfo /* sysInfo */ ) //----------------------------------------------------------------------------------------------- { MPT_TRACE(); std::vector<SoundDevice::Info> devices; UINT numDevs = waveOutGetNumDevs(); for(UINT index = 0; index <= numDevs; ++index) { SoundDevice::Info info; info.type = TypeWAVEOUT; info.internalID = mpt::ufmt::dec(index); info.apiName = MPT_USTRING("WaveOut"); info.useNameAsIdentifier = true; WAVEOUTCAPSW woc; MemsetZero(woc); if(waveOutGetDevCapsW((index == 0) ? WAVE_MAPPER : (index - 1), &woc, sizeof(woc)) == MMSYSERR_NOERROR) { info.name = mpt::ToUnicode(woc.szPname); info.extraData[MPT_USTRING("DriverID")] = mpt::format(MPT_USTRING("%1:%2"))(mpt::ufmt::hex0<4>(woc.wMid), mpt::ufmt::hex0<4>(woc.wPid)); info.extraData[MPT_USTRING("DriverVersion")] = mpt::format(MPT_USTRING("%3.%4"))(mpt::ufmt::dec((static_cast<uint32>(woc.vDriverVersion) >> 24) & 0xff), mpt::ufmt::dec((static_cast<uint32>(woc.vDriverVersion) >> 0) & 0xff)); } else if(index == 0)
bool CWaveDevice::InternalClose() //------------------------------- { MPT_TRACE(); if(m_hWaveOut) { waveOutReset(m_hWaveOut); m_JustStarted = false; InterlockedExchange(&m_nBuffersPending, 0); m_nWriteBuffer = 0; m_nDoneBuffer = 0; while(m_nPreparedHeaders > 0) { m_nPreparedHeaders--; waveOutUnprepareHeader(m_hWaveOut, &m_WaveBuffers[m_nPreparedHeaders], sizeof(WAVEHDR)); } waveOutClose(m_hWaveOut); m_hWaveOut = NULL; } #ifdef _DEBUG if(m_DriverBugs.load()) { SendDeviceMessage(LogError, MPT_USTRING("Errors were detected while playing sound:\n") + GetStatistics().text); } #endif m_DriverBugs = 0; m_Failed = false; if(m_ThreadWakeupEvent) { CloseHandle(m_ThreadWakeupEvent); m_ThreadWakeupEvent = NULL; } { MPT_LOCK_GUARD<mpt::mutex> guard(m_PositionWraparoundMutex); MemsetZero(m_PositionLast); m_PositionWrappedCount = 0; } return true; }
void WavesReverb::Resume() //------------------------ { m_isResumed = true; // Recalculate delays uint32 delay0 = Util::Round<uint32>(m_SndFile.GetSampleRate() * 0.045); uint32 delay1 = Util::Round<uint32>(delay0 * 1.189207077026367); // 2^0.25 uint32 delay2 = Util::Round<uint32>(delay1 * 1.189207077026367); uint32 delay3 = Util::Round<uint32>(delay2 * 1.189207077026367); uint32 delay4 = Util::Round<uint32>((delay0 + delay2) * 0.1154666692018509); uint32 delay5 = Util::Round<uint32>((delay1 + delay3) * 0.1154666692018509); // Comb delays m_delay[0] = (delay0 - delay4) * 4; m_delay[1] = (delay2 - delay4) * 4; m_delay[2] = (delay1 - delay5) * 4; m_delay[3] = (delay3 - delay5) * 4; // Allpass delays m_delay[4] = delay4 * 2; m_delay[5] = delay5 * 2; RecalculateWavesReverbParams(); MemsetZero(m_state); }
PLUGINDEX CModDoc::RemovePlugs(const std::vector<bool> &keepMask) //--------------------------------------------------------------- { //Remove all plugins whose keepMask[plugindex] is false. PLUGINDEX nRemoved = 0; const PLUGINDEX maxPlug = static_cast<PLUGINDEX>(MIN(MAX_MIXPLUGINS, keepMask.size())); CriticalSection cs; for(PLUGINDEX nPlug = 0; nPlug < maxPlug; nPlug++) { SNDMIXPLUGIN &plug = m_SndFile.m_MixPlugins[nPlug]; if(keepMask[nPlug]) { continue; } if(plug.pMixPlugin || plug.IsValidPlugin()) { nRemoved++; } delete[] plug.pPluginData; plug.pPluginData = nullptr; if(plug.pMixPlugin) { plug.pMixPlugin->Release(); plug.pMixPlugin = nullptr; } MemsetZero(plug.Info); plug.nPluginDataSize = 0; plug.fDryRatio = 0; plug.defaultProgram = 0; } return nRemoved; }
// Convert OpenMPT's internal sample representation to an XMInstrument. uint16 XMInstrument::ConvertToXM(const ModInstrument &mptIns, bool compatibilityExport) //------------------------------------------------------------------------------------- { MemsetZero(*this); // FFF is maximum in the FT2 GUI, but it can also accept other values. MilkyTracker just allows 0...4095 and 32767 ("cut") volFade = static_cast<uint16>(std::min(mptIns.nFadeOut, uint32(32767))); // Convert envelopes ConvertEnvelopeToXM(mptIns.VolEnv, volPoints, volFlags, volSustain, volLoopStart, volLoopEnd, EnvTypeVol); ConvertEnvelopeToXM(mptIns.PanEnv, panPoints, panFlags, panSustain, panLoopStart, panLoopEnd, EnvTypePan); // Create sample assignment table std::vector<SAMPLEINDEX> sampleList = GetSampleList(mptIns, compatibilityExport); for(size_t i = 0; i < CountOf(sampleMap); i++) { if(mptIns.Keyboard[i + 12] > 0) { std::vector<SAMPLEINDEX>::iterator sample = std::find(sampleList.begin(), sampleList.end(), mptIns.Keyboard[i + 12]); if(sample != sampleList.end()) { // Yep, we want to export this sample. sampleMap[i] = static_cast<uint8>(sample - sampleList.begin()); } } } if(mptIns.nMidiChannel != MidiNoChannel) { midiEnabled = 1; midiChannel = (mptIns.nMidiChannel != MidiMappedChannel ? (mptIns.nMidiChannel - MidiFirstChannel) : 0); } midiProgram = (mptIns.nMidiProgram != 0 ? mptIns.nMidiProgram - 1 : 0); pitchWheelRange = std::min(mptIns.midiPWD, int8(36)); return static_cast<uint16>(sampleList.size()); }
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; }
VSTPresets::ErrorCode VSTPresets::LoadFile(FileReader &file, CVstPlugin &plugin) //------------------------------------------------------------------------------ { const bool firstChunk = file.GetPosition() == 0; ChunkHeader header; if(!file.ReadConvertEndianness(header) || header.chunkMagic != cMagic) { return invalidFile; } if(header.fxID != plugin.GetUID()) { return wrongPlugin; } if(header.fxMagic == fMagic || header.fxMagic == chunkPresetMagic) { // Program PlugParamIndex numParams = file.ReadUint32BE(); VstPatchChunkInfo info; info.version = 1; info.pluginUniqueID = header.fxID; info.pluginVersion = header.fxVersion; info.numElements = numParams; MemsetZero(info.future); plugin.Dispatch(effBeginLoadProgram, 0, 0, &info, 0.0f); plugin.Dispatch(effBeginSetProgram, 0, 0, nullptr, 0.0f); char prgName[28]; file.ReadString<mpt::String::maybeNullTerminated>(prgName, 28); plugin.Dispatch(effSetProgramName, 0, 0, prgName, 0.0f); if(header.fxMagic == fMagic) { if(plugin.GetNumParameters() != numParams) { return wrongParameters; } for(PlugParamIndex p = 0; p < numParams; p++) { plugin.SetParameter(p, file.ReadFloatBE()); } } else { FileReader chunk = file.ReadChunk(file.ReadUint32BE()); plugin.Dispatch(effSetChunk, 1, chunk.GetLength(), const_cast<char *>(chunk.GetRawData()), 0); } plugin.Dispatch(effEndSetProgram, 0, 0, nullptr, 0.0f); } else if((header.fxMagic == bankMagic || header.fxMagic == chunkBankMagic) && firstChunk) { // Bank - only read if it's the first chunk in the file, not if it's a sub chunk. uint32 numProgs = file.ReadUint32BE(); uint32 currentProgram = file.ReadUint32BE(); file.Skip(124); VstPatchChunkInfo info; info.version = 1; info.pluginUniqueID = header.fxID; info.pluginVersion = header.fxVersion; info.numElements = numProgs; MemsetZero(info.future); plugin.Dispatch(effBeginLoadBank, 0, 0, &info, 0.0f); if(header.fxMagic == bankMagic) { VstInt32 oldCurrentProgram = plugin.GetCurrentProgram(); for(uint32 p = 0; p < numProgs; p++) { plugin.Dispatch(effBeginSetProgram, 0, 0, nullptr, 0.0f); plugin.Dispatch(effSetProgram, 0, 0, nullptr, 0.0f); ErrorCode retVal = LoadFile(file, plugin); if(retVal != noError) { return retVal; } plugin.Dispatch(effEndSetProgram, 0, 0, nullptr, 0.0f); } plugin.SetCurrentProgram(oldCurrentProgram); } else { FileReader chunk = file.ReadChunk(file.ReadUint32BE()); plugin.Dispatch(effSetChunk, 0, chunk.GetLength(), const_cast<char *>(chunk.GetRawData()), 0); } if(header.version >= 2) { plugin.SetCurrentProgram(currentProgram); } } return noError; }
bool module_renderer::ReadXM(const uint8_t *lpStream, const uint32_t dwMemLength) //-------------------------------------------------------------------- { XMFILEHEADER xmheader; XMSAMPLEHEADER xmsh; XMSAMPLESTRUCT xmss; uint32_t dwMemPos; bool bMadeWithModPlug = false, bProbablyMadeWithModPlug = false, bProbablyMPT109 = false, bIsFT2 = false; m_nChannels = 0; if ((!lpStream) || (dwMemLength < 0xAA)) return false; // the smallest XM I know is 174 Bytes if (_strnicmp((LPCSTR)lpStream, "Extended Module", 15)) return false; // look for null-terminated song name - that's most likely a tune made with modplug for(int i = 0; i < 20; i++) if(lpStream[17 + i] == 0) bProbablyMadeWithModPlug = true; assign_without_padding(this->song_name, reinterpret_cast<const char *>(lpStream + 17), 20); // load and convert header memcpy(&xmheader, lpStream + 58, sizeof(XMFILEHEADER)); xmheader.size = LittleEndian(xmheader.size); xmheader.xmversion = LittleEndianW(xmheader.xmversion); xmheader.orders = LittleEndianW(xmheader.orders); xmheader.restartpos = LittleEndianW(xmheader.restartpos); xmheader.channels = LittleEndianW(xmheader.channels); xmheader.patterns = LittleEndianW(xmheader.patterns); xmheader.instruments = LittleEndianW(xmheader.instruments); xmheader.flags = LittleEndianW(xmheader.flags); xmheader.speed = LittleEndianW(xmheader.speed); xmheader.tempo = LittleEndianW(xmheader.tempo); m_nType = MOD_TYPE_XM; m_nMinPeriod = 27; m_nMaxPeriod = 54784; if (xmheader.orders > MAX_ORDERS) return false; if ((!xmheader.channels) || (xmheader.channels > MAX_BASECHANNELS)) return false; if (xmheader.channels > 32) bMadeWithModPlug = true; m_nRestartPos = xmheader.restartpos; m_nChannels = xmheader.channels; m_nInstruments = bad_min(xmheader.instruments, MAX_INSTRUMENTS - 1); m_nSamples = 0; m_nDefaultSpeed = CLAMP(xmheader.speed, 1, 31); m_nDefaultTempo = CLAMP(xmheader.tempo, 32, 512); if(xmheader.flags & 1) m_dwSongFlags |= SONG_LINEARSLIDES; if(xmheader.flags & 0x1000) m_dwSongFlags |= SONG_EXFILTERRANGE; Order.ReadAsByte(lpStream + 80, xmheader.orders, dwMemLength - 80); dwMemPos = xmheader.size + 60; // set this here already because XMs compressed with BoobieSqueezer will exit the function early SetModFlag(MSF_COMPATIBLE_PLAY, true); if(xmheader.xmversion >= 0x0104) { if (dwMemPos + 8 >= dwMemLength) return true; dwMemPos = ReadXMPatterns(lpStream, dwMemLength, dwMemPos, &xmheader, this); if(dwMemPos == 0) return true; } vector<bool> samples_used; // for removing unused samples modplug::tracker::sampleindex_t unused_samples = 0; // dito // Reading instruments for (modplug::tracker::instrumentindex_t iIns = 1; iIns <= m_nInstruments; iIns++) { XMINSTRUMENTHEADER pih; uint8_t flags[32]; uint32_t samplesize[32]; UINT samplemap[32]; uint16_t nsamples; if (dwMemPos + sizeof(uint32_t) >= dwMemLength) return true; uint32_t ihsize = LittleEndian(*((uint32_t *)(lpStream + dwMemPos))); if (dwMemPos + ihsize > dwMemLength) return true; MemsetZero(pih); memcpy(&pih, lpStream + dwMemPos, bad_min(sizeof(pih), ihsize)); if ((Instruments[iIns] = new modinstrument_t) == nullptr) continue; memcpy(Instruments[iIns], &m_defaultInstrument, sizeof(modinstrument_t)); Instruments[iIns]->nPluginVelocityHandling = PLUGIN_VELOCITYHANDLING_CHANNEL; Instruments[iIns]->nPluginVolumeHandling = PLUGIN_VOLUMEHANDLING_IGNORE; memcpy(Instruments[iIns]->name, pih.name, 22); SpaceToNullStringFixed<22>(Instruments[iIns]->name); memset(&xmsh, 0, sizeof(XMSAMPLEHEADER)); if ((nsamples = pih.samples) > 0) { /* we have samples, so let's read the rest of this instrument the header that is being read here is not the sample header, though, it's rather the instrument settings. */ if (dwMemPos + ihsize >= dwMemLength) return true; memcpy(&xmsh, lpStream + dwMemPos + sizeof(XMINSTRUMENTHEADER), bad_min(ihsize - sizeof(XMINSTRUMENTHEADER), sizeof(XMSAMPLEHEADER))); xmsh.shsize = LittleEndian(xmsh.shsize); if(xmsh.shsize == 0 && bProbablyMadeWithModPlug) bMadeWithModPlug = true; for (int i = 0; i < 24; ++i) { xmsh.venv[i] = LittleEndianW(xmsh.venv[i]); xmsh.penv[i] = LittleEndianW(xmsh.penv[i]); } xmsh.volfade = LittleEndianW(xmsh.volfade); xmsh.midiprogram = LittleEndianW(xmsh.midiprogram); xmsh.pitchwheelrange = LittleEndianW(xmsh.pitchwheelrange); if(xmsh.midichannel != 0 || xmsh.midienabled != 0 || xmsh.midiprogram != 0 || xmsh.mutecomputer != 0 || xmsh.pitchwheelrange != 0) bIsFT2 = true; // definitely not MPT. (or any other tracker) } if (LittleEndian(pih.size)) dwMemPos += LittleEndian(pih.size); else dwMemPos += sizeof(XMINSTRUMENTHEADER); memset(samplemap, 0, sizeof(samplemap)); if (nsamples > 32) return true; UINT newsamples = m_nSamples; for (UINT nmap = 0; nmap < nsamples; nmap++) { UINT n = m_nSamples + nmap + 1; if (n >= MAX_SAMPLES) { n = m_nSamples; while (n > 0) { if (!Samples[n].sample_data) { for (UINT xmapchk=0; xmapchk < nmap; xmapchk++) { if (samplemap[xmapchk] == n) goto alreadymapped; } for (UINT clrs=1; clrs<iIns; clrs++) if (Instruments[clrs]) { modinstrument_t *pks = Instruments[clrs]; for (UINT ks=0; ks<128; ks++) { if (pks->Keyboard[ks] == n) pks->Keyboard[ks] = 0; } } break; } alreadymapped: n--; } #ifndef FASTSOUNDLIB // Damn! Too many samples: look for duplicates if (!n) { if (!unused_samples) { unused_samples = DetectUnusedSamples(samples_used); if (!unused_samples) unused_samples = modplug::tracker::SampleIndexInvalid; } if ((unused_samples) && (unused_samples != modplug::tracker::SampleIndexInvalid)) { for (UINT iext=m_nSamples; iext>=1; iext--) if (!samples_used[iext]) { unused_samples--; samples_used[iext] = true; DestroySample(iext); n = iext; for (UINT mapchk=0; mapchk<nmap; mapchk++) { if (samplemap[mapchk] == n) samplemap[mapchk] = 0; } for (UINT clrs=1; clrs<iIns; clrs++) if (Instruments[clrs]) { modinstrument_t *pks = Instruments[clrs]; for (UINT ks=0; ks<128; ks++) { if (pks->Keyboard[ks] == n) pks->Keyboard[ks] = 0; } } MemsetZero(Samples[n]); break; } } } #endif // FASTSOUNDLIB } if (newsamples < n) newsamples = n; samplemap[nmap] = n; } m_nSamples = newsamples; // Reading Volume Envelope modinstrument_t *pIns = Instruments[iIns]; pIns->midi_program = pih.type; pIns->fadeout = xmsh.volfade; pIns->default_pan = 128; pIns->pitch_pan_center = 5*12; SetDefaultInstrumentValues(pIns); pIns->nPluginVelocityHandling = PLUGIN_VELOCITYHANDLING_CHANNEL; pIns->nPluginVolumeHandling = PLUGIN_VOLUMEHANDLING_IGNORE; if (xmsh.vtype & 1) pIns->volume_envelope.flags |= ENV_ENABLED; if (xmsh.vtype & 2) pIns->volume_envelope.flags |= ENV_SUSTAIN; if (xmsh.vtype & 4) pIns->volume_envelope.flags |= ENV_LOOP; if (xmsh.ptype & 1) pIns->panning_envelope.flags |= ENV_ENABLED; if (xmsh.ptype & 2) pIns->panning_envelope.flags |= ENV_SUSTAIN; if (xmsh.ptype & 4) pIns->panning_envelope.flags |= ENV_LOOP; if (xmsh.vnum > 12) xmsh.vnum = 12; if (xmsh.pnum > 12) xmsh.pnum = 12; pIns->volume_envelope.num_nodes = xmsh.vnum; if (!xmsh.vnum) pIns->volume_envelope.flags &= ~ENV_ENABLED; if (!xmsh.pnum) pIns->panning_envelope.flags &= ~ENV_ENABLED; pIns->panning_envelope.num_nodes = xmsh.pnum; pIns->volume_envelope.sustain_start = pIns->volume_envelope.sustain_end = xmsh.vsustain; if (xmsh.vsustain >= 12) pIns->volume_envelope.flags &= ~ENV_SUSTAIN; pIns->volume_envelope.loop_start = xmsh.vloops; pIns->volume_envelope.loop_end = xmsh.vloope; if (pIns->volume_envelope.loop_end >= 12) pIns->volume_envelope.loop_end = 0; if (pIns->volume_envelope.loop_start >= pIns->volume_envelope.loop_end) pIns->volume_envelope.flags &= ~ENV_LOOP; pIns->panning_envelope.sustain_start = pIns->panning_envelope.sustain_end = xmsh.psustain; if (xmsh.psustain >= 12) pIns->panning_envelope.flags &= ~ENV_SUSTAIN; pIns->panning_envelope.loop_start = xmsh.ploops; pIns->panning_envelope.loop_end = xmsh.ploope; if (pIns->panning_envelope.loop_end >= 12) pIns->panning_envelope.loop_end = 0; if (pIns->panning_envelope.loop_start >= pIns->panning_envelope.loop_end) pIns->panning_envelope.flags &= ~ENV_LOOP; pIns->global_volume = 64; for (UINT ienv=0; ienv<12; ienv++) { pIns->volume_envelope.Ticks[ienv] = (uint16_t)xmsh.venv[ienv*2]; pIns->volume_envelope.Values[ienv] = (uint8_t)xmsh.venv[ienv*2+1]; pIns->panning_envelope.Ticks[ienv] = (uint16_t)xmsh.penv[ienv*2]; pIns->panning_envelope.Values[ienv] = (uint8_t)xmsh.penv[ienv*2+1]; if (ienv) { if (pIns->volume_envelope.Ticks[ienv] < pIns->volume_envelope.Ticks[ienv-1]) { pIns->volume_envelope.Ticks[ienv] &= 0xFF; pIns->volume_envelope.Ticks[ienv] += pIns->volume_envelope.Ticks[ienv-1] & 0xFF00; if (pIns->volume_envelope.Ticks[ienv] < pIns->volume_envelope.Ticks[ienv-1]) pIns->volume_envelope.Ticks[ienv] += 0x100; } if (pIns->panning_envelope.Ticks[ienv] < pIns->panning_envelope.Ticks[ienv-1]) { pIns->panning_envelope.Ticks[ienv] &= 0xFF; pIns->panning_envelope.Ticks[ienv] += pIns->panning_envelope.Ticks[ienv-1] & 0xFF00; if (pIns->panning_envelope.Ticks[ienv] < pIns->panning_envelope.Ticks[ienv-1]) pIns->panning_envelope.Ticks[ienv] += 0x100; } } } for (UINT j=0; j<96; j++) { pIns->NoteMap[j+12] = j+1+12; if (xmsh.snum[j] < nsamples) pIns->Keyboard[j+12] = samplemap[xmsh.snum[j]]; } // Reading samples for (UINT ins=0; ins<nsamples; ins++) { if ((dwMemPos + sizeof(xmss) > dwMemLength) || (dwMemPos + xmsh.shsize > dwMemLength)) return true; memcpy(&xmss, lpStream + dwMemPos, sizeof(xmss)); xmss.samplen = LittleEndian(xmss.samplen); xmss.loopstart = LittleEndian(xmss.loopstart); xmss.looplen = LittleEndian(xmss.looplen); dwMemPos += sizeof(XMSAMPLESTRUCT); // was: dwMemPos += xmsh.shsize; (this fixes IFULOVE.XM) flags[ins] = (xmss.type & 0x10) ? RS_PCM16D : RS_PCM8D; if (xmss.type & 0x20) flags[ins] = (xmss.type & 0x10) ? RS_STPCM16D : RS_STPCM8D; samplesize[ins] = xmss.samplen; if (!samplemap[ins]) continue; if (xmss.type & 0x10) { xmss.looplen >>= 1; xmss.loopstart >>= 1; xmss.samplen >>= 1; } if (xmss.type & 0x20) { xmss.looplen >>= 1; xmss.loopstart >>= 1; xmss.samplen >>= 1; } if (xmss.samplen > MAX_SAMPLE_LENGTH) xmss.samplen = MAX_SAMPLE_LENGTH; if (xmss.loopstart >= xmss.samplen) xmss.type &= ~3; xmss.looplen += xmss.loopstart; if (xmss.looplen > xmss.samplen) xmss.looplen = xmss.samplen; if (!xmss.looplen) xmss.type &= ~3; UINT imapsmp = samplemap[ins]; memcpy(m_szNames[imapsmp], xmss.name, 22); SpaceToNullStringFixed<22>(m_szNames[imapsmp]); modsample_t *pSmp = &Samples[imapsmp]; pSmp->length = (xmss.samplen > MAX_SAMPLE_LENGTH) ? MAX_SAMPLE_LENGTH : xmss.samplen; pSmp->loop_start = xmss.loopstart; pSmp->loop_end = xmss.looplen; if (pSmp->loop_end > pSmp->length) pSmp->loop_end = pSmp->length; if (pSmp->loop_start >= pSmp->loop_end) { pSmp->loop_start = pSmp->loop_end = 0; } if (xmss.type & 3) pSmp->flags |= CHN_LOOP; if (xmss.type & 2) pSmp->flags |= CHN_PINGPONGLOOP; pSmp->default_volume = xmss.vol << 2; if (pSmp->default_volume > 256) pSmp->default_volume = 256; pSmp->global_volume = 64; if ((xmss.res == 0xAD) && (!(xmss.type & 0x30))) { flags[ins] = RS_ADPCM4; samplesize[ins] = (samplesize[ins]+1)/2 + 16; } pSmp->nFineTune = xmss.finetune; pSmp->RelativeTone = (int)xmss.relnote; pSmp->default_pan = xmss.pan; pSmp->flags |= CHN_PANNING; pSmp->vibrato_type = xmsh.vibtype; pSmp->vibrato_sweep = xmsh.vibsweep; pSmp->vibrato_depth = xmsh.vibdepth; pSmp->vibrato_rate = xmsh.vibrate; memcpy(pSmp->legacy_filename, xmss.name, 22); SpaceToNullStringFixed<21>(pSmp->legacy_filename); if ((xmss.type & 3) == 3) // MPT 1.09 and maybe newer / older versions set both flags for bidi loops bProbablyMPT109 = true; }
bool module_renderer::SaveITProject(LPCSTR lpszFileName) //------------------------------------------------- { // Check song type if(!(m_dwSongFlags & SONG_ITPROJECT)) return false; UINT i,j = 0; for(i = 0 ; i < m_nInstruments ; i++) { if(m_szInstrumentPath[i][0] != '\0' || !Instruments[i+1]) j++; } if(m_nInstruments && j != m_nInstruments) return false; // Open file FILE *f; if((!lpszFileName) || ((f = fopen(lpszFileName, "wb")) == NULL)) return false; // File ID uint32_t id = ITP_FILE_ID; fwrite(&id, 1, sizeof(id), f); id = ITP_VERSION; fwrite(&id, 1, sizeof(id), f); // Song name // name string length id = 27; fwrite(&id, 1, sizeof(id), f); // song name char namebuf[27]; copy_with_padding(namebuf, 27, this->song_name); fwrite(namebuf, 1, 27, f); // Song comments // comment string length id = m_lpszSongComments ? strlen(m_lpszSongComments)+1 : 0; fwrite(&id, 1, sizeof(id), f); // comment string if(m_lpszSongComments) fwrite(&m_lpszSongComments[0], 1, strlen(m_lpszSongComments)+1, f); // Song global config id = (m_dwSongFlags & SONG_FILE_FLAGS); fwrite(&id, 1, sizeof(id), f); id = m_nDefaultGlobalVolume; fwrite(&id, 1, sizeof(id), f); id = m_nSamplePreAmp; fwrite(&id, 1, sizeof(id), f); id = m_nDefaultSpeed; fwrite(&id, 1, sizeof(id), f); id = m_nDefaultTempo; fwrite(&id, 1, sizeof(id), f); // Song channels data // number of channels id = m_nChannels; fwrite(&id, 1, sizeof(id), f); // channel name string length id = MAX_CHANNELNAME; fwrite(&id, 1, sizeof(id), f); // channel config data for(i=0; i<m_nChannels; i++){ id = ChnSettings[i].nPan; fwrite(&id, 1, sizeof(id), f); id = ChnSettings[i].dwFlags; fwrite(&id, 1, sizeof(id), f); id = ChnSettings[i].nVolume; fwrite(&id, 1, sizeof(id), f); fwrite(&ChnSettings[i].szName[0], 1, MAX_CHANNELNAME, f); } // Song mix plugins // mix plugins data length id = SaveMixPlugins(NULL, TRUE); fwrite(&id, 1, sizeof(id), f); // mix plugins data SaveMixPlugins(f, FALSE); // Song midi config // midi cfg data length id = sizeof(MODMIDICFG); fwrite(&id, 1, sizeof(id), f); // midi cfg fwrite(&m_MidiCfg, 1, sizeof(MODMIDICFG), f); // Song Instruments // number of instruments id = m_nInstruments; fwrite(&id, 1, sizeof(id), f); // path name string length id = _MAX_PATH; fwrite(&id, 1, sizeof(id), f); // instruments' path for(i=0; i<m_nInstruments; i++) fwrite(&m_szInstrumentPath[i][0], 1, _MAX_PATH, f); // Song Orders // order array size id = Order.size(); fwrite(&id, 1, sizeof(id), f); // order array Order.WriteAsByte(f, id); // Song Patterns // number of patterns id = MAX_PATTERNS; fwrite(&id, 1, sizeof(id), f); // number of pattern name strings modplug::tracker::patternindex_t numNamedPats = Patterns.GetNumNamedPatterns(); numNamedPats = bad_min(numNamedPats, MAX_PATTERNS); id = numNamedPats; fwrite(&id, 1, sizeof(id), f); // length of a pattern name string id = MAX_PATTERNNAME; fwrite(&id, 1, sizeof(id), f); // pattern name string for(modplug::tracker::patternindex_t nPat = 0; nPat < numNamedPats; nPat++) { char name[MAX_PATTERNNAME]; MemsetZero(name); Patterns[nPat].GetName(name, MAX_PATTERNNAME); fwrite(name, 1, MAX_PATTERNNAME, f); } // modcommand data length id = sizeof(modplug::tracker::modevent_t); fwrite(&id, 1, sizeof(id), f); // patterns data content for(UINT npat=0; npat<MAX_PATTERNS; npat++){ // pattern size (number of rows) id = Patterns[npat] ? Patterns[npat].GetNumRows() : 0; fwrite(&id, 1, sizeof(id), f); // pattern data if(Patterns[npat] && Patterns[npat].GetNumRows()) Patterns[npat].WriteITPdata(f); //fwrite(Patterns[npat], 1, m_nChannels * Patterns[npat].GetNumRows() * sizeof(modplug::tracker::modcommand_t), f); } // Song lonely (instrument-less) samples // Write original number of samples id = m_nSamples; fwrite(&id, 1, sizeof(id), f); vector<bool> sampleUsed(m_nSamples, false); // Mark samples used in instruments for(i=0; i<m_nInstruments; i++) { if(Instruments[i + 1] != nullptr) { modinstrument_t *p = Instruments[i + 1]; for(j = 0; j < 128; j++) { if(p->Keyboard[j] > 0 && p->Keyboard[j] <= m_nSamples) sampleUsed[p->Keyboard[j] - 1] = true; } } } // Count samples not used in any instrument i = 0; for(j = 1; j <= m_nSamples; j++) if(!sampleUsed[j - 1] && Samples[j].sample_data) i++; id = i; fwrite(&id, 1, sizeof(id), f); // Write samples not used in any instrument (help, this looks like duplicate code!) ITSAMPLESTRUCT itss; for(UINT nsmp=1; nsmp<=m_nSamples; nsmp++) { if(!sampleUsed[nsmp - 1] && Samples[nsmp].sample_data) { modsample_t *psmp = &Samples[nsmp]; memset(&itss, 0, sizeof(itss)); memcpy(itss.filename, psmp->legacy_filename, 12); memcpy(itss.name, m_szNames[nsmp], 26); itss.id = LittleEndian(IT_IMPS); itss.gvl = (uint8_t)psmp->global_volume; itss.flags = 0x00; if(psmp->flags & CHN_LOOP) itss.flags |= 0x10; if(psmp->flags & CHN_SUSTAINLOOP) itss.flags |= 0x20; if(psmp->flags & CHN_PINGPONGLOOP) itss.flags |= 0x40; if(psmp->flags & CHN_PINGPONGSUSTAIN) itss.flags |= 0x80; itss.C5Speed = psmp->c5_samplerate; if (!itss.C5Speed) itss.C5Speed = 8363; itss.length = psmp->length; itss.loopbegin = psmp->loop_start; itss.loopend = psmp->loop_end; itss.susloopbegin = psmp->sustain_start; itss.susloopend = psmp->sustain_end; itss.vol = (uint8_t)(psmp->default_volume >> 2); itss.dfp = (uint8_t)(psmp->default_pan >> 2); itss.vit = autovibxm2it[psmp->vibrato_type & 7]; itss.vis = bad_min(psmp->vibrato_rate, 64); itss.vid = bad_min(psmp->vibrato_depth, 32); itss.vir = bad_min(psmp->vibrato_sweep, 255); //(psmp->vibrato_sweep < 64) ? psmp->vibrato_sweep * 4 : 255; if (psmp->flags & CHN_PANNING) itss.dfp |= 0x80; if ((psmp->sample_data) && (psmp->length)) itss.cvt = 0x01; UINT flags = RS_PCM8S; if(psmp->flags & CHN_STEREO) { flags = RS_STPCM8S; itss.flags |= 0x04; } if(psmp->flags & CHN_16BIT) { itss.flags |= 0x02; flags = (psmp->flags & CHN_STEREO) ? RS_STPCM16S : RS_PCM16S; } id = nsmp; fwrite(&id, 1, sizeof(id), f); itss.samplepointer = NULL; fwrite(&itss, 1, sizeof(ITSAMPLESTRUCT), f); id = WriteSample(NULL, psmp, flags); fwrite(&id, 1, sizeof(id), f); WriteSample(f, psmp, flags); } }
void CViewComments::OnUpdate(CView *pSender, LPARAM lHint, CObject *) //------------------------------------------------------------------- { //CHAR s[256], stmp[256]; CHAR s[512], stmp[256]; //rewbs.fix3082 CModDoc *pModDoc = GetDocument(); LV_COLUMN lvc; LV_ITEM lvi, lvi2; if ((!pModDoc) || (pSender == this) || (!(m_ItemList.m_hWnd))) return; if (lHint & HINT_MPTOPTIONS) { m_ToolBar.UpdateStyle(); lHint &= ~HINT_MPTOPTIONS; } lHint &= (HINT_MODTYPE |HINT_SMPNAMES|HINT_SAMPLEINFO |HINT_INSNAMES|HINT_INSTRUMENT /*|HINT_PATNAMES|HINT_PATTERNROW*/); // pattern stuff currently unused if (!lHint) return; const CSoundFile &sndFile = pModDoc->GetrSoundFile(); m_ToolBar.ChangeBitmap(IDC_LIST_INSTRUMENTS, sndFile.GetNumInstruments() ? IMAGE_INSTRUMENTS : IMAGE_INSTRMUTE); m_ItemList.SetRedraw(FALSE); // Add sample headers if ((m_nListId != m_nCurrentListId) || (lHint & HINT_MODTYPE)) { UINT ichk = 0; m_ItemList.DeleteAllItems(); while ((m_ItemList.DeleteColumn(0)) && (ichk < 25)) ichk++; m_nCurrentListId = m_nListId; // Add Sample Headers if (m_nCurrentListId == IDC_LIST_SAMPLES) { UINT nCol = 0; for (UINT iSmp=0; iSmp<SMPLIST_COLUMNS; iSmp++) { MemsetZero(lvc); lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; lvc.fmt = (iSmp) ? LVCFMT_RIGHT : LVCFMT_LEFT; lvc.pszText = (LPTSTR)gSampleHeaders[iSmp].pszName; lvc.cx = gSampleHeaders[iSmp].cx; lvc.iSubItem = iSmp; m_ItemList.InsertColumn(nCol, &lvc); nCol++; } } else // Add Instrument Headers if (m_nCurrentListId == IDC_LIST_INSTRUMENTS) { UINT nCol = 0; for (UINT i=0; i<INSLIST_COLUMNS; i++) { MemsetZero(lvc); lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; lvc.fmt = (i) ? LVCFMT_RIGHT : LVCFMT_LEFT; lvc.pszText = (LPTSTR)gInstrumentHeaders[i].pszName; lvc.cx = gInstrumentHeaders[i].cx; lvc.iSubItem = i; m_ItemList.InsertColumn(nCol, &lvc); nCol++; } } else lHint |= HINT_MODTYPE; } // Add Items UINT nCount = m_ItemList.GetItemCount(); // Add Samples if ((m_nCurrentListId == IDC_LIST_SAMPLES) && (lHint & (HINT_MODTYPE|HINT_SMPNAMES|HINT_SAMPLEINFO))) { SAMPLEINDEX nMax = static_cast<SAMPLEINDEX>(nCount); if (nMax < sndFile.GetNumSamples()) nMax = sndFile.GetNumSamples(); for (SAMPLEINDEX iSmp = 0; iSmp < nMax; iSmp++) { if (iSmp < sndFile.GetNumSamples()) { UINT nCol = 0; for (UINT iCol=0; iCol<SMPLIST_COLUMNS; iCol++) { const ModSample &sample = sndFile.GetSample(iSmp + 1); s[0] = 0; switch(iCol) { case SMPLIST_SAMPLENAME: mpt::String::Copy(s, sndFile.m_szNames[iSmp + 1]); break; case SMPLIST_SAMPLENO: wsprintf(s, "%02d", iSmp + 1); break; case SMPLIST_SIZE: if (sample.nLength) { if(sample.GetSampleSizeInBytes() >= 1024) wsprintf(s, "%d KB", sample.GetSampleSizeInBytes() >> 10); else wsprintf(s, "%d B", sample.GetSampleSizeInBytes()); } break; case SMPLIST_TYPE: if(sample.nLength) { wsprintf(s, "%d Bit", sample.GetElementarySampleSize() * 8); } break; case SMPLIST_INSTR: if (sndFile.GetNumInstruments()) { bool first = true; for (INSTRUMENTINDEX i = 1; i <= sndFile.GetNumInstruments(); i++) { if (sndFile.IsSampleReferencedByInstrument(iSmp + 1, i)) { if (!first) strcat(s, ","); first = false; wsprintf(stmp, "%d", i); strcat(s, stmp); if (strlen(s) > sizeof(s) - 10) { strcat(s, "..."); break; } } } } break; case SMPLIST_MIDDLEC: if (sample.nLength) { wsprintf(s, "%d Hz", sample.GetSampleRate(sndFile.GetType())); } break; case SMPLIST_FILENAME: memcpy(s, sample.filename, sizeof(sample.filename)); s[CountOf(sample.filename)] = 0; break; } lvi.mask = LVIF_TEXT; lvi.iItem = iSmp; lvi.iSubItem = nCol; lvi.pszText = (LPTSTR)s; if ((iCol) || (iSmp < nCount)) { bool bOk = true; if (iSmp < nCount) { lvi2 = lvi; lvi2.pszText = (LPTSTR)stmp; lvi2.cchTextMax = sizeof(stmp); stmp[0] = 0; m_ItemList.GetItem(&lvi2); if (!strcmp(s, stmp)) bOk = false; } if (bOk) m_ItemList.SetItem(&lvi); } else { m_ItemList.InsertItem(&lvi); } nCol++; } } else
bool CWaveDevice::InternalOpen() //------------------------------ { MPT_TRACE(); if(m_Settings.InputChannels > 0) { return false; } WAVEFORMATEXTENSIBLE wfext; if(!FillWaveFormatExtensible(wfext, m_Settings)) { return false; } WAVEFORMATEX *pwfx = &wfext.Format; UINT nWaveDev = GetDeviceIndex(); nWaveDev = (nWaveDev > 0) ? nWaveDev - 1 : WAVE_MAPPER; m_ThreadWakeupEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if(m_ThreadWakeupEvent == INVALID_HANDLE_VALUE) { InternalClose(); return false; } m_Failed = false; m_DriverBugs = 0; m_hWaveOut = NULL; if(waveOutOpen(&m_hWaveOut, nWaveDev, pwfx, (DWORD_PTR)WaveOutCallBack, (DWORD_PTR)this, CALLBACK_FUNCTION | (m_Settings.ExclusiveMode ? WAVE_FORMAT_DIRECT : 0)) != MMSYSERR_NOERROR) { InternalClose(); return false; } if(waveOutPause(m_hWaveOut) != MMSYSERR_NOERROR) { InternalClose(); return false; } m_nWaveBufferSize = Util::Round<int32>(m_Settings.UpdateInterval * pwfx->nAvgBytesPerSec); m_nWaveBufferSize = Util::AlignUp<uint32>(m_nWaveBufferSize, pwfx->nBlockAlign); m_nWaveBufferSize = mpt::clamp(m_nWaveBufferSize, static_cast<uint32>(WAVEOUT_MINBUFFERFRAMECOUNT * pwfx->nBlockAlign), static_cast<uint32>(Util::AlignDown<uint32>(WAVEOUT_MAXBUFFERSIZE, pwfx->nBlockAlign))); std::size_t numBuffers = Util::Round<int32>(m_Settings.Latency * pwfx->nAvgBytesPerSec / m_nWaveBufferSize); numBuffers = mpt::clamp(numBuffers, WAVEOUT_MINBUFFERS, WAVEOUT_MAXBUFFERS); m_nPreparedHeaders = 0; m_WaveBuffers.resize(numBuffers); m_WaveBuffersData.resize(numBuffers); for(std::size_t buf = 0; buf < numBuffers; ++buf) { MemsetZero(m_WaveBuffers[buf]); m_WaveBuffersData[buf].resize(m_nWaveBufferSize); m_WaveBuffers[buf].dwFlags = 0; m_WaveBuffers[buf].lpData = &m_WaveBuffersData[buf][0]; m_WaveBuffers[buf].dwBufferLength = m_nWaveBufferSize; if(waveOutPrepareHeader(m_hWaveOut, &m_WaveBuffers[buf], sizeof(WAVEHDR)) != MMSYSERR_NOERROR) { break; } m_WaveBuffers[buf].dwFlags |= WHDR_DONE; m_nPreparedHeaders++; } if(!m_nPreparedHeaders) { InternalClose(); return false; } m_nBuffersPending = 0; m_nWriteBuffer = 0; m_nDoneBuffer = 0; { MPT_LOCK_GUARD<mpt::mutex> guard(m_PositionWraparoundMutex); MemsetZero(m_PositionLast); m_PositionWrappedCount = 0; } SetWakeupEvent(m_ThreadWakeupEvent); SetWakeupInterval(m_nWaveBufferSize * 1.0 / m_Settings.GetBytesPerSecond()); m_Flags.NeedsClippedFloat = GetSysInfo().WindowsVersion.IsAtLeast(mpt::Windows::Version::WinVista); return true; }
void CSelectPluginDlg::OnOK() //--------------------------- { if(m_pPlugin==nullptr) { CDialog::OnOK(); return; } bool changed = false; CVstPluginManager *pManager = theApp.GetPluginManager(); VSTPluginLib *pNewPlug = GetSelectedPlugin(); VSTPluginLib *pFactory = nullptr; IMixPlugin *pCurrentPlugin = nullptr; if (m_pPlugin) pCurrentPlugin = m_pPlugin->pMixPlugin; if ((pManager) && (pManager->IsValidPlugin(pNewPlug))) pFactory = pNewPlug; if (pFactory) { // Plugin selected if ((!pCurrentPlugin) || &pCurrentPlugin->GetPluginFactory() != pFactory) { CriticalSection cs; // Destroy old plugin, if there was one. m_pPlugin->Destroy(); // Initialize plugin info MemsetZero(m_pPlugin->Info); m_pPlugin->Info.dwPluginId1 = pFactory->pluginId1; m_pPlugin->Info.dwPluginId2 = pFactory->pluginId2; m_pPlugin->editorX = m_pPlugin->editorY = int32_min; #ifndef NO_VST if(m_pPlugin->Info.dwPluginId1 == kEffectMagic) { switch(m_pPlugin->Info.dwPluginId2) { // Enable drymix by default for these known plugins case CCONST('S', 'c', 'o', 'p'): m_pPlugin->SetWetMix(); break; } } #endif // NO_VST mpt::String::Copy(m_pPlugin->Info.szName, pFactory->libraryName.ToLocale().c_str()); mpt::String::Copy(m_pPlugin->Info.szLibraryName, pFactory->libraryName.ToUTF8().c_str()); cs.Leave(); // Now, create the new plugin if(pManager && m_pModDoc) { pManager->CreateMixPlugin(*m_pPlugin, m_pModDoc->GetrSoundFile()); if (m_pPlugin->pMixPlugin) { IMixPlugin *p = m_pPlugin->pMixPlugin; const CString name = p->GetDefaultEffectName(); if(!name.IsEmpty()) { mpt::String::Copy(m_pPlugin->Info.szName, name.GetString()); } // Check if plugin slot is already assigned to any instrument, and if not, create one. if(p->IsInstrument() && m_pModDoc->HasInstrumentForPlugin(m_nPlugSlot) == INSTRUMENTINDEX_INVALID) { m_pModDoc->InsertInstrumentForPlugin(m_nPlugSlot); } } else { MemsetZero(m_pPlugin->Info); } } changed = true; } } else if(m_pPlugin->IsValidPlugin()) { // No effect CriticalSection cs; m_pPlugin->Destroy(); // Clear plugin info MemsetZero(m_pPlugin->Info); changed = true; } //remember window size: SaveWindowPos(); if(changed) { if(m_pPlugin->Info.dwPluginId2) TrackerSettings::Instance().gnPlugWindowLast = m_pPlugin->Info.dwPluginId2; if(m_pModDoc) { m_pModDoc->UpdateAllViews(nullptr, PluginHint(static_cast<PLUGINDEX>(m_nPlugSlot + 1)).Info().Names()); } CDialog::OnOK(); } else { CDialog::OnCancel(); } }
int64 CWaveDevice::InternalGetStreamPositionFrames() const //--------------------------------------------------------- { // Apparently, at least with Windows XP, TIME_SAMPLES wraps aroud at 0x7FFFFFF (see // http://www.tech-archive.net/Archive/Development/microsoft.public.win32.programmer.mmedia/2005-02/0070.html // ). // We may also, additionally, default to TIME_BYTES which would wraparound the earliest. // We could thereby try to avoid any potential wraparound inside the driver on older // Windows versions, which would be, once converted into other units, really // difficult to detect or handle. static const UINT timeType = TIME_SAMPLES; // shpuld work for sane systems //static const std::size_t valid_bits = 32; // shpuld work for sane systems //static const UINT timeType = TIME_BYTES; // safest static const std::size_t valid_bits = 27; // safe for WinXP TIME_SAMPLES static const uint32 valid_mask = static_cast<uint32>((uint64(1) << valid_bits) - 1u); static const uint32 valid_watermark = static_cast<uint32>(uint64(1) << (valid_bits - 1u)); // half the valid range in order to be able to catch backwards fluctuations MMTIME mmtime; MemsetZero(mmtime); mmtime.wType = timeType; if(waveOutGetPosition(m_hWaveOut, &mmtime, sizeof(mmtime)) != MMSYSERR_NOERROR) { return 0; } if(mmtime.wType != TIME_MS && mmtime.wType != TIME_BYTES && mmtime.wType != TIME_SAMPLES) { // unsupported time format return 0; } int64 offset = 0; { // handle wraparound MPT_LOCK_GUARD<mpt::mutex> guard(m_PositionWraparoundMutex); if(!m_PositionLast.wType) { // first call m_PositionWrappedCount = 0; } else if(mmtime.wType != m_PositionLast.wType) { // what? value type changed, do not try handling that for now. m_PositionWrappedCount = 0; } else { DWORD oldval = 0; DWORD curval = 0; switch(mmtime.wType) { case TIME_MS: oldval = m_PositionLast.u.ms; curval = mmtime.u.ms; break; case TIME_BYTES: oldval = m_PositionLast.u.cb; curval = mmtime.u.cb; break; case TIME_SAMPLES: oldval = m_PositionLast.u.sample; curval = mmtime.u.sample; break; } oldval &= valid_mask; curval &= valid_mask; if(((curval - oldval) & valid_mask) >= valid_watermark) // guard against driver problems resulting in time jumping backwards for short periods of time. BEWARE of integer wraparound when refactoring { curval = oldval; } switch(mmtime.wType) { case TIME_MS: mmtime.u.ms = curval; break; case TIME_BYTES: mmtime.u.cb = curval; break; case TIME_SAMPLES: mmtime.u.sample = curval; break; } if((curval ^ oldval) & valid_watermark) // MSB flipped { if(!(curval & valid_watermark)) // actually wrapped { m_PositionWrappedCount += 1; } } } m_PositionLast = mmtime; offset = (static_cast<uint64>(m_PositionWrappedCount) << valid_bits); } int64 result = 0; switch(mmtime.wType) { case TIME_MS: result += (static_cast<int64>(mmtime.u.ms & valid_mask) + offset) * m_Settings.GetBytesPerSecond() / (1000 * m_Settings.GetBytesPerFrame()); break; case TIME_BYTES: result += (static_cast<int64>(mmtime.u.cb & valid_mask) + offset) / m_Settings.GetBytesPerFrame(); break; case TIME_SAMPLES: result += (static_cast<int64>(mmtime.u.sample & valid_mask) + offset); break; } return result; }