diff options
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/mptrack/Moddoc.h')
-rw-r--r-- | Src/external_dependencies/openmpt-trunk/mptrack/Moddoc.h | 475 |
1 files changed, 475 insertions, 0 deletions
diff --git a/Src/external_dependencies/openmpt-trunk/mptrack/Moddoc.h b/Src/external_dependencies/openmpt-trunk/mptrack/Moddoc.h new file mode 100644 index 00000000..e3fefa96 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/mptrack/Moddoc.h @@ -0,0 +1,475 @@ +/* + * ModDoc.h + * -------- + * Purpose: Converting between various module formats. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "openmpt/all/BuildSettings.hpp" + +#include "Sndfile.h" +#include "../common/misc_util.h" +#include "Undo.h" +#include "Notification.h" +#include "UpdateHints.h" +#include <time.h> + +OPENMPT_NAMESPACE_BEGIN + +class EncoderFactoryBase; +class CChildFrame; + +///////////////////////////////////////////////////////////////////////// +// Split Keyboard Settings (pattern editor) + +struct SplitKeyboardSettings +{ + enum + { + splitOctaveRange = 9, + }; + + bool IsSplitActive() const { return (octaveLink && (octaveModifier != 0)) || (splitInstrument != 0) || (splitVolume != 0); } + + int octaveModifier = 0; // determines by how many octaves the notes should be transposed up or down + ModCommand::NOTE splitNote = NOTE_MIDDLEC - 1; + ModCommand::INSTR splitInstrument = 0; + ModCommand::VOL splitVolume = 0; + bool octaveLink = false; // apply octaveModifier +}; + +enum InputTargetContext : int8; + + +struct LogEntry +{ + LogLevel level; + mpt::ustring message; + LogEntry() : level(LogInformation) {} + LogEntry(LogLevel l, const mpt::ustring &m) : level(l), message(m) {} +}; + + +enum LogMode +{ + LogModeInstantReporting, + LogModeGather, +}; + + +class ScopedLogCapturer +{ +private: + CModDoc &m_modDoc; + LogMode m_oldLogMode; + CString m_title; + CWnd *m_pParent; + bool m_showLog; +public: + ScopedLogCapturer(CModDoc &modDoc, const CString &title = {}, CWnd *parent = nullptr, bool showLog = true); + ~ScopedLogCapturer(); + void ShowLog(bool force = false); + void ShowLog(const CString &preamble, bool force = false); + [[deprecated]] void ShowLog(const std::string &preamble, bool force = false); + void ShowLog(const mpt::ustring &preamble, bool force = false); +}; + + +struct PlayNoteParam +{ + std::bitset<128> *m_notesPlaying = nullptr; + SmpLength m_loopStart = 0, m_loopEnd = 0, m_sampleOffset = 0; + int32 m_volume = -1; + SAMPLEINDEX m_sample = 0; + INSTRUMENTINDEX m_instr = 0; + CHANNELINDEX m_currentChannel = CHANNELINDEX_INVALID; + ModCommand::NOTE m_note; + + PlayNoteParam(ModCommand::NOTE note) : m_note(note) { } + + PlayNoteParam& LoopStart(SmpLength loopStart) { m_loopStart = loopStart; return *this; } + PlayNoteParam& LoopEnd(SmpLength loopEnd) { m_loopEnd = loopEnd; return *this; } + PlayNoteParam& Offset(SmpLength sampleOffset) { m_sampleOffset = sampleOffset; return *this; } + + PlayNoteParam& Volume(int32 volume) { m_volume = volume; return *this; } + PlayNoteParam& Sample(SAMPLEINDEX sample) { m_sample = sample; return *this; } + PlayNoteParam& Instrument(INSTRUMENTINDEX instr) { m_instr = instr; return *this; } + PlayNoteParam& Channel(CHANNELINDEX channel) { m_currentChannel = channel; return *this; } + + PlayNoteParam& CheckNNA(std::bitset<128> ¬esPlaying) { m_notesPlaying = ¬esPlaying; return *this; } +}; + + +enum class RecordGroup : uint8 +{ + NoGroup = 0, + Group1 = 1, + Group2 = 2, +}; + + +class CModDoc final : public CDocument +{ +protected: + friend ScopedLogCapturer; + mutable std::vector<LogEntry> m_Log; + LogMode m_LogMode = LogModeInstantReporting; + CSoundFile m_SndFile; + + HWND m_hWndFollow = nullptr; + FlagSet<Notification::Type, uint16> m_notifyType; + Notification::Item m_notifyItem = 0; + CSize m_szOldPatternScrollbarsPos = { -10, -10 }; + + CPatternUndo m_PatternUndo; + CSampleUndo m_SampleUndo; + CInstrumentUndo m_InstrumentUndo; + SplitKeyboardSettings m_SplitKeyboardSettings; // this is maybe not the best place to keep them, but it should do the job + time_t m_creationTime; + + std::atomic<bool> m_modifiedAutosave = false; // Modified since last autosave? + +public: + class NoteToChannelMap : public std::array<CHANNELINDEX, NOTE_MAX - NOTE_MIN + 1> + { + public: + NoteToChannelMap() { fill(CHANNELINDEX_INVALID); } + }; + NoteToChannelMap m_noteChannel; // Note -> Preview channel assignment + + bool m_ShowSavedialog = false; + bool m_bHasValidPath = false; //becomes true if document is loaded or saved. + +protected: + // Note-off event buffer for MIDI sustain pedal + std::array<std::vector<uint32>, 16> m_midiSustainBuffer; + std::array<std::bitset<128>, 16> m_midiPlayingNotes; + std::bitset<16> m_midiSustainActive; + + std::bitset<MAX_BASECHANNELS> m_bsMultiRecordMask; + std::bitset<MAX_BASECHANNELS> m_bsMultiSplitRecordMask; + +protected: // create from serialization only + CModDoc(); + DECLARE_DYNCREATE(CModDoc) + +// public members +public: + CSoundFile &GetSoundFile() { return m_SndFile; } + const CSoundFile &GetSoundFile() const { return m_SndFile; } + +#if MPT_COMPILER_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Woverloaded-virtual" +#endif // MPT_COMPILER_CLANG + bool IsModified() const { return m_bModified != FALSE; } // Work-around: CDocument::IsModified() is not const... +#if MPT_COMPILER_CLANG +#pragma clang diagnostic pop +#endif // MPT_COMPILER_CLANG + void SetModified(bool modified = true); + bool ModifiedSinceLastAutosave(); + void SetShowSaveDialog(bool b) { m_ShowSavedialog = b; } + void PostMessageToAllViews(UINT uMsg, WPARAM wParam = 0, LPARAM lParam = 0); + void SendNotifyMessageToAllViews(UINT uMsg, WPARAM wParam = 0, LPARAM lParam = 0); + void SendMessageToActiveView(UINT uMsg, WPARAM wParam = 0, LPARAM lParam = 0); + MODTYPE GetModType() const { return m_SndFile.m_nType; } + INSTRUMENTINDEX GetNumInstruments() const { return m_SndFile.m_nInstruments; } + SAMPLEINDEX GetNumSamples() const { return m_SndFile.m_nSamples; } + + // Logging for general progress and error events. + void AddToLog(LogLevel level, const mpt::ustring &text) const; + /*[[deprecated]]*/ void AddToLog(const CString &text) const { AddToLog(LogInformation, mpt::ToUnicode(text)); } + /*[[deprecated]]*/ void AddToLog(const std::string &text) const { AddToLog(LogInformation, mpt::ToUnicode(mpt::Charset::Locale, text)); } + /*[[deprecated]]*/ void AddToLog(const char *text) const { AddToLog(LogInformation, mpt::ToUnicode(mpt::Charset::Locale, text ? text : "")); } + + const std::vector<LogEntry> & GetLog() const { return m_Log; } + mpt::ustring GetLogString() const; + LogLevel GetMaxLogLevel() const; +protected: + LogMode GetLogMode() const { return m_LogMode; } + void SetLogMode(LogMode mode) { m_LogMode = mode; } + void ClearLog(); + UINT ShowLog(const CString &preamble, const CString &title = {}, CWnd *parent = nullptr); + UINT ShowLog(const CString &title = {}, CWnd *parent = nullptr) { return ShowLog(_T(""), title, parent); } + +public: + + void ClearFilePath() { m_strPathName.Empty(); } + + void ViewPattern(UINT nPat, UINT nOrd); + void ViewSample(UINT nSmp); + void ViewInstrument(UINT nIns); + HWND GetFollowWnd() const { return m_hWndFollow; } + void SetFollowWnd(HWND hwnd); + + void SetNotifications(FlagSet<Notification::Type> type, Notification::Item item = 0) { m_notifyType = type; m_notifyItem = item; } + FlagSet<Notification::Type, uint16> GetNotificationType() const { return m_notifyType; } + Notification::Item GetNotificationItem() const { return m_notifyItem; } + + void ActivateWindow(); + + void OnSongProperties(); + + void PrepareUndoForAllPatterns(bool storeChannelInfo = false, const char *description = ""); + CPatternUndo &GetPatternUndo() { return m_PatternUndo; } + CSampleUndo &GetSampleUndo() { return m_SampleUndo; } + CInstrumentUndo &GetInstrumentUndo() { return m_InstrumentUndo; } + SplitKeyboardSettings &GetSplitKeyboardSettings() { return m_SplitKeyboardSettings; } + + time_t GetCreationTime() const { return m_creationTime; } + +// operations +public: + bool ChangeModType(MODTYPE wType); + + bool ChangeNumChannels(CHANNELINDEX nNewChannels, const bool showCancelInRemoveDlg = true); + bool RemoveChannels(const std::vector<bool> &keepMask, bool verbose = false); + void CheckUsedChannels(std::vector<bool> &usedMask, CHANNELINDEX maxRemoveCount = MAX_BASECHANNELS) const; + + CHANNELINDEX ReArrangeChannels(const std::vector<CHANNELINDEX> &fromToArray, const bool createUndoPoint = true); + SAMPLEINDEX ReArrangeSamples(const std::vector<SAMPLEINDEX> &newOrder); + INSTRUMENTINDEX ReArrangeInstruments(const std::vector<INSTRUMENTINDEX> &newOrder, deleteInstrumentSamples removeSamples = doNoDeleteAssociatedSamples); + SEQUENCEINDEX ReArrangeSequences(const std::vector<SEQUENCEINDEX> &newOrder); + + bool ConvertInstrumentsToSamples(); + bool ConvertSamplesToInstruments(); + PLUGINDEX RemovePlugs(const std::vector<bool> &keepMask); + bool RemovePlugin(PLUGINDEX plugin); + + void ClonePlugin(SNDMIXPLUGIN &target, const SNDMIXPLUGIN &source); + void AppendModule(const CSoundFile &source); + + // Create a new pattern and, if order position is specified, inserts it into the order list. + PATTERNINDEX InsertPattern(ROWINDEX rows, ORDERINDEX ord = ORDERINDEX_INVALID); + SAMPLEINDEX InsertSample(); + INSTRUMENTINDEX InsertInstrument(SAMPLEINDEX sample = SAMPLEINDEX_INVALID, INSTRUMENTINDEX duplicateSource = INSTRUMENTINDEX_INVALID, bool silent = false); + INSTRUMENTINDEX InsertInstrumentForPlugin(PLUGINDEX plug); + INSTRUMENTINDEX HasInstrumentForPlugin(PLUGINDEX plug) const; + void InitializeInstrument(ModInstrument *pIns); + bool RemoveOrder(SEQUENCEINDEX nSeq, ORDERINDEX nOrd); + bool RemovePattern(PATTERNINDEX nPat); + bool RemoveSample(SAMPLEINDEX nSmp); + bool RemoveInstrument(INSTRUMENTINDEX nIns); + + void ProcessMIDI(uint32 midiData, INSTRUMENTINDEX ins, IMixPlugin *plugin, InputTargetContext ctx); + CHANNELINDEX PlayNote(PlayNoteParam ¶ms, NoteToChannelMap *noteChannel = nullptr); + bool NoteOff(UINT note, bool fade = false, INSTRUMENTINDEX ins = INSTRUMENTINDEX_INVALID, CHANNELINDEX currentChn = CHANNELINDEX_INVALID); + void CheckNNA(ModCommand::NOTE note, INSTRUMENTINDEX ins, std::bitset<128> &playingNotes); + void UpdateOPLInstrument(SAMPLEINDEX smp); + + bool IsNotePlaying(UINT note, SAMPLEINDEX nsmp = 0, INSTRUMENTINDEX nins = 0); + bool MuteChannel(CHANNELINDEX nChn, bool bMute); + bool UpdateChannelMuteStatus(CHANNELINDEX nChn); + bool MuteSample(SAMPLEINDEX nSample, bool bMute); + bool MuteInstrument(INSTRUMENTINDEX nInstr, bool bMute); + // Returns true if toggling the mute status of a channel should set the document as modified given the current module format and settings. + bool MuteToggleModifiesDocument() const; + + bool SoloChannel(CHANNELINDEX nChn, bool bSolo); + bool IsChannelSolo(CHANNELINDEX nChn) const; + + bool SurroundChannel(CHANNELINDEX nChn, bool bSurround); + bool SetChannelGlobalVolume(CHANNELINDEX nChn, uint16 nVolume); + bool SetChannelDefaultPan(CHANNELINDEX nChn, uint16 nPan); + bool IsChannelMuted(CHANNELINDEX nChn) const; + bool IsSampleMuted(SAMPLEINDEX nSample) const; + bool IsInstrumentMuted(INSTRUMENTINDEX nInstr) const; + + bool NoFxChannel(CHANNELINDEX nChn, bool bNoFx, bool updateMix = true); + bool IsChannelNoFx(CHANNELINDEX nChn) const; + + RecordGroup GetChannelRecordGroup(CHANNELINDEX channel) const; + void SetChannelRecordGroup(CHANNELINDEX channel, RecordGroup recordGroup); + void ToggleChannelRecordGroup(CHANNELINDEX channel, RecordGroup recordGroup); + void ReinitRecordState(bool unselect = true); + + CHANNELINDEX GetNumChannels() const { return m_SndFile.m_nChannels; } + UINT GetPatternSize(PATTERNINDEX nPat) const; + bool IsChildSample(INSTRUMENTINDEX nIns, SAMPLEINDEX nSmp) const; + INSTRUMENTINDEX FindSampleParent(SAMPLEINDEX sample) const; + SAMPLEINDEX FindInstrumentChild(INSTRUMENTINDEX nIns) const; + bool MoveOrder(ORDERINDEX nSourceNdx, ORDERINDEX nDestNdx, bool bUpdate = true, bool bCopy = false, SEQUENCEINDEX nSourceSeq = SEQUENCEINDEX_INVALID, SEQUENCEINDEX nDestSeq = SEQUENCEINDEX_INVALID); + BOOL ExpandPattern(PATTERNINDEX nPattern); + BOOL ShrinkPattern(PATTERNINDEX nPattern); + + bool SetDefaultChannelColors() { return SetDefaultChannelColors(0, GetNumChannels()); } + bool SetDefaultChannelColors(CHANNELINDEX channel) { return SetDefaultChannelColors(channel, channel + 1u); } + bool SetDefaultChannelColors(CHANNELINDEX minChannel, CHANNELINDEX maxChannel); + bool SupportsChannelColors() const { return GetModType() & (MOD_TYPE_XM | MOD_TYPE_IT | MOD_TYPE_MPT); } + + bool CopyEnvelope(INSTRUMENTINDEX nIns, EnvelopeType nEnv); + bool SaveEnvelope(INSTRUMENTINDEX nIns, EnvelopeType nEnv, const mpt::PathString &fileName); + bool PasteEnvelope(INSTRUMENTINDEX nIns, EnvelopeType nEnv); + bool LoadEnvelope(INSTRUMENTINDEX nIns, EnvelopeType nEnv, const mpt::PathString &fileName); + + LRESULT ActivateView(UINT nIdView, DWORD dwParam); + // Notify all views of document updates (GUI thread only) + void UpdateAllViews(CView *pSender, UpdateHint hint, CObject *pHint=NULL); + // Notify all views of document updates (for non-GUI threads) + void UpdateAllViews(UpdateHint hint); + void GetEditPosition(ROWINDEX &row, PATTERNINDEX &pat, ORDERINDEX &ord); + LRESULT OnCustomKeyMsg(WPARAM, LPARAM); + void TogglePluginEditor(UINT m_nCurrentPlugin, bool onlyThisEditor = false); + void RecordParamChange(PLUGINDEX slot, PlugParamIndex param); + void LearnMacro(int macro, PlugParamIndex param); + void SetElapsedTime(ORDERINDEX nOrd, ROWINDEX nRow, bool setSamplePos); + void SetLoopSong(bool loop); + + // Global settings to pattern effect conversion + bool GlobalVolumeToPattern(); + + bool HasMPTHacks(const bool autofix = false); + + void FixNullStrings(); + +// Fix: save pattern scrollbar position when switching to other tab + CSize GetOldPatternScrollbarsPos() const { return m_szOldPatternScrollbarsPos; }; + void SetOldPatternScrollbarsPos( CSize s ){ m_szOldPatternScrollbarsPos = s; }; + + void OnFileWaveConvert(ORDERINDEX nMinOrder, ORDERINDEX nMaxOrder); + void OnFileWaveConvert(ORDERINDEX nMinOrder, ORDERINDEX nMaxOrder, const std::vector<EncoderFactoryBase*> &encFactories); + + // Returns formatted ModInstrument name. + // [in] bEmptyInsteadOfNoName: In case of unnamed instrument string, "(no name)" is returned unless this + // parameter is true is case which an empty name is returned. + // [in] bIncludeIndex: True to include instrument index in front of the instrument name, false otherwise. + CString GetPatternViewInstrumentName(INSTRUMENTINDEX nInstr, bool bEmptyInsteadOfNoName = false, bool bIncludeIndex = true) const; + + // Check if a given channel contains data. + bool IsChannelUnused(CHANNELINDEX nChn) const; + // Check whether a sample is used. + // In sample mode, the sample numbers in all patterns are checked. + // In instrument mode, it is only checked if a sample is referenced by an instrument (but not if the sample is actually played anywhere) + bool IsSampleUsed(SAMPLEINDEX sample, bool searchInMutedChannels = true) const; + // Check whether an instrument is used (only for instrument mode). + bool IsInstrumentUsed(INSTRUMENTINDEX instr, bool searchInMutedChannels = true) const; + +// protected members +protected: + + void InitializeMod(); + + CChildFrame *GetChildFrame(); //rewbs.customKeys + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CModDoc) + public: + BOOL OnNewDocument() override; + BOOL OnOpenDocument(LPCTSTR lpszPathName) override; + BOOL OnSaveDocument(LPCTSTR lpszPathName) override + { + return OnSaveDocument(lpszPathName ? mpt::PathString::FromCString(lpszPathName) : mpt::PathString()) ? TRUE : FALSE; + } + void OnCloseDocument() override; + void SafeFileClose(); + bool OnSaveDocument(const mpt::PathString &filename, const bool setPath = true); + +#if MPT_COMPILER_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Woverloaded-virtual" +#endif // MPT_COMPILER_CLANG + void SetPathName(const mpt::PathString &filename, BOOL bAddToMRU = TRUE) + { + CDocument::SetPathName(filename.ToCString(), bAddToMRU); + } +#if MPT_COMPILER_CLANG +#pragma clang diagnostic pop +#endif // MPT_COMPILER_CLANG + mpt::PathString GetPathNameMpt() const + { + return mpt::PathString::FromCString(GetPathName()); + } + + BOOL SaveModified() override; + bool SaveAllSamples(bool showPrompt = true); + bool SaveSample(SAMPLEINDEX smp); + + BOOL DoSave(LPCTSTR lpszPathName, BOOL /*bSaveAs*/ = TRUE) override + { + return DoSave(lpszPathName ? mpt::PathString::FromCString(lpszPathName) : mpt::PathString()); + } + BOOL DoSave(const mpt::PathString &filename, bool setPath = true); + void DeleteContents() override; + //}}AFX_VIRTUAL + + // Get the sample index for the current pattern cell (resolves instrument note maps, etc) + SAMPLEINDEX GetSampleIndex(const ModCommand &m, ModCommand::INSTR lastInstr = 0) const; + // Get group (octave) size from given instrument (or sample in sample mode) + int GetInstrumentGroupSize(INSTRUMENTINDEX instr) const; + int GetBaseNote(INSTRUMENTINDEX instr) const; + ModCommand::NOTE GetNoteWithBaseOctave(int noteOffset, INSTRUMENTINDEX instr) const; + + // Convert a linear volume property to decibels + static CString LinearToDecibels(double value, double valueAtZeroDB); + // Convert a panning value to a more readable string + static CString PanningToString(int32 value, int32 valueAtCenter); + + void SerializeViews() const; + void DeserializeViews(); + + // View MIDI Mapping dialog for given plugin and parameter combination. + void ViewMIDIMapping(PLUGINDEX plugin = PLUGINDEX_INVALID, PlugParamIndex param = 0); + +// Implementation +public: + virtual ~CModDoc(); + +// Generated message map functions +public: + //{{AFX_MSG(CModDoc) + afx_msg void OnFileWaveConvert(); + afx_msg void OnFileMidiConvert(); + afx_msg void OnFileOPLExport(); + afx_msg void OnFileCompatibilitySave(); + afx_msg void OnPlayerPlay(); + afx_msg void OnPlayerStop(); + afx_msg void OnPlayerPause(); + afx_msg void OnPlayerPlayFromStart(); + afx_msg void OnPanic(); + afx_msg void OnEditGlobals(); + afx_msg void OnEditPatterns(); + afx_msg void OnEditSamples(); + afx_msg void OnEditInstruments(); + afx_msg void OnEditComments(); + afx_msg void OnShowCleanup(); + afx_msg void OnShowSampleTrimmer(); + afx_msg void OnSetupZxxMacros(); + afx_msg void OnEstimateSongLength(); + afx_msg void OnApproximateBPM(); + afx_msg void OnUpdateXMITMPTOnly(CCmdUI *p); + afx_msg void OnUpdateHasEditHistory(CCmdUI *p); + afx_msg void OnUpdateHasMIDIMappings(CCmdUI *p); + afx_msg void OnUpdateCompatExportableOnly(CCmdUI *p); + afx_msg void OnPatternRestart() { OnPatternRestart(true); } //rewbs.customKeys + afx_msg void OnPatternRestart(bool loop); //rewbs.customKeys + afx_msg void OnPatternPlay(); //rewbs.customKeys + afx_msg void OnPatternPlayNoLoop(); //rewbs.customKeys + afx_msg void OnViewEditHistory(); + afx_msg void OnViewMPTHacks(); + afx_msg void OnViewTempoSwingSettings(); + afx_msg void OnSaveCopy(); + afx_msg void OnSaveTemplateModule(); + afx_msg void OnAppendModule(); + afx_msg void OnViewMIDIMapping() { ViewMIDIMapping(); } + afx_msg void OnChannelManager(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +private: + + void ChangeFileExtension(MODTYPE nNewType); + CHANNELINDEX FindAvailableChannel() const; +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + + +OPENMPT_NAMESPACE_END |