// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved. #include "stdafx.h" #include "multichan.h" #include "DlgDest.h" #include "dlgsrc.h" #include "childview.h" #include #include #include #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif /* * CDlgDest * MFC stuff */ ///////////////////////////////////////////////////////////////////////////// // CDlgDest dialog void CDlgDest::DoDataExchange ( CDataExchange* pDX ) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CDlgDest) DDX_Control(pDX, IDC_SPEAKERFLAGS, m_cChannelMask); DDX_Control(pDX, IDC_AVEBITS, m_cAveBitsPerSec); DDX_Control(pDX, IDC_REMIX, m_cRemix); DDX_Control(pDX, IDC_EDIT_VALIDBITS, m_cValidBits); DDX_Control(pDX, IDC_CHANNELS, m_cChannels); DDX_Control(pDX, IDC_OUTPUT, m_cOutput); DDX_Control(pDX, IDC_COMBO_BITDEPTH, m_comboBitDepth); DDX_Control(pDX, IDC_COMBO_SAMPLERATE, m_comboSampleRate); DDX_Control(pDX, IDC_COMBO_WAVEFORMAT, m_comboFormat); DDX_Control(pDX, IDC_PLAY, m_butPlay); DDX_Control(pDX, IDC_STOP, m_butStop); DDX_Text(pDX, IDC_EDIT_VALIDBITS, m_wValidBitsPerSample); DDV_MinMaxUInt(pDX, m_wValidBitsPerSample, 0, 65535); DDX_Text(pDX, IDC_SPEAKERFLAGS, m_strChannelMask); //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CDlgDest, CDialog) //{{AFX_MSG_MAP(CDlgDest) ON_BN_CLICKED(IDC_REMIX, OnRemix) ON_BN_CLICKED(IDC_PLAY, OnPlay) ON_BN_CLICKED(IDC_STOP, OnStop) ON_CBN_SELENDOK(IDC_COMBO_SAMPLERATE, OnComboSamplerate) ON_CBN_SELENDOK(IDC_COMBO_WAVEFORMAT, OnComboWaveformat) ON_CBN_SELENDOK(IDC_COMBO_BITDEPTH, OnComboBitdepth) ON_EN_KILLFOCUS(IDC_EDIT_VALIDBITS, OnEditValidbits) ON_WM_CTLCOLOR() ON_WM_LBUTTONDOWN() ON_WM_LBUTTONUP() ON_WM_MOUSEMOVE() //}}AFX_MSG_MAP END_MESSAGE_MAP() /* * CDlgDest * construction */ // ---------------------------------------------------------------------------------- // constructor // ---------------------------------------------------------------------------------- CDlgDest::CDlgDest ( CWnd* pParent /*=NULL*/ ) : CDialog(CDlgDest::IDD, pParent), m_pwndParent(pParent), m_pbData(NULL), m_fPlayable(FALSE), m_fDragging(FALSE), m_cbData( 0 ), m_lpwhdr( 0 ) { //{{AFX_DATA_INIT(CDlgDest) m_wValidBitsPerSample = 0; m_strChannelMask = _T("0x00000000"); //}}AFX_DATA_INIT m_wfExt.Format.wFormatTag = WAVE_FORMAT_PCM; m_wfExt.Format.cbSize = 0; m_wfExt.Format.wBitsPerSample = 16; m_wfExt.Format.nChannels = 0; m_wfExt.Format.nBlockAlign = 4; m_wfExt.Format.nSamplesPerSec = 44100; m_wfExt.Format.nAvgBytesPerSec = m_wfExt.Format.nBlockAlign * m_wfExt.Format.nSamplesPerSec; m_wfExt.Samples.wValidBitsPerSample = 16; m_wfExt.dwChannelMask = 0; m_wfExt.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; m_wValidBitsPerSample = m_wfExt.Samples.wValidBitsPerSample; } // ---------------------------------------------------------------------------------- // destructor // ---------------------------------------------------------------------------------- CDlgDest::~CDlgDest () { SafeLocalFree( m_pbData ); SafeLocalFree( m_lpwhdr ); } // ---------------------------------------------------------------------------------- // Create // ---------------------------------------------------------------------------------- void CDlgDest::Create ( void ) { CDialog::Create(CDlgDest::IDD, m_pwndParent); } // ---------------------------------------------------------------------------------- // OnInitDialog // ---------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------- CONST_GUID_REP cgrFormats[] = { &GUID_NULL, "GUID_NULL", &KSDATAFORMAT_SUBTYPE_PCM, "KSDATAFORMAT_SUBTYPE_PCM" }; UINT rgnSampleRate[] = { 8000, 11025, 16000, 22050, 32000, 44100, 48000, 96000 }; UINT rgnBitDepth[] = { 8, 16, 24, 32 }; BOOL CDlgDest::OnInitDialog () { CDialog::OnInitDialog(); int c,i; i = m_comboFormat.AddString("WAVE_FORMAT_PCM"); m_comboFormat.SetItemData(i, WAVE_FORMAT_PCM); i = m_comboFormat.AddString("WAVE_FORMAT_EXTENSIBLE"); m_comboFormat.SetItemData(i, WAVE_FORMAT_EXTENSIBLE); char sz[10]; for(c = 0; c < ARRAY_ELEMENTS(rgnSampleRate); c++) { StringCchPrintfA(sz, 10, "%d", rgnSampleRate[c]); i = m_comboSampleRate.AddString(sz); m_comboSampleRate.SetItemData(i, rgnSampleRate[c]); } for(c = 0; c < ARRAY_ELEMENTS(rgnBitDepth); c++) { StringCchPrintfA(sz, 10, "%d", rgnBitDepth[c]); i = m_comboBitDepth.AddString(sz); m_comboBitDepth.SetItemData(i, rgnBitDepth[c]); } m_comboSampleRate.SelectString(0, "44100"); m_comboBitDepth.SelectString(0, "16"); m_comboFormat.SelectString(0, "WAVE_FORMAT_PCM"); return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } /* * CDlgDest * operations */ // ---------------------------------------------------------------------------------- // OnRemix // traverses the list of source channels and mixes them according to the user settings // ---------------------------------------------------------------------------------- void CDlgDest::OnRemix () { CDlgSrc * pdlgSrc = 0; ULONG cMaxSamples = 0; UINT nSample; ULONG cbSample; POSITION pos; // move over, bacon SafeLocalFree(m_pbData); // // update UI // Update(); // // determine the max number of samples // ULONG cSamplesSrcThisFormat = 0; for ( pos = g_listSources.GetHeadPosition(); pos; ) { pdlgSrc = g_listSources.GetNext(pos); cSamplesSrcThisFormat = MulDiv( m_wfExt.Format.nSamplesPerSec, pdlgSrc->m_cSamples, pdlgSrc->m_wfx.nSamplesPerSec ); cMaxSamples = max(cMaxSamples, cSamplesSrcThisFormat); } // // determine the length of the buffer required // cbSample = m_wfExt.Format.nChannels * m_wfExt.Format.wBitsPerSample / 8; m_cbData = cbSample * cMaxSamples; m_pbData = (PBYTE)LocalAlloc(LPTR, m_cbData); if( 0 == m_pbData) { MessageBox( "Insufficient memory to complete the task.", "MultiChan : Error", MB_OK | MB_ICONSTOP ); return; } // // copy data from each src to the dst // MMRESULT mmRes; HACMSTREAM has; ULONG cbConversion; PVOID pvConversion; ACMSTREAMHEADER ash; ULONG cSamplesOut; BOOL fRes = FALSE; WAVEFORMATEX wfxConversion; wfxConversion.wFormatTag = WAVE_FORMAT_PCM; wfxConversion.cbSize = 0; wfxConversion.nChannels = 1; wfxConversion.nSamplesPerSec = m_wfExt.Format.nSamplesPerSec; wfxConversion.wBitsPerSample = m_wfExt.Format.wBitsPerSample; wfxConversion.nBlockAlign = wfxConversion.wBitsPerSample / 8; wfxConversion.nAvgBytesPerSec = wfxConversion.nBlockAlign * wfxConversion.nSamplesPerSec; // // for each SRC (src dialog), convert it's buffer into 1 channel of the Dst format // for ( pos = g_listSources.GetHeadPosition(); pos; ) { pdlgSrc = g_listSources.GetNext(pos); // // first, convert dlg format to dest format using ACM // // open an appropriate ACM driver mmRes = acmStreamOpen ( &has, NULL, &pdlgSrc->m_wfx, &wfxConversion, NULL, //wFilter, NULL, 0, ACM_STREAMOPENF_NONREALTIME ); if(!TrapMMError(mmRes, "acmStreamOpen")) break; // // how big a buffer do we need to convert this one channel? // in any case, acm overestimates the size of the buffer needed // if the acm size is used when copying we will exceed the boundaries of our allocated m_pbData // mmRes = acmStreamSize ( has, pdlgSrc->m_cbData, &cbConversion, ACM_STREAMSIZEF_SOURCE ); if(!TrapMMError(mmRes, "acmStreamSize")) break; // alloc pvConversion = LocalAlloc(LPTR, cbConversion); if(!(fRes = (pvConversion != NULL))) { MessageBox("Not enough memory for ACM operation", "MultiChan : Error!", MB_ICONEXCLAMATION | MB_OK); break; } // be prepared! ZeroMemory(&ash, sizeof(ACMSTREAMHEADER)); ash.cbStruct = sizeof(ACMSTREAMHEADER); ash.pbSrc = (PBYTE)pdlgSrc->m_pvData; ash.cbSrcLength = pdlgSrc->m_cbData; ash.pbDst = (PBYTE)pvConversion; ash.cbDstLength = cbConversion; mmRes = acmStreamPrepareHeader(has, &ash, 0); if(!TrapMMError(mmRes, "acmStreamPrepareHeader")) break; // do it mmRes = acmStreamConvert(has, &ash, 0); if(!TrapMMError(mmRes, "acmStreamConvert")) break; // clean up mmRes = acmStreamUnprepareHeader(has, &ash, 0) | acmStreamClose(has, 0); if(!TrapMMError(mmRes, "acmStreamUnprepareHeader")) break; // // now copy the data from conversion buffer to final output buffer // cSamplesOut = ( cbConversion > m_cbData ) ? ( m_cbData / wfxConversion.nBlockAlign ) : ( cbConversion / wfxConversion.nBlockAlign ); switch(wfxConversion.nBlockAlign) { case 1: // 8-bit { // stagger dest PBYTE pbDst = m_pbData + pdlgSrc->m_nChannel; PBYTE pbSrc = (PBYTE)pvConversion; for(nSample = 0; nSample < cSamplesOut; nSample++) { *pbDst = *pbSrc++; pbDst += m_wfExt.Format.nChannels; } break; } case 2: // 16-bit { // stagger dest PUSHORT pusDst = ((PUSHORT)m_pbData) + pdlgSrc->m_nChannel; PUSHORT pusSrc = (PUSHORT)pvConversion; for(nSample = 0; nSample < cSamplesOut; nSample++) { *pusDst = *pusSrc++; pusDst += m_wfExt.Format.nChannels; } break; } case 3: // 24-bit { typedef struct { BYTE b[3]; } S24BITS, *PS24BITS; // stagger dest PS24BITS psDst = ((PS24BITS)m_pbData) + pdlgSrc->m_nChannel; PS24BITS psSrc = (PS24BITS)pvConversion; for(nSample = 0; nSample < cSamplesOut; nSample++) { *psDst = *psSrc++; psDst += m_wfExt.Format.nChannels; } break; } default: ASSERT(0); } // clean up SafeLocalFree( pvConversion ); } if(fRes) { // make playable m_fPlayable = TRUE; m_butPlay.EnableWindow(m_fPlayable); } } // ---------------------------------------------------------------------------------- // OnPlay // ---------------------------------------------------------------------------------- void CDlgDest::OnPlay () { MMRESULT mmRes = MMSYSERR_NOERROR; // // must not be in use // ASSERT( 0 == g_hwo ); if( 0 == m_lpwhdr ) { m_lpwhdr = ( LPWAVEHDR )LocalAlloc( LPTR, sizeof( WAVEHDR ) ); if( 0 == m_lpwhdr ) { MessageBox( "Insufficient memory for rendering !", "MultiChan : Error", MB_ICONEXCLAMATION | MB_OK ); return; } } m_lpwhdr->dwBufferLength = m_cbData; m_lpwhdr->lpData = ( char * )m_pbData; // // open wave device // mmRes = waveOutOpen( &g_hwo, // handle of the output device WAVE_MAPPER, // as appropriate ( WAVEFORMATEX* )&m_wfExt, // describe what's next ( DWORD_PTR )WavePlayFileCB, // the callback ( DWORD_PTR )this, // object issuing the request CALLBACK_FUNCTION // callback via function ); if(TrapMMError(mmRes, "waveOutOpen")) { // // prepare the header // mmRes = waveOutPrepareHeader(g_hwo, m_lpwhdr, sizeof(WAVEHDR)); if(TrapMMError(mmRes, "waveOutPrepareHeader")) { // // revert playable state // WaveTogglePlayback( WAVE_TOGGLE_DESTINATION, WAVE_TOGGLE_DISABLE ); // // start singing // mmRes = waveOutWrite(g_hwo, m_lpwhdr, sizeof(WAVEHDR)); TrapMMError(mmRes, "waveOutWrite"); } // prepare header } // open } // OnPlay // ---------------------------------------------------------------------------------- // OnStop // ---------------------------------------------------------------------------------- void CDlgDest::OnStop () { MMRESULT mmRes = MMSYSERR_NOERROR; // // ensure validity // ASSERT( g_hwo ); ASSERT( !m_fPlayable ); // // stop playback // mmRes = waveOutReset( g_hwo ); TrapMMError( mmRes, "waveOutReset" ); } // OnStop // // RecalcDependant // void CDlgDest::RecalcDependantVariables () { m_wfExt.Format.nBlockAlign = m_wfExt.Format.wBitsPerSample * m_wfExt.Format.nChannels / 8; m_wfExt.Format.nAvgBytesPerSec = m_wfExt.Format.nBlockAlign * m_wfExt.Format.nSamplesPerSec; // update UI char sz[100]; StringCchPrintfA(sz, 100, "%d", m_wfExt.Format.nAvgBytesPerSec); SetDlgItemText(IDC_AVEBITS, sz); } /* * CDlgDest * UI operations */ // ---------------------------------------------------------------------------------- // track combobox changes // ---------------------------------------------------------------------------------- void CDlgDest::OnComboSamplerate () { m_wfExt.Format.nSamplesPerSec = ( DWORD )m_comboSampleRate.GetItemData(m_comboSampleRate.GetCurSel()); RecalcDependantVariables(); } void CDlgDest::OnComboBitdepth () { m_wfExt.Format.wBitsPerSample = (USHORT)m_comboBitDepth.GetItemData(m_comboBitDepth.GetCurSel()); m_wfExt.Samples.wValidBitsPerSample = min(m_wfExt.Samples.wValidBitsPerSample, m_wfExt.Format.wBitsPerSample); m_wValidBitsPerSample = m_wfExt.Samples.wValidBitsPerSample; RecalcDependantVariables(); UpdateData(FALSE); // update m_wValidBitsPerSample display } void CDlgDest::OnComboWaveformat () { m_wfExt.Format.wFormatTag = (WORD)m_comboFormat.GetItemData(m_comboFormat.GetCurSel()); switch(m_wfExt.Format.wFormatTag) { case WAVE_FORMAT_PCM: m_wfExt.Format.cbSize = 0; break; case WAVE_FORMAT_IEEE_FLOAT: m_wfExt.Format.cbSize = 0; break; case WAVE_FORMAT_EXTENSIBLE: m_wfExt.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); break; default: MessageBox("Format type not supported", "MultiChan : Warning", MB_ICONWARNING | MB_OK); break; } } // ---------------------------------------------------------------------------------- // Update // ---------------------------------------------------------------------------------- void CDlgDest::Update () { // update dwChannelMask CDlgSrc* pdlgSrc = 0; BOOL fGotSources = (0 != g_listSources.GetCount() ); m_cRemix.EnableWindow( fGotSources ); m_cValidBits.EnableWindow( fGotSources ); m_cChannels.EnableWindow( fGotSources ); m_cOutput.EnableWindow( fGotSources ); m_comboBitDepth.EnableWindow( fGotSources ); m_comboSampleRate.EnableWindow( fGotSources ); m_cAveBitsPerSec.EnableWindow( fGotSources ); m_cChannelMask.EnableWindow( fGotSources ); m_comboFormat.EnableWindow( fGotSources ); m_butPlay.EnableWindow( fGotSources && m_pbData ); m_butStop.EnableWindow( !fGotSources ); // // setting channel mask - if SPEAKER_NOT_SPECIFIED, don't mind // m_wfExt.dwChannelMask = 0; for ( POSITION pos = g_listSources.GetHeadPosition(); pos; ) { // // iterative step // pdlgSrc = g_listSources.GetNext(pos); if( !(SPEAKER_NOT_SPECIFIED & pdlgSrc->m_dwChannelMask) ) m_wfExt.dwChannelMask |= pdlgSrc->m_dwChannelMask; } // for m_wfExt.dwChannelMask &= 0x7fffffff; // correct any deviations // // set up a proper WAVEFORMATEXTENSIBLE (number of channels = the number of SRC dialogs that were created) // m_wfExt.Format.nChannels = (WORD)g_listSources.GetCount(); if(m_wfExt.Format.nChannels > 2) { m_wfExt.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; m_wfExt.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); m_comboFormat.SelectString(0, "WAVE_FORMAT_EXTENSIBLE"); } RecalcDependantVariables(); m_wValidBitsPerSample = m_wfExt.Samples.wValidBitsPerSample; // update UI char sz[100]; StringCchPrintfA(sz, 100, "%d", m_wfExt.Format.nChannels); SetDlgItemText(IDC_CHANNELS, sz); m_strChannelMask.Format("0x%08x", m_wfExt.dwChannelMask); UpdateData(FALSE); } void CDlgDest::OnEditValidbits () { UpdateData(TRUE); m_wfExt.Samples.wValidBitsPerSample = LOWORD(m_wValidBitsPerSample); } // ---------------------------------------------------------------------------------- // dummy implementations to prevent dlgs from disappearing when user hits enter or esc // ---------------------------------------------------------------------------------- void CDlgDest::OnOK() { // do nothing } void CDlgDest::OnCancel() { // do nothing } HBRUSH CDlgDest::OnCtlColor ( CDC* pDC, CWnd* pWnd, UINT nCtlColor ) { HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); if((nCtlColor == CTLCOLOR_STATIC)) { if(pWnd->m_hWnd == m_cOutput.m_hWnd) { pDC->SetBkColor(GetSysColor(COLOR_ACTIVECAPTION)); pDC->SetTextColor(GetSysColor(COLOR_CAPTIONTEXT)); return GetSysColorBrush(COLOR_ACTIVECAPTION); } } return hbr; } void CDlgDest::OnLButtonDown ( UINT nFlags, CPoint point ) { RECT rcClient; m_cOutput.GetClientRect(&rcClient); m_cOutput.MapWindowPoints(this, &rcClient); m_fDragging = PtInRect(&rcClient, point); if(m_fDragging) { SetCapture(); m_fDragging = TRUE; m_ptPosInCaption = point; } CDialog::OnLButtonDown(nFlags, point); SetFocus(); SetWindowPos(&wndTop, 0,0,0,0, SWP_NOMOVE | SWP_NOSIZE); } void CDlgDest::OnLButtonUp ( UINT nFlags, CPoint point ) { m_fDragging = FALSE; ReleaseCapture(); CDialog::OnLButtonUp(nFlags, point); } void CDlgDest::OnMouseMove ( UINT nFlags, CPoint point ) { RECT rcClientParent; GetParent()->GetClientRect(&rcClientParent); GetParent()->MapWindowPoints(this, &rcClientParent); if(PtInRect(&rcClientParent, point)) { if(m_fDragging) { CRect rcWindow; CRect rcParent; CPoint pt(point.x - m_ptPosInCaption.x, point.y - m_ptPosInCaption.y); GetWindowRect(&rcWindow); GetParent()->GetWindowRect(&rcParent); rcWindow.OffsetRect(pt); rcWindow -= rcParent.TopLeft(); SetWindowPos(NULL, rcWindow.left, rcWindow.top, 0,0, SWP_NOZORDER | SWP_NOSIZE); } } else { ReleaseCapture(); m_fDragging = FALSE; } } /* * CDlgDest * message processing */ LRESULT CDlgDest::WindowProc ( UINT nMessage, WPARAM wParam, LPARAM lParam ) { MMRESULT mmRes = MMSYSERR_NOERROR; switch( nMessage ) { case WM_START_PLAYBACK: { break; } case WM_STOP_PLAYBACK: { // // revert playable state // WaveTogglePlayback( WAVE_TOGGLE_DESTINATION, WAVE_TOGGLE_ALLOW ); // // unprepare and release // mmRes = waveOutUnprepareHeader( g_hwo, m_lpwhdr, sizeof( WAVEHDR ) ); if( TrapMMError( mmRes, "waveOutUnprepareHeader" ) ) { mmRes = waveOutClose( g_hwo ); TrapMMError( mmRes, "waveOutClose" ); g_hwo = 0; } break; } } return( CWnd::WindowProc( nMessage, wParam, lParam ) ); }