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

589 lines
18 KiB
C++

/*
* Mainfrm.h
* ---------
* Purpose: Implementation of OpenMPT's main window code.
* 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 <Msctf.h>
#include "Mptrack.h"
#include "AutoSaver.h"
#include "UpdateHints.h"
#include "../soundlib/AudioCriticalSection.h"
#include "mpt/mutex/mutex.hpp"
#include "../soundlib/Sndfile.h"
#include "openmpt/soundbase/Dither.hpp"
#include "../common/Dither.h"
#include "mpt/audio/span.hpp"
#include "openmpt/sounddevice/SoundDeviceBuffer.hpp"
OPENMPT_NAMESPACE_BEGIN
class CDLSBank;
class CInputHandler;
class CModDoc;
class CAutoSaver;
struct UpdateCheckResult;
namespace SoundDevice {
class Base;
class ICallback;
} // namerspace SoundDevice
#define MAINFRAME_TITLE _T("Open ModPlug Tracker")
// Custom window messages
enum
{
WM_MOD_UPDATEPOSITION = (WM_USER+1973),
WM_MOD_INVALIDATEPATTERNS,
WM_MOD_ACTIVATEVIEW,
WM_MOD_CHANGEVIEWCLASS,
WM_MOD_UNLOCKCONTROLS,
WM_MOD_CTRLMSG,
WM_MOD_VIEWMSG,
WM_MOD_MIDIMSG,
WM_MOD_GETTOOLTIPTEXT,
WM_MOD_DRAGONDROPPING,
WM_MOD_KBDNOTIFY,
WM_MOD_INSTRSELECTED,
WM_MOD_KEYCOMMAND,
WM_MOD_RECORDPARAM,
WM_MOD_PLUGPARAMAUTOMATE,
WM_MOD_MIDIMAPPING,
WM_MOD_UPDATEVIEWS,
WM_MOD_SETMODIFIED,
WM_MOD_MDIACTIVATE,
WM_MOD_MDIDEACTIVATE,
WM_MOD_UPDATENOTIFY,
WM_MOD_PLUGINDRYWETRATIOCHANGED,
};
enum
{
MPT_WM_APP_UPDATECHECK_START = WM_APP + 1,
MPT_WM_APP_UPDATECHECK_PROGRESS = WM_APP + 2,
MPT_WM_APP_UPDATECHECK_CANCELED = WM_APP + 3,
MPT_WM_APP_UPDATECHECK_FAILURE = WM_APP + 4,
MPT_WM_APP_UPDATECHECK_SUCCESS = WM_APP + 5,
};
enum
{
CTRLMSG_BASE = 0,
CTRLMSG_SETVIEWWND,
CTRLMSG_ACTIVATEPAGE,
CTRLMSG_DEACTIVATEPAGE,
CTRLMSG_SETFOCUS,
// Pattern-Specific
CTRLMSG_GETCURRENTPATTERN,
CTRLMSG_NOTIFYCURRENTORDER,
CTRLMSG_SETCURRENTORDER,
CTRLMSG_GETCURRENTORDER,
CTRLMSG_FORCEREFRESH,
CTRLMSG_PAT_PREVINSTRUMENT,
CTRLMSG_PAT_NEXTINSTRUMENT,
CTRLMSG_PAT_SETINSTRUMENT,
CTRLMSG_PAT_FOLLOWSONG,
CTRLMSG_PAT_LOOP,
CTRLMSG_PAT_NEWPATTERN,
CTRLMSG_PAT_SETSEQUENCE,
CTRLMSG_GETCURRENTINSTRUMENT,
CTRLMSG_SETCURRENTINSTRUMENT,
CTRLMSG_SETSPACING,
CTRLMSG_PATTERNCHANGED,
CTRLMSG_PREVORDER,
CTRLMSG_NEXTORDER,
CTRLMSG_SETRECORD,
CTRLMSG_PAT_DUPPATTERN,
// Sample-Specific
CTRLMSG_SMP_PREVINSTRUMENT,
CTRLMSG_SMP_NEXTINSTRUMENT,
CTRLMSG_SMP_OPENFILE,
CTRLMSG_SMP_SETZOOM,
CTRLMSG_SMP_GETZOOM,
CTRLMSG_SMP_SONGDROP,
CTRLMSG_SMP_INITOPL,
CTRLMSG_SMP_NEWSAMPLE,
// Instrument-Specific
CTRLMSG_INS_PREVINSTRUMENT,
CTRLMSG_INS_NEXTINSTRUMENT,
CTRLMSG_INS_OPENFILE,
CTRLMSG_INS_NEWINSTRUMENT,
CTRLMSG_INS_SONGDROP,
CTRLMSG_INS_SAMPLEMAP,
};
enum
{
VIEWMSG_BASE=0,
VIEWMSG_SETCTRLWND,
VIEWMSG_SETACTIVE,
VIEWMSG_SETFOCUS,
VIEWMSG_SAVESTATE,
VIEWMSG_LOADSTATE,
// Pattern-Specific
VIEWMSG_SETCURRENTPATTERN,
VIEWMSG_GETCURRENTPATTERN,
VIEWMSG_FOLLOWSONG,
VIEWMSG_PATTERNLOOP,
VIEWMSG_GETCURRENTPOS,
VIEWMSG_SETRECORD,
VIEWMSG_SETSPACING,
VIEWMSG_PATTERNPROPERTIES,
VIEWMSG_SETVUMETERS,
VIEWMSG_SETPLUGINNAMES, //rewbs.patPlugNames
VIEWMSG_DOMIDISPACING,
VIEWMSG_EXPANDPATTERN,
VIEWMSG_SHRINKPATTERN,
VIEWMSG_COPYPATTERN,
VIEWMSG_PASTEPATTERN,
VIEWMSG_AMPLIFYPATTERN,
VIEWMSG_SETDETAIL,
// Sample-Specific
VIEWMSG_SETCURRENTSAMPLE,
VIEWMSG_SETMODIFIED,
VIEWMSG_PREPAREUNDO,
// Instrument-Specific
VIEWMSG_SETCURRENTINSTRUMENT,
VIEWMSG_DOSCROLL,
};
#define NUM_VUMETER_PENS 32
// Tab Order
enum OptionsPage
{
OPTIONS_PAGE_DEFAULT = 0,
OPTIONS_PAGE_GENERAL = OPTIONS_PAGE_DEFAULT,
OPTIONS_PAGE_SOUNDCARD,
OPTIONS_PAGE_MIXER,
OPTIONS_PAGE_PLAYER,
OPTIONS_PAGE_SAMPLEDITOR,
OPTIONS_PAGE_KEYBOARD,
OPTIONS_PAGE_COLORS,
OPTIONS_PAGE_MIDI,
OPTIONS_PAGE_PATHS,
OPTIONS_PAGE_UPDATE,
OPTIONS_PAGE_ADVANCED,
OPTIONS_PAGE_WINE,
};
/////////////////////////////////////////////////////////////////////////
// Player position notification
#define MAX_UPDATE_HISTORY 2000 // 2 seconds with 1 ms updates
OPENMPT_NAMESPACE_END
#include "Notification.h"
OPENMPT_NAMESPACE_BEGIN
OPENMPT_NAMESPACE_END
#include "CImageListEx.h"
#include "Mainbar.h"
#include "TrackerSettings.h"
OPENMPT_NAMESPACE_BEGIN
struct MODPLUGDIB;
template<> inline SettingValue ToSettingValue(const WINDOWPLACEMENT &val)
{
return SettingValue(EncodeBinarySetting<WINDOWPLACEMENT>(val), "WINDOWPLACEMENT");
}
template<> inline WINDOWPLACEMENT FromSettingValue(const SettingValue &val)
{
ASSERT(val.GetTypeTag() == "WINDOWPLACEMENT");
return DecodeBinarySetting<WINDOWPLACEMENT>(val.as<std::vector<std::byte> >());
}
class VUMeter
: public IMonitorInput
, public IMonitorOutput
{
public:
static constexpr std::size_t maxChannels = 4;
static const float dynamicRange; // corresponds to the current implementation of the UI widget diplaying the result
struct Channel
{
int32 peak = 0;
bool clipped = false;
};
private:
Channel channels[maxChannels];
int32 decayParam;
void Process(Channel &channel, MixSampleInt sample);
void Process(Channel &channel, MixSampleFloat sample);
public:
VUMeter() : decayParam(0) { SetDecaySpeedDecibelPerSecond(88.0f); }
void SetDecaySpeedDecibelPerSecond(float decibelPerSecond);
public:
const Channel & operator [] (std::size_t channel) const { return channels[channel]; }
void Process(mpt::audio_span_interleaved<const MixSampleInt> buffer);
void Process(mpt::audio_span_planar<const MixSampleInt> buffer);
void Process(mpt::audio_span_interleaved<const MixSampleFloat> buffer);
void Process(mpt::audio_span_planar<const MixSampleFloat> buffer);
void Decay(int32 secondsNum, int32 secondsDen);
void ResetClipped();
};
class TfLanguageProfileNotifySink : public ITfLanguageProfileNotifySink
{
public:
TfLanguageProfileNotifySink();
~TfLanguageProfileNotifySink();
HRESULT STDMETHODCALLTYPE OnLanguageChange(LANGID langid, __RPC__out BOOL *pfAccept) override;
HRESULT STDMETHODCALLTYPE OnLanguageChanged() override;
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override;
ULONG STDMETHODCALLTYPE AddRef() override;
ULONG STDMETHODCALLTYPE Release() override;
protected:
ITfInputProcessorProfiles *m_pProfiles = nullptr;
ITfSource *m_pSource = nullptr;
DWORD m_dwCookie = TF_INVALID_COOKIE;
};
class CMainFrame
: public CMDIFrameWnd
, public SoundDevice::CallbackBufferHandler<DithersOpenMPT>
, public SoundDevice::IMessageReceiver
, public TfLanguageProfileNotifySink
{
DECLARE_DYNAMIC(CMainFrame)
// static data
public:
// Globals
static OptionsPage m_nLastOptionsPage;
static HHOOK ghKbdHook;
// GDI
static HICON m_hIcon;
static HFONT m_hGUIFont, m_hFixedFont;
static HPEN penDarkGray, penHalfDarkGray, penGray99;
static HCURSOR curDragging, curNoDrop, curArrow, curNoDrop2, curVSplit;
static MODPLUGDIB *bmpNotes, *bmpVUMeters, *bmpPluginVUMeters;
static COLORREF gcolrefVuMeter[NUM_VUMETER_PENS * 2]; // General tab VU meters
public:
// Low-Level Audio
CriticalSection m_SoundDeviceFillBufferCriticalSection;
Util::MultimediaClock m_SoundDeviceClock;
SoundDevice::IBase *gpSoundDevice = nullptr;
UINT_PTR m_NotifyTimer = 0;
VUMeter m_VUMeterInput;
VUMeter m_VUMeterOutput;
DWORD m_AudioThreadId = 0;
bool m_InNotifyHandler = false;
// Midi Input
public:
static HMIDIIN shMidiIn;
public:
CImageListEx m_MiscIcons, m_MiscIconsDisabled; // Misc Icons
CImageListEx m_PatternIcons, m_PatternIconsDisabled; // Pattern icons (includes some from sample editor as well...)
CImageListEx m_EnvelopeIcons; // Instrument editor icons
CImageListEx m_SampleIcons; // Sample editor icons
protected:
CModTreeBar m_wndTree;
CStatusBar m_wndStatusBar;
CMainToolBar m_wndToolBar;
CSoundFile *m_pSndFile = nullptr; // != NULL only when currently playing or rendering
HWND m_hWndMidi = nullptr;
CSoundFile::samplecount_t m_dwTimeSec = 0;
UINT_PTR m_nTimer = 0;
UINT m_nAvgMixChn = 0, m_nMixChn = 0;
// Misc
class COptionsSoundcard *m_SoundCardOptionsDialog = nullptr;
#if defined(MPT_ENABLE_UPDATE)
class CUpdateSetupDlg *m_UpdateOptionsDialog = nullptr;
std::unique_ptr<UpdateCheckResult> m_updateCheckResult;
#endif // MPT_ENABLE_UPDATE
DWORD helpCookie = 0;
bool m_bOptionsLocked = false;
// Notification Buffer
mpt::mutex m_NotificationBufferMutex; // to avoid deadlocks, this mutex should only be taken as a innermost lock, i.e. do not block on anything while holding this mutex
Util::fixed_size_queue<Notification,MAX_UPDATE_HISTORY> m_NotifyBuffer;
// Instrument preview in tree view
CSoundFile m_WaveFile;
TCHAR m_szUserText[512], m_szInfoText[512], m_szXInfoText[512];
CAutoSaver m_AutoSaver;
public:
CWnd *m_pNoteMapHasFocus = nullptr;
CWnd *m_pOrderlistHasFocus = nullptr;
bool m_bModTreeHasFocus = false;
public:
CMainFrame(/*CString regKeyExtension*/);
void Initialize();
// Low-Level Audio
public:
static void UpdateDspEffects(CSoundFile &sndFile, bool reset=false);
static void UpdateAudioParameters(CSoundFile &sndFile, bool reset=false);
// from SoundDevice::IBufferHandler
uint64 SoundCallbackGetReferenceClockNowNanoseconds() const override;
void SoundCallbackPreStart() override;
void SoundCallbackPostStop() override;
bool SoundCallbackIsLockedByCurrentThread() const override;
void SoundCallbackLock() override;
uint64 SoundCallbackLockedGetReferenceClockNowNanoseconds() const override;
void SoundCallbackLockedProcessPrepare(SoundDevice::TimeInfo timeInfo) override;
void SoundCallbackLockedCallback(SoundDevice::CallbackBuffer<DithersOpenMPT> &buffer) override;
void SoundCallbackLockedProcessDone(SoundDevice::TimeInfo timeInfo) override;
void SoundCallbackUnlock() override;
// from SoundDevice::IMessageReceiver
void SoundDeviceMessage(LogLevel level, const mpt::ustring &str) override;
bool InGuiThread() const { return theApp.InGuiThread(); }
bool InAudioThread() const { return GetCurrentThreadId() == m_AudioThreadId; }
bool InNotifyHandler() const { return m_InNotifyHandler; }
bool audioOpenDevice();
void audioCloseDevice();
bool IsAudioDeviceOpen() const;
bool DoNotification(DWORD dwSamplesRead, int64 streamPosition);
// Midi Input Functions
public:
bool midiOpenDevice(bool showSettings = true);
void midiCloseDevice();
void SetMidiRecordWnd(HWND hwnd) { m_hWndMidi = hwnd; }
HWND GetMidiRecordWnd() const { return m_hWndMidi; }
static int ApplyVolumeRelatedSettings(const DWORD &dwParam1, const BYTE midivolume);
// static functions
public:
static CMainFrame *GetMainFrame() { return (CMainFrame *)theApp.m_pMainWnd; }
static void UpdateColors();
static HICON GetModIcon() { return m_hIcon; }
static HFONT GetGUIFont() { return m_hGUIFont; }
static HFONT &GetCommentsFont() { return m_hFixedFont; }
static void UpdateAllViews(UpdateHint hint, CObject *pHint=NULL);
static LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam);
static CInputHandler *m_InputHandler;
// Misc functions
public:
void SetUserText(LPCTSTR lpszText);
void SetInfoText(LPCTSTR lpszText);
void SetXInfoText(LPCTSTR lpszText);
void SetHelpText(LPCTSTR lpszText);
UINT GetBaseOctave() const;
CModDoc *GetActiveDoc() const;
CView *GetActiveView() const;
void OnDocumentCreated(CModDoc *pModDoc);
void OnDocumentClosed(CModDoc *pModDoc);
void UpdateTree(CModDoc *pModDoc, UpdateHint hint, CObject *pHint = nullptr);
void RefreshDlsBanks();
static CInputHandler* GetInputHandler() { return m_InputHandler; }
void SetElapsedTime(double t) { m_dwTimeSec = static_cast<CSoundFile::samplecount_t>(t); }
#if defined(MPT_ENABLE_UPDATE)
bool ShowUpdateIndicator(const UpdateCheckResult &result, const CString &releaseVersion, const CString &infoURL, bool showHighlight);
#endif // MPT_ENABLE_UPDATE
CModTree *GetUpperTreeview() { return m_wndTree.m_pModTree; }
CModTree *GetLowerTreeview() { return m_wndTree.m_pModTreeData; }
bool SetTreeSoundfile(FileReader &file) { return m_wndTree.SetTreeSoundfile(file); }
void CreateExampleModulesMenu();
void CreateTemplateModulesMenu();
CMenu *GetFileMenu() const;
// Creates submenu whose items are filenames of files in both
// AppDirectory\folderName\ (usually C:\Program Files\OpenMPT\folderName\)
// and
// ConfigDirectory\folderName (usually %appdata%\OpenMPT\folderName\)
// [in] maxCount: Maximum number of items allowed in the menu
// [out] paths: Receives the full paths of the files added to the menu.
// [in] folderName: Name of the folder
// [in] idRangeBegin: First ID for the menu item.
static HMENU CreateFileMenu(const size_t maxCount, std::vector<mpt::PathString>& paths, const mpt::PathString &folderName, const uint16 idRangeBegin);
// Player functions
public:
// High-level synchronous playback functions, do not hold AudioCriticalSection while calling these
bool PreparePlayback();
bool StartPlayback();
void StopPlayback();
bool RestartPlayback();
bool PausePlayback();
static bool IsValidSoundFile(CSoundFile &sndFile) { return sndFile.GetType() ? true : false; }
static bool IsValidSoundFile(CSoundFile *pSndFile) { return pSndFile && pSndFile->GetType(); }
void SetPlaybackSoundFile(CSoundFile *pSndFile);
void UnsetPlaybackSoundFile();
void GenerateStopNotification();
bool PlayMod(CModDoc *);
bool StopMod(CModDoc *pDoc = nullptr);
bool PauseMod(CModDoc *pDoc = nullptr);
bool StopSoundFile(CSoundFile *);
bool PlaySoundFile(CSoundFile *);
bool PlaySoundFile(const mpt::PathString &filename, ModCommand::NOTE note, int volume = -1);
bool PlaySoundFile(CSoundFile &sndFile, INSTRUMENTINDEX nInstrument, SAMPLEINDEX nSample, ModCommand::NOTE note, int volume = -1);
bool PlayDLSInstrument(const CDLSBank &bank, UINT instr, UINT region, ModCommand::NOTE note, int volume = -1);
void InitPreview();
void PreparePreview(ModCommand::NOTE note, int volume);
void StopPreview() { StopSoundFile(&m_WaveFile); }
void PlayPreview() { PlaySoundFile(&m_WaveFile); }
inline bool IsPlaying() const { return m_pSndFile != nullptr; }
// Return currently playing module (nullptr if none is playing)
inline CModDoc *GetModPlaying() const { return m_pSndFile ? m_pSndFile->GetpModDoc() : nullptr; }
// Return currently playing module (nullptr if none is playing)
inline CSoundFile *GetSoundFilePlaying() const { return m_pSndFile; }
BOOL InitRenderer(CSoundFile*);
BOOL StopRenderer(CSoundFile*);
void SwitchToActiveView();
void IdleHandlerSounddevice();
BOOL ResetSoundCard();
BOOL SetupSoundCard(SoundDevice::Settings deviceSettings, SoundDevice::Identifier deviceIdentifier, SoundDeviceStopMode stoppedMode, bool forceReset = false);
BOOL SetupMiscOptions();
BOOL SetupPlayer();
void SetupMidi(DWORD d, UINT n);
HWND GetFollowSong() const;
HWND GetFollowSong(const CModDoc *pDoc) const { return (pDoc == GetModPlaying()) ? GetFollowSong() : nullptr; }
void ResetNotificationBuffer();
// Notify accessbility software that it should read out updated UI elements
void NotifyAccessibilityUpdate(CWnd &source);
// Overrides
protected:
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CMainFrame)
BOOL PreCreateWindow(CREATESTRUCT& cs) override;
BOOL PreTranslateMessage(MSG *pMsg) override;
BOOL DestroyWindow() override;
void OnUpdateFrameTitle(BOOL bAddToTitle) override;
//}}AFX_VIRTUAL
/// Opens either template or example menu item.
void OpenMenuItemFile(const UINT nId, const bool isTemplateFile);
public:
void UpdateMRUList();
// Implementation
public:
~CMainFrame() override;
#ifdef _DEBUG
void AssertValid() const override;
void Dump(CDumpContext& dc) const override;
#endif
void OnTimerGUI();
void OnTimerNotify();
// Message map functions
//{{AFX_MSG(CMainFrame)
public:
afx_msg void OnAddDlsBank();
afx_msg void OnImportMidiLib();
afx_msg void OnViewOptions();
protected:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnRButtonDown(UINT, CPoint);
afx_msg void OnClose();
afx_msg void OnTimer(UINT_PTR);
afx_msg void OnPluginManager();
afx_msg void OnClipboardManager();
afx_msg LRESULT OnViewMIDIMapping(WPARAM wParam, LPARAM lParam);
afx_msg void OnUpdateTime(CCmdUI *pCmdUI);
afx_msg void OnUpdateUser(CCmdUI *pCmdUI);
afx_msg void OnUpdateInfo(CCmdUI *pCmdUI);
afx_msg void OnUpdateXInfo(CCmdUI *pCmdUI);
afx_msg void OnUpdateMidiRecord(CCmdUI *pCmdUI);
afx_msg void OnPlayerPause();
afx_msg void OnMidiRecord();
afx_msg void OnPrevOctave();
afx_msg void OnNextOctave();
afx_msg void OnPanic();
afx_msg void OnReportBug();
afx_msg BOOL OnInternetLink(UINT nID);
afx_msg LRESULT OnUpdatePosition(WPARAM, LPARAM lParam);
afx_msg LRESULT OnUpdateViews(WPARAM modDoc, LPARAM hint);
afx_msg LRESULT OnSetModified(WPARAM modDoc, LPARAM);
afx_msg void OnOpenTemplateModule(UINT nId);
afx_msg void OnExampleSong(UINT nId);
afx_msg void OnOpenMRUItem(UINT nId);
afx_msg void OnUpdateMRUItem(CCmdUI *cmd);
afx_msg LRESULT OnInvalidatePatterns(WPARAM, LPARAM);
afx_msg LRESULT OnCustomKeyMsg(WPARAM, LPARAM);
afx_msg void OnInternetUpdate();
afx_msg void OnUpdateAvailable();
afx_msg void OnShowSettingsFolder();
#if defined(MPT_ENABLE_UPDATE)
afx_msg LRESULT OnUpdateCheckStart(WPARAM wparam, LPARAM lparam);
afx_msg LRESULT OnUpdateCheckProgress(WPARAM wparam, LPARAM lparam);
afx_msg LRESULT OnUpdateCheckCanceled(WPARAM wparam, LPARAM lparam);
afx_msg LRESULT OnUpdateCheckFailure(WPARAM wparam, LPARAM lparam);
afx_msg LRESULT OnUpdateCheckSuccess(WPARAM wparam, LPARAM lparam);
afx_msg LRESULT OnToolbarUpdateIndicatorClick(WPARAM wparam, LPARAM lparam);
#endif // MPT_ENABLE_UPDATE
afx_msg void OnHelp();
afx_msg BOOL OnDeviceChange(UINT nEventType, DWORD_PTR dwData);
afx_msg void OnDropFiles(HDROP hDropInfo);
afx_msg BOOL OnQueryEndSession();
afx_msg void OnActivateApp(BOOL active, DWORD threadID);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnInitMenu(CMenu *pMenu);
bool UpdateEffectKeys(const CModDoc *modDoc);
afx_msg void OnKillFocus(CWnd* pNewWnd);
afx_msg void OnShowWindow(BOOL bShow, UINT nStatus);
// Defines maximum number of items in example modules menu.
static constexpr size_t nMaxItemsInExampleModulesMenu = 50;
static constexpr size_t nMaxItemsInTemplateModulesMenu = 50;
private:
/// Array of paths of example modules that are available from help menu.
std::vector<mpt::PathString> m_ExampleModulePaths;
/// Array of paths of template modules that are available from file menu.
std::vector<mpt::PathString> m_TemplateModulePaths;
};
/////////////////////////////////////////////////////////////////////////////
//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
OPENMPT_NAMESPACE_END