2025-11-28 00:35:46 +09:00

802 lines
21 KiB
C++

// 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 <initguid.h>
#include <mmreg.h>
#include <msacm.h>
#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 ) );
}