void CEffectVis::OnSize(UINT nType, int cx, int cy) { MPT_UNREFERENCED_PARAMETER(nType); MPT_UNREFERENCED_PARAMETER(cx); MPT_UNREFERENCED_PARAMETER(cy); GetClientRect(&m_rcFullWin); m_rcDraw.SetRect( m_rcFullWin.left, m_rcFullWin.top, m_rcFullWin.right, m_rcFullWin.bottom - m_marginBottom); #define INFOWIDTH 200 #define ACTIONLISTWIDTH 150 #define COMMANDLISTWIDTH 150 if (IsWindow(m_edVisStatus.m_hWnd)) m_edVisStatus.SetWindowPos(this, m_rcFullWin.left, m_rcDraw.bottom, m_rcFullWin.right-COMMANDLISTWIDTH-ACTIONLISTWIDTH, m_rcFullWin.bottom-m_rcDraw.bottom, SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_SHOWWINDOW|SWP_NOZORDER); if (IsWindow(m_cmbActionList)) m_cmbActionList.SetWindowPos(this, m_rcFullWin.right-COMMANDLISTWIDTH-ACTIONLISTWIDTH, m_rcDraw.bottom, ACTIONLISTWIDTH, m_rcFullWin.bottom-m_rcDraw.bottom, SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_SHOWWINDOW|SWP_NOZORDER); if (IsWindow(m_cmbEffectList)) m_cmbEffectList.SetWindowPos(this, m_rcFullWin.right-COMMANDLISTWIDTH, m_rcDraw.bottom, COMMANDLISTWIDTH, m_rcFullWin.bottom-m_rcDraw.bottom, SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_SHOWWINDOW|SWP_NOZORDER); if(m_nRows) m_pixelsPerRow = (float)(m_rcDraw.Width() - m_innerBorder * 2) / (float)m_nRows; else m_pixelsPerRow = 1; m_pixelsPerFXParam = (float)(m_rcDraw.Height())/(float)0xFF; m_pixelsPerPCParam = (float)(m_rcDraw.Height())/(float)ModCommand::maxColumnValue; m_forceRedraw = true; InvalidateRect(NULL, FALSE); //redraw everything }
FLAC__StreamEncoderWriteStatus WriteCallback(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame) { MPT_UNREFERENCED_PARAMETER(encoder); MPT_UNREFERENCED_PARAMETER(samples); MPT_UNREFERENCED_PARAMETER(current_frame); f.write(reinterpret_cast<const char*>(buffer), bytes); if(!f) return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; return FLAC__STREAM_ENCODER_WRITE_STATUS_OK; }
FLAC__StreamEncoderSeekStatus SeekCallback(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset) { MPT_UNREFERENCED_PARAMETER(encoder); f.seekp(absolute_byte_offset); if(!f) return FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR; return FLAC__STREAM_ENCODER_SEEK_STATUS_OK; }
// Translate instrument properties between two given formats. void ModInstrument::Convert(MODTYPE fromType, MODTYPE toType) //----------------------------------------------------------- { MPT_UNREFERENCED_PARAMETER(fromType); if(toType & MOD_TYPE_XM) { ResetNoteMap(); PitchEnv.dwFlags.reset(ENV_ENABLED | ENV_FILTER); dwFlags.reset(INS_SETPANNING); SetCutoff(GetCutoff(), false); SetResonance(GetResonance(), false); nFilterMode = FLTMODE_UNCHANGED; nCutSwing = nPanSwing = nResSwing = nVolSwing = 0; nPPC = NOTE_MIDDLEC - 1; nPPS = 0; nNNA = NNA_NOTECUT; nDCT = DCT_NONE; nDNA = DNA_NOTECUT; if(nMidiChannel == MidiMappedChannel) { nMidiChannel = 1; } // FT2 only has signed Pitch Wheel Depth, and it's limited to 0...36 (in the GUI, at least. As you would expect it from FT2, this value is actually not sanitized on load). midiPWD = static_cast<int8>(abs(midiPWD)); Limit(midiPWD, int8(0), int8(36)); nGlobalVol = 64; nPan = 128; LimitMax(nFadeOut, 32767u); } VolEnv.Convert(fromType, toType); PanEnv.Convert(fromType, toType); PitchEnv.Convert(fromType, toType); // Limit fadeout length for IT / MPT if(toType & (MOD_TYPE_IT | MOD_TYPE_MPT)) { LimitMax(nFadeOut, 8192u); } // MPT-specific features - remove instrument tunings, Pitch/Tempo Lock, cutoff / resonance swing and filter mode for other formats if(!(toType & MOD_TYPE_MPT)) { SetTuning(nullptr); wPitchToTempoLock = 0; nCutSwing = nResSwing = 0; nFilterMode = FLTMODE_UNCHANGED; } }
FLAC__StreamEncoderTellStatus TellCallback(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset) { MPT_UNREFERENCED_PARAMETER(encoder); if(absolute_byte_offset) { *absolute_byte_offset = f.tellp(); } if(!f) return FLAC__STREAM_ENCODER_TELL_STATUS_ERROR; return FLAC__STREAM_ENCODER_TELL_STATUS_OK; }
void CEffectVis::ShowVis(CDC * pDC, CRect rectBorder) //--------------------------------------------------- { MPT_UNREFERENCED_PARAMETER(rectBorder); if (m_forceRedraw) { m_forceRedraw = false; // if we already have a memory dc, destroy it (this occurs for a re-size) if (m_dcGrid.GetSafeHdc()) { m_dcGrid.SelectObject(m_pbOldGrid) ; m_dcGrid.DeleteDC() ; m_dcNodes.SelectObject(m_pbOldNodes) ; m_dcNodes.DeleteDC() ; m_dcPlayPos.SelectObject(m_pbOldPlayPos) ; m_dcPlayPos.DeleteDC() ; m_bPlayPos.DeleteObject() ; m_bGrid.DeleteObject() ; m_bNodes.DeleteObject() ; } // create a memory based dc for drawing the grid m_dcGrid.CreateCompatibleDC(pDC); m_bGrid.CreateCompatibleBitmap(pDC, m_rcDraw.Width(), m_rcDraw.Height()); m_pbOldGrid = m_dcGrid.SelectObject(&m_bGrid); // create a memory based dc for drawing the nodes m_dcNodes.CreateCompatibleDC(pDC); m_bNodes.CreateCompatibleBitmap(pDC, m_rcDraw.Width(), m_rcDraw.Height()); m_pbOldNodes = m_dcNodes.SelectObject(&m_bNodes); // create a memory based dc for drawing the nodes m_dcPlayPos.CreateCompatibleDC(pDC); m_bPlayPos.CreateCompatibleBitmap(pDC, m_rcDraw.Width(), m_rcDraw.Height()); m_pbOldPlayPos = m_dcPlayPos.SelectObject(&m_bPlayPos); SetPlayCursor(m_nPattern, m_nOldPlayPos); DrawGrid(); DrawNodes(); } // display the new image, combining the nodes with the grid ShowVisImage(pDC); }
// Returns macro description including plugin parameter / MIDI CC information CString MIDIMacroConfig::GetParameteredMacroName(uint32 macroIndex, IMixPlugin *plugin) const //------------------------------------------------------------------------------------------- { const parameteredMacroType macroType = GetParameteredMacroType(macroIndex); switch(macroType) { case sfx_plug: { const int param = MacroToPlugParam(macroIndex); CString formattedName; formattedName.Format(_T("Param %u"), param); #ifndef NO_PLUGINS if(plugin != nullptr) { CString paramName = plugin->GetParamName(param); if(!paramName.IsEmpty()) { formattedName += _T(" (") + paramName + _T(")"); } } else #else MPT_UNREFERENCED_PARAMETER(plugin); #endif // NO_PLUGINS { formattedName += _T(" (N/A)"); } return formattedName; } case sfx_cc: { CString formattedCC; formattedCC.Format(_T("MIDI CC %u"), MacroToMidiCC(macroIndex)); return formattedCC; } default: return GetParameteredMacroName(macroType); } }
void ModInstrument::Sanitize(MODTYPE modType) { LimitMax(nFadeOut, 65536u); LimitMax(nGlobalVol, 64u); LimitMax(nPan, 256u); LimitMax(wMidiBank, uint16(16384)); LimitMax(nMidiProgram, uint8(128)); LimitMax(nMidiChannel, uint8(17)); if(nNNA > NNA_NOTEFADE) nNNA = NNA_NOTECUT; if(nDCT > DCT_PLUGIN) nDCT = DCT_NONE; if(nDNA > DNA_NOTEFADE) nDNA = DNA_NOTECUT; LimitMax(nPanSwing, uint8(64)); LimitMax(nVolSwing, uint8(100)); Limit(nPPS, int8(-32), int8(32)); LimitMax(nCutSwing, uint8(64)); LimitMax(nResSwing, uint8(64)); #ifdef MODPLUG_TRACKER MPT_UNREFERENCED_PARAMETER(modType); const uint8 range = ENVELOPE_MAX; #else const uint8 range = modType == MOD_TYPE_AMS2 ? uint8_max : ENVELOPE_MAX; #endif VolEnv.Sanitize(); PanEnv.Sanitize(); PitchEnv.Sanitize(range); for(size_t i = 0; i < CountOf(NoteMap); i++) { if(NoteMap[i] < NOTE_MIN || NoteMap[i] > NOTE_MAX) NoteMap[i] = static_cast<uint8>(i + NOTE_MIN); } }
// Translate instrument properties between two given formats. void ModInstrument::Convert(MODTYPE fromType, MODTYPE toType) { MPT_UNREFERENCED_PARAMETER(fromType); if(toType & MOD_TYPE_XM) { ResetNoteMap(); PitchEnv.dwFlags.reset(ENV_ENABLED | ENV_FILTER); dwFlags.reset(INS_SETPANNING); SetCutoff(GetCutoff(), false); SetResonance(GetResonance(), false); nFilterMode = FLTMODE_UNCHANGED; nCutSwing = nPanSwing = nResSwing = nVolSwing = 0; nPPC = NOTE_MIDDLEC - 1; nPPS = 0; nNNA = NNA_NOTECUT; nDCT = DCT_NONE; nDNA = DNA_NOTECUT; if(nMidiChannel == MidiMappedChannel) { nMidiChannel = 1; } // FT2 only has unsigned Pitch Wheel Depth, and it's limited to 0...36 (in the GUI, at least. As you would expect it from FT2, this value is actually not sanitized on load). midiPWD = static_cast<int8>(mpt::abs(midiPWD)); Limit(midiPWD, int8(0), int8(36)); nGlobalVol = 64; nPan = 128; LimitMax(nFadeOut, 32767u); } VolEnv.Convert(fromType, toType); PanEnv.Convert(fromType, toType); PitchEnv.Convert(fromType, toType); if(fromType == MOD_TYPE_XM && (toType & (MOD_TYPE_IT | MOD_TYPE_MPT))) { if(!VolEnv.dwFlags[ENV_ENABLED]) { // Note-Off with no envelope cuts the note immediately in XM VolEnv.resize(2); VolEnv[0].tick = 0; VolEnv[0].value = ENVELOPE_MAX; VolEnv[1].tick = 1; VolEnv[1].value = ENVELOPE_MIN; VolEnv.dwFlags.set(ENV_ENABLED | ENV_SUSTAIN); VolEnv.dwFlags.reset(ENV_LOOP); VolEnv.nSustainStart = VolEnv.nSustainEnd = 0; } } // Limit fadeout length for IT if(toType & MOD_TYPE_IT) { LimitMax(nFadeOut, 8192u); } // MPT-specific features - remove instrument tunings, Pitch/Tempo Lock, cutoff / resonance swing and filter mode for other formats if(!(toType & MOD_TYPE_MPT)) { SetTuning(nullptr); pitchToTempoLock.Set(0); nCutSwing = nResSwing = 0; nFilterMode = FLTMODE_UNCHANGED; nVolRampUp = 0; } }
bool CSoundFile::ReadOpusSample(SAMPLEINDEX sample, FileReader &file) { file.Rewind(); #if defined(MPT_WITH_OPUSFILE) int rate = 0; int channels = 0; std::vector<int16> raw_sample_data; FileReader initial = file.GetChunk(65536); // 512 is recommended by libopusfile if(op_test(NULL, initial.GetRawData<unsigned char>(), initial.GetLength()) != 0) { return false; } OggOpusFile *of = op_open_memory(file.GetRawData<unsigned char>(), file.GetLength(), NULL); if(!of) { return false; } rate = 48000; channels = op_channel_count(of, -1); if(rate <= 0 || channels <= 0) { op_free(of); of = NULL; return false; } if(channels > 2 || op_link_count(of) != 1) { // We downmix multichannel to stereo as recommended by Opus specification in // case we are not able to handle > 2 channels. // We also decode chained files as stereo even if they start with a mono // stream, which simplifies handling of link boundaries for us. channels = 2; } std::vector<int16> decodeBuf(120 * 48000 / 1000); // 120ms (max Opus packet), 48kHz bool eof = false; while(!eof) { int framesRead = 0; if(channels == 2) { framesRead = op_read_stereo(of, &(decodeBuf[0]), static_cast<int>(decodeBuf.size())); } else if(channels == 1) { framesRead = op_read(of, &(decodeBuf[0]), static_cast<int>(decodeBuf.size()), NULL); } if(framesRead > 0) { raw_sample_data.insert(raw_sample_data.end(), decodeBuf.begin(), decodeBuf.begin() + (framesRead * channels)); } else if(framesRead == 0) { eof = true; } else if(framesRead == OP_HOLE) { // continue } else { // other errors are fatal, stop decoding eof = true; } } op_free(of); of = NULL; if(raw_sample_data.empty()) { return false; } DestroySampleThreadsafe(sample); strcpy(m_szNames[sample], ""); Samples[sample].Initialize(); Samples[sample].nC5Speed = rate; Samples[sample].nLength = raw_sample_data.size() / channels; Samples[sample].uFlags.set(CHN_16BIT); Samples[sample].uFlags.set(CHN_STEREO, channels == 2); Samples[sample].AllocateSample(); std::copy(raw_sample_data.begin(), raw_sample_data.end(), Samples[sample].pSample16); Samples[sample].Convert(MOD_TYPE_IT, GetType()); Samples[sample].PrecomputeLoops(*this, false); return Samples[sample].pSample != nullptr; #else // !MPT_WITH_OPUSFILE MPT_UNREFERENCED_PARAMETER(sample); MPT_UNREFERENCED_PARAMETER(file); return false; #endif // MPT_WITH_OPUSFILE }
OPENMPT_NAMESPACE_BEGIN // Version changelog: // v1.03: - Relative unicode instrument paths instead of absolute ANSI paths // - Per-path variable string length // - Embedded samples are IT-compressed // (rev. 3249) // v1.02: Explicitely updated format to use new instrument flags representation (rev. 483) // v1.01: Added option to embed instrument headers bool CSoundFile::ReadITProject(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------------- { #ifndef MPT_EXTERNAL_SAMPLES // Doesn't really make sense to support this format when there's no support for external files... MPT_UNREFERENCED_PARAMETER(file); MPT_UNREFERENCED_PARAMETER(loadFlags); return false; #else // MPT_EXTERNAL_SAMPLES enum ITPSongFlags { ITP_EMBEDMIDICFG = 0x00001, // Embed macros in file ITP_ITOLDEFFECTS = 0x00004, // Old Impulse Tracker effect implementations ITP_ITCOMPATGXX = 0x00008, // IT "Compatible Gxx" (IT's flag to behave more like other trackers w/r/t portamento effects) ITP_LINEARSLIDES = 0x00010, // Linear slides vs. Amiga slides ITP_EXFILTERRANGE = 0x08000, // Cutoff Filter has double frequency range (up to ~10Khz) ITP_ITPROJECT = 0x20000, // Is a project file ITP_ITPEMBEDIH = 0x40000, // Embed instrument headers in project file }; uint32 version; FileReader::off_t size; file.Rewind(); // Check file ID if(!file.CanRead(12 + 4 + 24 + 4) || file.ReadUint32LE() != MAGIC4BE('.','i','t','p') // Magic bytes || (version = file.ReadUint32LE()) > 0x00000103 // Format version || version < 0x00000100) { return false; } else if(loadFlags == onlyVerifyHeader) { return true; } InitializeGlobals(MOD_TYPE_IT); m_playBehaviour.reset(); file.ReadString<mpt::String::maybeNullTerminated>(m_songName, file.ReadUint32LE()); // Song comments m_songMessage.Read(file, file.ReadUint32LE(), SongMessage::leCR); // Song global config const uint32 songFlags = file.ReadUint32LE(); if(!(songFlags & ITP_ITPROJECT)) { return false; } if(songFlags & ITP_EMBEDMIDICFG) m_SongFlags.set(SONG_EMBEDMIDICFG); if(songFlags & ITP_ITOLDEFFECTS) m_SongFlags.set(SONG_ITOLDEFFECTS); if(songFlags & ITP_ITCOMPATGXX) m_SongFlags.set(SONG_ITCOMPATGXX); if(songFlags & ITP_LINEARSLIDES) m_SongFlags.set(SONG_LINEARSLIDES); if(songFlags & ITP_EXFILTERRANGE) m_SongFlags.set(SONG_EXFILTERRANGE); m_nDefaultGlobalVolume = file.ReadUint32LE(); m_nSamplePreAmp = file.ReadUint32LE(); m_nDefaultSpeed = std::max(uint32(1), file.ReadUint32LE()); m_nDefaultTempo.Set(std::max(uint32(32), file.ReadUint32LE())); m_nChannels = static_cast<CHANNELINDEX>(file.ReadUint32LE()); if(m_nChannels == 0 || m_nChannels > MAX_BASECHANNELS) { return false; } // channel name string length (=MAX_CHANNELNAME) size = file.ReadUint32LE(); // Channels' data for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) { ChnSettings[chn].nPan = std::min(static_cast<uint16>(file.ReadUint32LE()), uint16(256)); ChnSettings[chn].dwFlags.reset(); uint32 flags = file.ReadUint32LE(); if(flags & 0x100) ChnSettings[chn].dwFlags.set(CHN_MUTE); if(flags & 0x800) ChnSettings[chn].dwFlags.set(CHN_SURROUND); ChnSettings[chn].nVolume = std::min(static_cast<uint16>(file.ReadUint32LE()), uint16(64)); file.ReadString<mpt::String::maybeNullTerminated>(ChnSettings[chn].szName, size); } // Song mix plugins { FileReader plugChunk = file.ReadChunk(file.ReadUint32LE()); LoadMixPlugins(plugChunk); } // MIDI Macro config file.ReadStructPartial(m_MidiCfg, file.ReadUint32LE()); m_MidiCfg.Sanitize(); // Song Instruments m_nInstruments = static_cast<INSTRUMENTINDEX>(file.ReadUint32LE()); if(m_nInstruments >= MAX_INSTRUMENTS) { return false; } // Instruments' paths if(version <= 0x00000102) { size = file.ReadUint32LE(); // path string length } std::vector<mpt::PathString> instrPaths(GetNumInstruments()); for(INSTRUMENTINDEX ins = 0; ins < GetNumInstruments(); ins++) { if(version > 0x00000102) { size = file.ReadUint32LE(); // path string length } std::string path; file.ReadString<mpt::String::maybeNullTerminated>(path, size); if(version <= 0x00000102) { instrPaths[ins] = mpt::PathString::FromLocaleSilent(path); } else { instrPaths[ins] = mpt::PathString::FromUTF8(path); } } // Song Orders size = file.ReadUint32LE(); Order.ReadAsByte(file, size, size, 0xFF, 0xFE); // Song Patterns const PATTERNINDEX numPats = static_cast<PATTERNINDEX>(file.ReadUint32LE()); const PATTERNINDEX numNamedPats = static_cast<PATTERNINDEX>(file.ReadUint32LE()); size_t patNameLen = file.ReadUint32LE(); // Size of each pattern name FileReader pattNames = file.ReadChunk(numNamedPats * patNameLen); // modcommand data length size = file.ReadUint32LE(); if(size != 6) { return false; } for(PATTERNINDEX pat = 0; pat < numPats; pat++) { const ROWINDEX numRows = file.ReadUint32LE(); FileReader patternChunk = file.ReadChunk(numRows * size * GetNumChannels()); // Allocate pattern if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, numRows)) { pattNames.Skip(patNameLen); continue; } if(pat < numNamedPats) { char patName[32]; pattNames.ReadString<mpt::String::maybeNullTerminated>(patName, patNameLen); Patterns[pat].SetName(patName); } // Pattern data size_t numCommands = GetNumChannels() * numRows; if(patternChunk.CanRead(sizeof(MODCOMMAND_ORIGINAL) * numCommands)) { ModCommand *target = Patterns[pat].GetpModCommand(0, 0); while(numCommands-- != 0) { STATIC_ASSERT(sizeof(MODCOMMAND_ORIGINAL) == 6); MODCOMMAND_ORIGINAL data; patternChunk.ReadStruct(data); if(data.command >= MAX_EFFECTS) data.command = CMD_NONE; if(data.volcmd >= MAX_VOLCMDS) data.volcmd = VOLCMD_NONE; if(data.note > NOTE_MAX && data.note < NOTE_MIN_SPECIAL) data.note = NOTE_NONE; *(target++) = data; } } } // Load embedded samples // Read original number of samples m_nSamples = static_cast<SAMPLEINDEX>(file.ReadUint32LE()); LimitMax(m_nSamples, SAMPLEINDEX(MAX_SAMPLES - 1)); // Read number of embedded samples uint32 embeddedSamples = file.ReadUint32LE(); // Read samples for(uint32 smp = 0; smp < embeddedSamples; smp++) { SAMPLEINDEX realSample = static_cast<SAMPLEINDEX>(file.ReadUint32LE()); ITSample sampleHeader; file.ReadConvertEndianness(sampleHeader); FileReader sampleData = file.ReadChunk(file.ReadUint32LE()); if(realSample >= 1 && realSample <= GetNumSamples() && !memcmp(sampleHeader.id, "IMPS", 4) && (loadFlags & loadSampleData)) { sampleHeader.ConvertToMPT(Samples[realSample]); mpt::String::Read<mpt::String::nullTerminated>(m_szNames[realSample], sampleHeader.name); // Read sample data sampleHeader.GetSampleFormat().ReadSample(Samples[realSample], sampleData); } } // Load instruments for(INSTRUMENTINDEX ins = 0; ins < GetNumInstruments(); ins++) { if(instrPaths[ins].empty()) continue; if(!file.GetFileName().empty()) { instrPaths[ins] = instrPaths[ins].RelativePathToAbsolute(file.GetFileName().GetPath()); } #ifdef MODPLUG_TRACKER else if(GetpModDoc() != nullptr) { instrPaths[ins] = instrPaths[ins].RelativePathToAbsolute(GetpModDoc()->GetPathNameMpt().GetPath()); } #endif // MODPLUG_TRACKER InputFile f(instrPaths[ins]); FileReader file = GetFileReader(f); if(!ReadInstrumentFromFile(ins + 1, file, true)) { AddToLog(LogWarning, MPT_USTRING("Unable to open instrument: ") + instrPaths[ins].ToUnicode()); } } // Extra info data uint32 code = file.ReadUint32LE(); // Embed instruments' header [v1.01] if(version >= 0x00000101 && (songFlags & ITP_ITPEMBEDIH) && code == MAGIC4BE('E', 'B', 'I', 'H')) { code = file.ReadUint32LE(); INSTRUMENTINDEX ins = 1; while(ins <= GetNumInstruments() && file.CanRead(4)) { if(code == MAGIC4BE('M', 'P', 'T', 'S')) { break; } else if(code == MAGIC4BE('S', 'E', 'P', '@') || code == MAGIC4BE('M', 'P', 'T', 'X')) { // jump code - switch to next instrument ins++; } else { ReadExtendedInstrumentProperty(Instruments[ins], code, file); } code = file.ReadUint32LE(); } } // Song extensions if(code == MAGIC4BE('M', 'P', 'T', 'S')) { file.SkipBack(4); LoadExtendedSongProperties(file); } m_nMaxPeriod = 0xF000; m_nMinPeriod = 8; // Before OpenMPT 1.20.01.09, the MIDI macros were always read from the file, even if the "embed" flag was not set. if(m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1,20,01,09) && !m_SongFlags[SONG_EMBEDMIDICFG]) { m_MidiCfg.Reset(); } else if(!m_MidiCfg.IsMacroDefaultSetupUsed()) { m_SongFlags.set(SONG_EMBEDMIDICFG); } m_madeWithTracker = "OpenMPT " + MptVersion::ToStr(m_dwLastSavedWithVersion); return true; #endif // MPT_EXTERNAL_SAMPLES }
void CSoundFile::ProcessPlugins(uint32 nCount) { #ifndef NO_PLUGINS // If any sample channels are active or any plugin has some input, possibly suspended master plugins need to be woken up. bool masterHasInput = (m_nMixStat > 0); #ifdef MPT_INTMIXER const float IntToFloat = m_PlayConfig.getIntToFloat(); const float FloatToInt = m_PlayConfig.getFloatToInt(); #endif // MPT_INTMIXER // Setup float inputs from samples for(PLUGINDEX plug = 0; plug < MAX_MIXPLUGINS; plug++) { SNDMIXPLUGIN &plugin = m_MixPlugins[plug]; if(plugin.pMixPlugin != nullptr && plugin.pMixPlugin->m_MixState.pMixBuffer != nullptr && plugin.pMixPlugin->m_mixBuffer.Ok()) { IMixPlugin *mixPlug = plugin.pMixPlugin; SNDMIXPLUGINSTATE &state = mixPlug->m_MixState; //We should only ever reach this point if the song is playing. if (!mixPlug->IsSongPlaying()) { //Plugin doesn't know it is in a song that is playing; //we must have added it during playback. Initialise it! mixPlug->NotifySongPlaying(true); mixPlug->Resume(); } // Setup float input float *plugInputL = mixPlug->m_mixBuffer.GetInputBuffer(0); float *plugInputR = mixPlug->m_mixBuffer.GetInputBuffer(1); if (state.dwFlags & SNDMIXPLUGINSTATE::psfMixReady) { #ifdef MPT_INTMIXER StereoMixToFloat(state.pMixBuffer, plugInputL, plugInputR, nCount, IntToFloat); #else DeinterleaveStereo(pState->pMixBuffer, plugInputL, plugInputR, nCount); #endif // MPT_INTMIXER } else if (state.nVolDecayR || state.nVolDecayL) { StereoFill(state.pMixBuffer, nCount, state.nVolDecayR, state.nVolDecayL); #ifdef MPT_INTMIXER StereoMixToFloat(state.pMixBuffer, plugInputL, plugInputR, nCount, IntToFloat); #else DeinterleaveStereo(pState->pMixBuffer, plugInputL, plugInputR, nCount); #endif // MPT_INTMIXER } else { memset(plugInputL, 0, nCount * sizeof(plugInputL[0])); memset(plugInputR, 0, nCount * sizeof(plugInputR[0])); } state.dwFlags &= ~SNDMIXPLUGINSTATE::psfMixReady; if(!plugin.IsMasterEffect() && !(state.dwFlags & SNDMIXPLUGINSTATE::psfSilenceBypass)) { masterHasInput = true; } } } // Convert mix buffer #ifdef MPT_INTMIXER StereoMixToFloat(MixSoundBuffer, MixFloatBuffer[0], MixFloatBuffer[1], nCount, IntToFloat); #else DeinterleaveStereo(MixSoundBuffer, MixFloatBuffer[0], MixFloatBuffer[1], nCount); #endif // MPT_INTMIXER float *pMixL = MixFloatBuffer[0]; float *pMixR = MixFloatBuffer[1]; const bool positionChanged = HasPositionChanged(); // Process Plugins for(PLUGINDEX plug = 0; plug < MAX_MIXPLUGINS; plug++) { SNDMIXPLUGIN &plugin = m_MixPlugins[plug]; if (plugin.pMixPlugin != nullptr && plugin.pMixPlugin->m_MixState.pMixBuffer != nullptr && plugin.pMixPlugin->m_mixBuffer.Ok()) { IMixPlugin *pObject = plugin.pMixPlugin; if(!plugin.IsMasterEffect() && !plugin.pMixPlugin->ShouldProcessSilence() && !(plugin.pMixPlugin->m_MixState.dwFlags & SNDMIXPLUGINSTATE::psfHasInput)) { // If plugin has no inputs and isn't a master plugin, we shouldn't let it process silence if possible. // I have yet to encounter a plugin which actually sets this flag. bool hasInput = false; for(PLUGINDEX inPlug = 0; inPlug < plug; inPlug++) { if(m_MixPlugins[inPlug].GetOutputPlugin() == plug) { hasInput = true; break; } } if(!hasInput) { continue; } } bool isMasterMix = false; float *plugInputL = pObject->m_mixBuffer.GetInputBuffer(0); float *plugInputR = pObject->m_mixBuffer.GetInputBuffer(1); if (pMixL == plugInputL) { isMasterMix = true; pMixL = MixFloatBuffer[0]; pMixR = MixFloatBuffer[1]; } SNDMIXPLUGINSTATE &state = plugin.pMixPlugin->m_MixState; float *pOutL = pMixL; float *pOutR = pMixR; if (!plugin.IsOutputToMaster()) { PLUGINDEX nOutput = plugin.GetOutputPlugin(); if(nOutput > plug && nOutput != PLUGINDEX_INVALID && m_MixPlugins[nOutput].pMixPlugin != nullptr) { IMixPlugin *outPlugin = m_MixPlugins[nOutput].pMixPlugin; if(!(state.dwFlags & SNDMIXPLUGINSTATE::psfSilenceBypass)) outPlugin->ResetSilence(); if(outPlugin->m_mixBuffer.Ok()) { pOutL = outPlugin->m_mixBuffer.GetInputBuffer(0); pOutR = outPlugin->m_mixBuffer.GetInputBuffer(1); } } } /* if (plugin.multiRouting) { int nOutput=0; for (int nOutput=0; nOutput < plugin.nOutputs / 2; nOutput++) { destinationPlug = plugin.multiRoutingDestinations[nOutput]; pOutState = m_MixPlugins[destinationPlug].pMixState; pOutputs[2 * nOutput] = plugInputL; pOutputs[2 * (nOutput + 1)] = plugInputR; } }*/ if (plugin.IsMasterEffect()) { if (!isMasterMix) { float *pInL = plugInputL; float *pInR = plugInputR; for (uint32 i=0; i<nCount; i++) { pInL[i] += pMixL[i]; pInR[i] += pMixR[i]; pMixL[i] = 0; pMixR[i] = 0; } } pMixL = pOutL; pMixR = pOutR; if(masterHasInput) { // Samples or plugins are being rendered, so turn off auto-bypass for this master effect. if(plugin.pMixPlugin != nullptr) plugin.pMixPlugin->ResetSilence(); SNDMIXPLUGIN *chain = &plugin; PLUGINDEX out = chain->GetOutputPlugin(), prevOut = plug; while(out > prevOut && out < MAX_MIXPLUGINS) { chain = &m_MixPlugins[out]; prevOut = out; out = chain->GetOutputPlugin(); if(chain->pMixPlugin) { chain->pMixPlugin->ResetSilence(); } } } } if(plugin.IsBypassed() || (plugin.IsAutoSuspendable() && (state.dwFlags & SNDMIXPLUGINSTATE::psfSilenceBypass))) { const float * const pInL = plugInputL; const float * const pInR = plugInputR; for (uint32 i=0; i<nCount; i++) { pOutL[i] += pInL[i]; pOutR[i] += pInR[i]; } } else { if(positionChanged) pObject->PositionChanged(); pObject->Process(pOutL, pOutR, nCount); state.inputSilenceCount += nCount; if(plugin.IsAutoSuspendable() && pObject->GetNumOutputChannels() > 0 && state.inputSilenceCount >= m_MixerSettings.gdwMixingFreq * 4) { bool isSilent = true; for(uint32 i = 0; i < nCount; i++) { if(pOutL[i] >= FLT_EPSILON || pOutL[i] <= -FLT_EPSILON || pOutR[i] >= FLT_EPSILON || pOutR[i] <= -FLT_EPSILON) { isSilent = false; break; } } if(isSilent) { state.dwFlags |= SNDMIXPLUGINSTATE::psfSilenceBypass; } else { state.inputSilenceCount = 0; } } } state.dwFlags &= ~SNDMIXPLUGINSTATE::psfHasInput; } } #ifdef MPT_INTMIXER FloatToStereoMix(pMixL, pMixR, MixSoundBuffer, nCount, FloatToInt); #else InterleaveStereo(pMixL, pMixR, MixSoundBuffer, nCount); #endif // MPT_INTMIXER #else MPT_UNREFERENCED_PARAMETER(nCount); #endif // NO_PLUGINS }