winamp/Src/external_dependencies/openmpt-trunk/mptrack/Moddoc.h

476 lines
18 KiB
C++

/*
* 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> &notesPlaying) { m_notesPlaying = &notesPlaying; 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 &params, 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