Example #1
0
// When trying to play a note using this plugin, but no instrument is assigned to it,
// the user is asked whether a new instrument should be added.
bool CAbstractVstEditor::ValidateCurrentInstrument()
//--------------------------------------------------
{
    if (!CheckInstrument(m_nInstrument))
            m_nInstrument = GetBestInstrumentCandidate();

    //only show messagebox if plug is able to process notes.
    if(m_nInstrument < 0)
    {
            if(m_pVstPlugin->CanRecieveMidiEvents())
            {
                    CModDoc *pModDoc = m_pVstPlugin->GetModDoc();
                    if(pModDoc == nullptr)
                            return false;

                    if(!m_pVstPlugin->isInstrument() || pModDoc->GetSoundFile()->GetModSpecifications().instrumentsMax == 0 ||
                            AfxMessageBox(_T("You need to assign an instrument to this plugin before you can play notes from here.\nCreate a new instrument and assign this plugin to the instrument?"), MB_YESNO | MB_ICONQUESTION) == IDNO)
                    {
                            return false;
                    } else
                    {
                            return CreateInstrument();
                    }
            } else
            {
                    // Can't process notes
                    return false;
            }
    } else
    {
            return true;
    }

}
Example #2
0
void CAbstractVstEditor::OnToggleEditor(UINT nID)
{
    CModDoc* pModDoc = m_pVstPlugin->GetModDoc();

    if (pModDoc) {
            pModDoc->TogglePluginEditor(nID-ID_PLUGSELECT);
    }
}
Example #3
0
void CAbstractVstEditor::UpdateInputMenu()
{
     CMenu* pInfoMenu = m_pMenu->GetSubMenu(2);
    pInfoMenu->DeleteMenu(0, MF_BYPOSITION);

    CModDoc* pModDoc = m_pVstPlugin->GetModDoc();
    module_renderer* pSndFile = pModDoc->GetSoundFile();

    if (m_pInputMenu->m_hMenu)        {
            m_pInputMenu->DestroyMenu();
    }
    if (!m_pInputMenu->m_hMenu) {
            m_pInputMenu->CreatePopupMenu();
    }

    CString name;

    CArray<CVstPlugin*, CVstPlugin*> inputPlugs;
    m_pVstPlugin->GetInputPlugList(inputPlugs);
    for (int nPlug=0; nPlug<inputPlugs.GetSize(); nPlug++) {
            name.Format("FX%02d: %s", inputPlugs[nPlug]->m_nSlot+1, inputPlugs[nPlug]->m_pMixStruct->Info.szName);
            m_pInputMenu->AppendMenu(MF_STRING, ID_PLUGSELECT+inputPlugs[nPlug]->m_nSlot, name);
    }

    CArray<UINT, UINT> inputChannels;
    m_pVstPlugin->GetInputChannelList(inputChannels);
    for (int nChn=0; nChn<inputChannels.GetSize(); nChn++) {
            if (nChn==0 && inputPlugs.GetSize()) {
                    m_pInputMenu->AppendMenu(MF_SEPARATOR);
            }
            name.Format("Chn%02d: %s", inputChannels[nChn]+1, pSndFile->ChnSettings[inputChannels[nChn]].szName);
            m_pInputMenu->AppendMenu(MF_STRING, NULL, name);
    }

    CArray<UINT, UINT> inputInstruments;
    m_pVstPlugin->GetInputInstrumentList(inputInstruments);
    bool checked;
    for (int nIns=0; nIns<inputInstruments.GetSize(); nIns++) {
            checked=false;
            if (nIns==0 && (inputPlugs.GetSize() || inputChannels.GetSize())) {
                    m_pInputMenu->AppendMenu(MF_SEPARATOR);
            }
            name.Format("Ins%02d: %s", inputInstruments[nIns], (LPCTSTR)pSndFile->GetInstrumentName(inputInstruments[nIns]));
            if (inputInstruments[nIns] == (UINT)m_nInstrument)        checked=true;
            m_pInputMenu->AppendMenu(MF_STRING|(checked?MF_CHECKED:0), ID_SELECTINST+inputInstruments[nIns], name);
    }

    if ((inputPlugs.GetSize() == 0) &&
            (inputChannels.GetSize() == 0) &&
            (inputInstruments.GetSize() == 0)) {
            m_pInputMenu->AppendMenu(MF_STRING|MF_GRAYED, NULL, "None");
    }

    pInfoMenu->InsertMenu(0, MF_BYPOSITION|MF_POPUP, (UINT)m_pInputMenu->m_hMenu, "I&nputs");
}
Example #4
0
void CChildFrame::ActivateFrame(int nCmdShow)
//-------------------------------------------
{
    if ((glMdiOpenCount == 1) && (CMainFrame::gbMdiMaximize) && (nCmdShow == -1))
    {
            nCmdShow = SW_SHOWMAXIMIZED;
    }
    CMDIChildWnd::ActivateFrame(nCmdShow);


    //rewbs.fix3185: When song first loads, initialise patternViewState
    //               to point to start of song.
    CView *pView = GetActiveView();
    CModDoc *pModDoc = NULL;
    if (pView) pModDoc = (CModDoc *)pView->GetDocument();
    if ((m_hWndCtrl) && (pModDoc))
    {
            if (m_bInitialActivation && m_ViewPatterns.nPattern==0)
            {
                    m_ViewPatterns.nPattern=pModDoc->GetSoundFile()->Order[0];
                    m_ViewPatterns.nOrder=0; //just in case (should already be 0)
                    m_ViewPatterns.nRow=0;   //just in case
                    m_bInitialActivation=false;
            }
        if (!pattern_test) {
            DEBUG_FUNC("creating my homie");
            DEBUG_FUNC("pModDoc = %p", pModDoc);
            auto pSndFile = pModDoc->GetSoundFile();
            if (pSndFile) {
                pattern_test = new modplug::gui::qt5::document_window(
                    pSndFile,
                    CMainFrame::GetMainFrame()->global_config,
                    nullptr
                );
                /*
                pattern_test->resize(400, 400);
                pattern_test->show();
                */
                CMainFrame::GetMainFrame()
                    ->ui_root
                    ->mainwindow.add_document_window(pattern_test);
            }
        }
    }
    //end rewbs.fix3185
}
Example #5
0
void CViewComments::OnInitialUpdate()
//-----------------------------------
{
	if(m_nListId == 0)
	{
		m_nListId = IDC_LIST_SAMPLES;

		// For XM, set the instrument list as the default list
		CModDoc *pModDoc = GetDocument();
		CSoundFile *pSndFile;
		if(pModDoc)
		{
			pSndFile= pModDoc->GetSoundFile();
			if(pSndFile && (pSndFile->m_nType & MOD_TYPE_XM) && pSndFile->m_nInstruments > 0)
			{
				m_nListId = IDC_LIST_INSTRUMENTS;
			}
		}
	}

	CChildFrame *pFrame = (CChildFrame *)GetParentFrame();
	CRect rect;

	if (pFrame)
	{
		COMMENTVIEWSTATE *pState = pFrame->GetCommentViewState();
		if (pState->cbStruct == sizeof(COMMENTVIEWSTATE))
		{
			m_nListId = pState->nId;
		}
	}
	GetClientRect(&rect);
	m_ToolBar.Create(WS_CHILD|WS_VISIBLE|CCS_NOPARENTALIGN, rect, this, IDC_TOOLBAR_DETAILS);
	m_ToolBar.Init(CMainFrame::GetMainFrame()->m_MiscIcons, CMainFrame::GetMainFrame()->m_MiscIconsDisabled);
	m_ItemList.Create(WS_CHILD|WS_VISIBLE|LVS_REPORT|LVS_SINGLESEL|LVS_EDITLABELS|LVS_NOSORTHEADER, rect, this, IDC_LIST_DETAILS);
	m_ItemList.ModifyStyleEx(0, WS_EX_STATICEDGE);
	// Add ToolBar Buttons
	m_ToolBar.AddButton(IDC_LIST_SAMPLES, IMAGE_SAMPLES);
	m_ToolBar.AddButton(IDC_LIST_INSTRUMENTS, IMAGE_INSTRUMENTS);
	//m_ToolBar.AddButton(IDC_LIST_PATTERNS, TIMAGE_TAB_PATTERNS);
	m_ToolBar.SetIndent(4);
	UpdateButtonState();
	OnUpdate(NULL, HINT_MODTYPE, NULL);
}
Example #6
0
LRESULT CChildFrame::OnInstrumentSelected(WPARAM wParam, LPARAM lParam)
//---------------------------------------------------------------------
{
    CView *pView = GetActiveView();
    CModDoc *pModDoc = NULL;
    if (pView) pModDoc = (CModDoc *)pView->GetDocument();
    if ((m_hWndCtrl) && (pModDoc))
    {
            module_renderer *pSndFile = pModDoc->GetSoundFile();
            UINT nIns = lParam;

            if ((!wParam) && (pSndFile->m_nInstruments > 0))
            {
                    nIns = pModDoc->FindSampleParent(nIns);
            }
            ::SendMessage(m_hWndCtrl, WM_MOD_CTRLMSG, CTRLMSG_PAT_SETINSTRUMENT, nIns);
    }
    return 0;
}
Example #7
0
void CModScrollView::OnDestroy()
//------------------------------
{
    CModDoc *pModDoc = GetDocument();
    CMainFrame *pMainFrm = CMainFrame::GetMainFrame();
    if ((pMainFrm) && (pModDoc))
    {
        if (pMainFrm->GetFollowSong(pModDoc) == m_hWnd)
        {
            pMainFrm->SetFollowSong(pModDoc, NULL, FALSE);
            pModDoc->SetFollowWnd(NULL, 0);
        }
        if (pMainFrm->GetMidiRecordWnd() == m_hWnd)
        {
            pMainFrm->SetMidiRecordWnd(NULL);
        }
    }
    CScrollView::OnDestroy();
}
Example #8
0
void CModControlView::UpdateView(uint32_t lHint, CObject *pObject)
//-------------------------------------------------------------
{
    CWnd *pActiveDlg = NULL;
    CModDoc *pDoc = GetDocument();
    if (!pDoc) return;
    // Module type changed: update tabs
    if (lHint & HINT_MODTYPE)
    {
        module_renderer *pSndFile = pDoc->GetSoundFile();
        UINT nCount = 4;
        UINT nType = pSndFile->GetType();
        UINT mask = 1 | 2 | 4;

        if (nType & (MOD_TYPE_XM|MOD_TYPE_IT|MOD_TYPE_MPT))
        {
            mask |= 8;
            //mask |= 32; //rewbs.graph
            nCount ++;
        }
        if (nCount != (UINT)m_TabCtrl.GetItemCount())
        {
            UINT count = 0;
            if ((m_nActiveDlg >= 0) && (m_nActiveDlg < MAX_PAGES))
            {
                pActiveDlg = m_Pages[m_nActiveDlg];
                if (pActiveDlg) pActiveDlg->ShowWindow(SW_HIDE);
            }
            m_TabCtrl.DeleteAllItems();
            if (mask & 1) m_TabCtrl.InsertItem(count++, "General", IDD_CONTROL_GLOBALS, IMAGE_GENERAL);
            if (mask & 2) m_TabCtrl.InsertItem(count++, "Patterns", IDD_CONTROL_PATTERNS, IMAGE_PATTERNS);
        }
    }
    // Update child dialogs
    for (UINT nIndex=0; nIndex<MAX_PAGES; nIndex++)
    {
        CModControlDlg *pDlg = m_Pages[nIndex];
        if ((pDlg) && (pObject != pDlg)) pDlg->UpdateView(lHint, pObject);
    }
    // Restore the displayed child dialog
    if (pActiveDlg) pActiveDlg->ShowWindow(SW_SHOW);
}
Example #9
0
// Test file loading and saving
void TestLoadSaveFile()
//---------------------
{
    CString theFile = theApp.GetAppDirPath();
    // Only run the tests when we're in the project directory structure.
    if(theFile.Mid(theFile.GetLength() - 6, 5) != "Debug")
            return;
    theFile.Delete(theFile.GetLength() - 6, 6);
    theFile.Append("../test/test.");

    // Test file loading
    CModDoc *pModDoc = (CModDoc *)theApp.OpenDocumentFile(theFile + "mptm");
    TestLoadFile(pModDoc);

    // Test file saving
     pModDoc->DoSave(theFile + "saved.mptm");
    pModDoc->OnCloseDocument();

    // Reload the saved file and test if everything is still working correctly.
    pModDoc = (CModDoc *)theApp.OpenDocumentFile(theFile + "saved.mptm");
    TestLoadFile(pModDoc);
    pModDoc->OnCloseDocument();
}
Example #10
0
// Try to set up a new instrument that is linked to the current plugin.
bool CAbstractVstEditor::CreateInstrument()
//-----------------------------------------
{
    CModDoc *pModDoc = m_pVstPlugin->GetModDoc();
    module_renderer *pSndFile = m_pVstPlugin->GetSoundFile();
    if(pModDoc == nullptr || pSndFile == nullptr)
    {
            return false;
    }

    bool bFirst = (pSndFile->GetNumInstruments() == 0);
    modplug::tracker::instrumentindex_t nIns = pModDoc->InsertInstrument(0);
    if(nIns == modplug::tracker::InstrumentIndexInvalid)
    {
            return false;
    }

    modplug::tracker::modinstrument_t *pIns = pSndFile->Instruments[nIns];
    m_nInstrument = nIns;

    _snprintf(pIns->name, CountOf(pIns->name) - 1, _T("%d: %s"), m_pVstPlugin->GetSlot() + 1, pSndFile->m_MixPlugins[m_pVstPlugin->GetSlot()].Info.szName);
    strncpy(pIns->legacy_filename, pSndFile->m_MixPlugins[m_pVstPlugin->GetSlot()].Info.szLibraryName, CountOf(pIns->legacy_filename) - 1);
    pIns->nMixPlug = (PLUGINDEX)m_pVstPlugin->GetSlot() + 1;
    pIns->midi_channel = 1;
    // People will forget to change this anyway, so the following lines can lead to some bad surprises after re-opening the module.
    //pIns->wMidiBank = (uint16_t)((m_pVstPlugin->GetCurrentProgram() >> 7) + 1);
    //pIns->nMidiProgram = (uint8_t)((m_pVstPlugin->GetCurrentProgram() & 0x7F) + 1);

    pModDoc->UpdateAllViews(NULL, (nIns << HINT_SHIFT_INS) | HINT_INSTRUMENT | HINT_INSNAMES | HINT_ENVELOPE | (bFirst ? HINT_MODTYPE : 0));
    if(pSndFile->GetModSpecifications().supportsPlugins)
    {
            pModDoc->SetModified();
    }

    return true;
}
Example #11
0
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
Example #12
0
void CAbstractVstEditor::UpdateMacroMenu()
//----------------------------------------
{
    CString label, macroName, macroText;
    char paramName[128];
    bool greyed;
    int macroType,nParam,action;

    CModDoc* pModDoc = m_pVstPlugin->GetModDoc();
    if (!pModDoc)
    {
            return;
    }

     CMenu* pInfoMenu = m_pMenu->GetSubMenu(2);
    pInfoMenu->DeleteMenu(2, MF_BYPOSITION);

    if (m_pMacroMenu->m_hMenu)
    {
            m_pMacroMenu->DestroyMenu();
    }
    if (!m_pMacroMenu->m_hMenu)
    {
            m_pMacroMenu->CreatePopupMenu();
    }

    for (int nMacro=0; nMacro<NUM_MACROS; nMacro++)
    {
            action=NULL;
            greyed=true;
            macroText = pModDoc->GetSoundFile()->m_MidiCfg.szMidiSFXExt[nMacro];
             macroType = pModDoc->GetMacroType(macroText);

            switch (macroType)        {
                    case sfx_unused:
                            macroName = "Unused. Learn Param...";
                            action=ID_LEARN_MACRO_FROM_PLUGGUI+nMacro;
                            greyed=false;
                            break;
                    case sfx_cutoff:
                            macroName = "Set Filter Cutoff";
                            break;
                    case sfx_reso:
                            macroName = "Set Filter Resonance";
                            break;
                    case sfx_mode: macroName = "Set Filter Mode";
                            break;
                    case sfx_drywet:
                            macroName = "Set plugin dry/wet ratio";
                            greyed=false;
                            break;
                    case sfx_plug:
                    {
                             nParam = pModDoc->MacroToPlugParam(macroText);
                            m_pVstPlugin->GetParamName(nParam, paramName, sizeof(paramName));

                            if (paramName[0] == 0) {
                                    strcpy(paramName, "N/A for this plug");
                            } else {
                                    greyed=false;
                            }

                            macroName.Format("%d - %s", nParam, paramName);
                            break;
                    }
                    case sfx_custom:
                    default:
                            macroName.Format("Custom: %s", macroText);
                            greyed=false;

            }
            label.Format("SF%X: %s", nMacro, macroName);
            m_pMacroMenu->AppendMenu(MF_STRING|(greyed?MF_GRAYED:0), action, label);
    }

    pInfoMenu->InsertMenu(2, MF_BYPOSITION|MF_POPUP, (UINT)m_pMacroMenu->m_hMenu, "&Macros");
}
Example #13
0
// Test PC note serialization
void TestPCnoteSerialization()
//----------------------------
{
    theApp.OnFileNewMPT();
    CMainFrame* pMainFrm = CMainFrame::GetMainFrame();
    if(pMainFrm == nullptr)
            throw(std::runtime_error("pMainFrm is nullptr"));
    CModDoc* pModDoc = pMainFrm->GetActiveDoc();
    if(pModDoc == nullptr)
            throw(std::runtime_error("pModdoc is nullptr"));

    module_renderer* pSndFile = pModDoc->GetSoundFile();
    if(pSndFile == nullptr)
            throw(std::runtime_error("pSndFile is nullptr"));

    // Set maximum number of channels.
    pModDoc->ReArrangeChannels(std::vector<modplug::tracker::chnindex_t>(ModSpecs::mptm.channelsMax , 0));

    pSndFile->Patterns.Remove(0);
    pSndFile->Patterns.Insert(0, ModSpecs::mptm.patternRowsMin);
    pSndFile->Patterns.Insert(1, 64);
    GenerateCommands(pSndFile->Patterns[1], 0.3, 0.3);
    pSndFile->Patterns.Insert(2, ModSpecs::mptm.patternRowsMax);
    GenerateCommands(pSndFile->Patterns[2], 0.5, 0.5);

    //
    vector<modplug::tracker::modevent_t> pat[3];
    const size_t numCommands[] = {        pSndFile->GetNumChannels() * pSndFile->Patterns[0].GetNumRows(),
                                                                    pSndFile->GetNumChannels() * pSndFile->Patterns[1].GetNumRows(),
                                                                    pSndFile->GetNumChannels() * pSndFile->Patterns[2].GetNumRows()
                                                             };
    pat[0].resize(numCommands[0]);
    pat[1].resize(numCommands[1]);
    pat[2].resize(numCommands[2]);

    for(size_t i = 0; i<3; i++) // Copy pattern data for comparison.
    {
            CPattern::const_iterator iter = pSndFile->Patterns[i].Begin();
            for(size_t j = 0; j < numCommands[i]; j++, iter++) pat[i][j] = *iter;
    }

    std::strstream mem;
    WriteModPatterns(mem, pSndFile->Patterns);

    VERIFY_EQUAL_NONCONT( mem.good(), true );

    // Clear patterns.
    pSndFile->Patterns[0].ClearCommands();
    pSndFile->Patterns[1].ClearCommands();
    pSndFile->Patterns[2].ClearCommands();

    // Read data back.
    ReadModPatterns(mem, pSndFile->Patterns);

    // Compare.
    VERIFY_EQUAL_NONCONT( pSndFile->Patterns[0].GetNumRows(), ModSpecs::mptm.patternRowsMin);
    VERIFY_EQUAL_NONCONT( pSndFile->Patterns[1].GetNumRows(), 64);
    VERIFY_EQUAL_NONCONT( pSndFile->Patterns[2].GetNumRows(), ModSpecs::mptm.patternRowsMax);
    for(size_t i = 0; i < 3; i++)
    {
            bool bPatternDataMatch = true;
            CPattern::const_iterator iter = pSndFile->Patterns[i].Begin();
            for(size_t j = 0; j < numCommands[i]; j++, iter++)
            {
                    if(pat[i][j] != *iter)
                    {
                            bPatternDataMatch = false;
                            break;
                    }
            }
            VERIFY_EQUAL( bPatternDataMatch, true);
    }

    pModDoc->OnCloseDocument();
}