source: trunk/Utilities/GeneratePcmWavFile/GeneratePcmWavFile.cpp @ 5

Last change on this file since 5 was 5, checked in by roman, 13 years ago
  • Property svn:keywords set to Id
File size: 16.1 KB
RevLine 
[5]1////////////////////////////////////////////////////////////
2// Copyright (C) Roman Ryltsov, 2008-2011
3// Created by Roman Ryltsov roman@alax.info
4//
5// $Id: GeneratePcmWavFile.cpp 5 2011-08-25 07:19:48Z roman $
6
7#include "stdafx.h"
8#include "rodshow.h"
9
10////////////////////////////////////////////////////////////
11// CModule
12
13class CModule : 
14        public CAtlExeModuleT<CModule>
15{
16        typedef CThreadT<CModule> CThread;
17
18public:
19
20        ////////////////////////////////////////////////////////
21        // CGenericFilterGraph
22
23        class CGenericFilterGraph
24        {
25        public:
26#if _DEVELOPMENT
27                BOOL m_bShowDestructorMessageBox;
28#endif // _DEVELOPMENT
29                CComPtr<IFilterGraph2> m_pFilterGraph;
30                CComPtr<IMediaControl> m_pMediaControl;
31                CComPtr<IMediaEventEx> m_pMediaEventEx;
32                CComPtr<IMediaFilter> m_pMediaFilter;
33                CComPtr<IMediaPosition> m_pMediaPosition;
34
35        public:
36        // CGenericFilterGraph
37                CGenericFilterGraph(BOOL bShowDestructorMessageBox = FALSE) throw()
38                {
39                        bShowDestructorMessageBox;
40#if _DEVELOPMENT
41                        m_bShowDestructorMessageBox = bShowDestructorMessageBox;
42#endif // _DEVELOPMENT
43                }
44                ~CGenericFilterGraph() throw()
45                {
46#if _DEVELOPMENT
47                        if(m_pFilterGraph && m_bShowDestructorMessageBox)
48                                AtlMessageBox(GetActiveWindow(), _T("DirectShow Filter Graph is about to be Released - Break In"), _T("Debug"), MB_ICONWARNING | MB_OK);
49#endif // _DEVELOPMENT
50                }
51                VOID CoCreateInstance(const CLSID& ClassIdentifier = CLSID_FilterGraph)
52                {
53                        __C(m_pFilterGraph.CoCreateInstance(ClassIdentifier));
54                        m_pMediaControl = CComQIPtr<IMediaControl>(m_pFilterGraph);
55                        m_pMediaEventEx = CComQIPtr<IMediaEventEx>(m_pFilterGraph);
56                        m_pMediaFilter = CComQIPtr<IMediaFilter>(m_pFilterGraph);
57                        m_pMediaPosition = CComQIPtr<IMediaPosition>(m_pFilterGraph);
58                        __D(m_pMediaControl && m_pMediaEventEx && m_pMediaFilter && m_pMediaPosition, E_NOINTERFACE);
59                }
60                VOID SetShowDestructorMessageBox(BOOL bShowDestructorMessageBox = TRUE)
61                {
62                        bShowDestructorMessageBox;
63#if _DEVELOPMENT
64                        m_bShowDestructorMessageBox = bShowDestructorMessageBox;
65#endif // _DEVELOPMENT
66                }
67                operator const CComPtr<IFilterGraph2>& () const throw()
68                {
69                        return m_pFilterGraph;
70                }
71                const CComPtr<IFilterGraph2>& operator -> () const throw()
72                {
73                        return m_pFilterGraph;
74                }
75        };
76
77        ////////////////////////////////////////////////////////
78        // CSourceFilter
79
80        class ATL_NO_VTABLE CSourceFilter :
81                public CComObjectRootEx<CComMultiThreadModelNoCS>,
82                public CComCoClass<CSourceFilter>,
83                public CPushSourceFilterT<CSourceFilter>,
84                public CBasePersistT<CSourceFilter>,
85                public CAmFilterMiscFlagsT<CSourceFilter, AM_FILTER_MISC_FLAGS_IS_SOURCE>
86        {
87        public:
88
89        DECLARE_NO_REGISTRY()
90
91        DECLARE_PROTECT_FINAL_CONSTRUCT()
92
93        //DECLARE_QI_TRACE(CSourceFilter)
94
95        BEGIN_COM_MAP(CSourceFilter)
96                COM_INTERFACE_ENTRY(IBaseFilter)
97                COM_INTERFACE_ENTRY(IMediaFilter)
98                COM_INTERFACE_ENTRY_IID(__uuidof(IPersist), IBaseFilter)
99                COM_INTERFACE_ENTRY(IAMFilterMiscFlags)
100        END_COM_MAP()
101
102        public:
103
104                ////////////////////////////////////////////////////////
105                // CThreadContext
106
107                class COutputPin;
108
109                class CThreadContext :
110                        public CPushSourceFilterT<CSourceFilter>::CThreadContext
111                {
112                public:
113                        CEvent m_MediaSampleTimeEvent;
114                        REFERENCE_TIME m_nMediaSampleTime;
115
116                public:
117                // CThreadContext
118                        CThreadContext(CEvent& TerminationEvent) throw() :
119                                CPushSourceFilter::CThreadContext(TerminationEvent),
120                                m_nMediaSampleTime(0)
121                        {
122                        }
123                };
124
125                ////////////////////////////////////////////////////////
126                // COutputPin
127
128                class ATL_NO_VTABLE COutputPin :
129                        public CComObjectRootEx<CComMultiThreadModelNoCS>,
130                        public CPushSourceFilterT<CSourceFilter>::COutputPinT<COutputPin, CSourceFilter, CThreadContext>,
131                        public CAmPushSourceT<COutputPin, 0>
132                {
133                public:
134
135                //DECLARE_QI_TRACE(CSourceFilter::COutputPin)
136
137                BEGIN_COM_MAP(COutputPin)
138                        COM_INTERFACE_ENTRY(IPin)
139                        COM_INTERFACE_ENTRY(IAMPushSource)
140                        COM_INTERFACE_ENTRY(IAMLatency)
141                END_COM_MAP()
142
143                public:
144                        CMediaType m_pDataMediaType;
145                        REFERENCE_TIME m_nDataLength;
146
147                public:
148                // COutputPin
149                        COutputPin() throw() :
150                                m_nDataLength(0)
151                        {
152                                _Z4(atlTraceRefcount, 4, _T("this 0x%p\n"), this);
153                        }
154                        ~COutputPin() throw()
155                        {
156                                _Z4(atlTraceRefcount, 4, _T("this 0x%p\n"), this);
157                        }
158                        VOID EnumerateMediaTypes(CAtlList<CMediaType>& MediaTypeList)
159                        {
160                                CRoCriticalSectionLock DataLock(GetDataCriticalSection());
161                                _W(MediaTypeList.AddTail(m_pDataMediaType));
162                        }
163                        BOOL CheckMediaType(const CMediaType& pMediaType) const throw()
164                        {
165                                _A(pMediaType);
166                                //if(pMediaType->majortype != MEDIATYPE_Audio || pMediaType->subtype != MEDIASUBTYPE_PCM)
167                                //      return FALSE;
168                                //const CWaveFormatEx* pWaveFormatEx = pMediaType.GetWaveFormatEx();
169                                //if(!pWaveFormatEx)
170                                //      return FALSE;
171                                return m_pDataMediaType.Compare(pMediaType);
172                        }
173                        BOOL DecideMemAllocatorProperties(IMemAllocator* pMemAllocator, ALLOCATOR_PROPERTIES Properties) throw()
174                        {
175                                CRoCriticalSectionLock DataLock(GetDataCriticalSection());
176                                const CMediaType& pMediaType = GetMediaTypeReference();
177                                const CWaveFormatEx* pWaveFormatEx = pMediaType.GetWaveFormatEx();
178                                if(!pWaveFormatEx)
179                                        return FALSE;
180                                if(!__super::DecideMemAllocatorProperties(pMemAllocator, Properties))
181                                        return FALSE;
182                                static const ULONG g_nDefaultBufferLength = 500; // 500 ms
183                                const SIZE_T nDefaultBufferBlockCount = (pWaveFormatEx->nAvgBytesPerSec * g_nDefaultBufferLength / 1000 + pWaveFormatEx->nBlockAlign - 1) / pWaveFormatEx->nBlockAlign;
184                                const SIZE_T nDefaultBufferSize = nDefaultBufferBlockCount * pWaveFormatEx->nBlockAlign;
185                                if(Properties.cbBuffer < (LONG) nDefaultBufferSize)
186                                        Properties.cbBuffer = (LONG) nDefaultBufferSize;
187                                if(Properties.cbAlign < pWaveFormatEx->nBlockAlign)
188                                        Properties.cbAlign = pWaveFormatEx->nBlockAlign;
189                                if(!SetMemAllocatorBufferSize(pMemAllocator, Properties))
190                                        return FALSE;
191                                const SIZE_T nSampleCount = Properties.cbBuffer / pWaveFormatEx->nBlockAlign;
192                                const SIZE_T nEffectiveBufferSize = nSampleCount * pWaveFormatEx->nBlockAlign;
193                                const REFERENCE_TIME nBufferTime = (REFERENCE_TIME) (1000 * 10000) * nEffectiveBufferSize / pWaveFormatEx->nAvgBytesPerSec;
194                                SetLatency(nBufferTime);
195                                return TRUE;
196                        }
197                        BOOL ComposeMediaSample(CThreadContext& ThreadContext, IMediaSample* pMediaSample)
198                        {
199                                CMediaSampleProperties Properties(pMediaSample);
200                                _A(!Properties.pMediaType);
201                                if(ThreadContext.m_nMediaSampleTime >= m_nDataLength)
202                                        return FALSE; // Finished
203                                CRoCriticalSectionLock DataLock(GetDataCriticalSection());
204                                const CWaveFormatEx* pWaveFormatEx = GetMediaTypeReference().GetWaveFormatEx();
205                                _A(pWaveFormatEx);
206                                const SIZE_T nRemainedDataSize = (SIZE_T) ((m_nDataLength - ThreadContext.m_nMediaSampleTime) * pWaveFormatEx->nAvgBytesPerSec / (1000 * 10000i64));
207                                SIZE_T nDataSize = Properties.cbBuffer;
208                                if(nDataSize > nRemainedDataSize)
209                                        nDataSize = nRemainedDataSize;
210                                nDataSize = (nDataSize / pWaveFormatEx->nBlockAlign) * pWaveFormatEx->nBlockAlign;
211                                if(pWaveFormatEx->wBitsPerSample == 8)
212                                        FillMemory(Properties.pbBuffer, nDataSize, 0x80);
213                                else
214                                        ZeroMemory(Properties.pbBuffer, nDataSize);
215                                ThreadContext.m_nMediaSampleTime += nDataSize * (1000 * 10000i64) / pWaveFormatEx->nAvgBytesPerSec;
216                                Properties.lActual = (LONG) nDataSize;
217                                Properties.Set();
218                                return TRUE;
219                        }
220                        VOID InitializeData(const CWaveFormatEx* pWaveFormatEx, REFERENCE_TIME nLength)
221                        {
222                                _A(pWaveFormatEx);
223                                _A(nLength > 0);
224                                __D(!m_pDataMediaType, E_UNNAMED);
225                                m_pDataMediaType.AllocateWaveFormatEx(pWaveFormatEx);
226                                m_nDataLength = nLength;
227                        }
228                };
229
230        private:
231                CObjectPtr<COutputPin> m_pOutputPin;
232
233        public:
234        // CSourceFilter
235                CSourceFilter() throw() :
236                        CBasePersistT<CSourceFilter>(GetDataCriticalSection())
237                {
238                        _Z4(atlTraceRefcount, 4, _T("this 0x%p\n"), this);
239                }
240                ~CSourceFilter() throw()
241                {
242                        _Z4(atlTraceRefcount, 4, _T("this 0x%p\n"), this);
243                }
244                HRESULT FinalConstruct() throw()
245                {
246                        _ATLTRY
247                        {
248                                m_pOutputPin.Construct()->Initialize(this, L"Output");
249                                AddPin(m_pOutputPin);
250                        }
251                        _ATLCATCH(Exception)
252                        {
253                                _C(Exception);
254                        }
255                        return S_OK;
256                }
257                VOID FinalRelease() throw()
258                {
259                        m_pOutputPin = NULL;
260                }
261                VOID DeliverBeginFlush(IPin*)
262                {
263                        m_pOutputPin->DeliverBeginFlush();
264                }
265                VOID DeliverEndFlush(IPin*)
266                {
267                        m_pOutputPin->DeliverEndFlush();
268                }
269                VOID DeliverNewSegment(IPin*, REFERENCE_TIME nStartTime, REFERENCE_TIME nStopTime, DOUBLE fRate)
270                {
271                        m_pOutputPin->DeliverNewSegment(nStartTime, nStopTime, fRate);
272                }
273                static BOOL CanCue() throw()
274                {
275                        return FALSE;
276                }
277                VOID CueFilter()
278                {
279                        m_pOutputPin->CuePin();
280                }
281                VOID RunFilter(REFERENCE_TIME nStartTime)
282                {
283                        m_pOutputPin->RunPin(nStartTime);
284                }
285                VOID PauseFilter() throw()
286                {
287                        m_pOutputPin->PausePin();
288                }
289                VOID StopFilter() throw()
290                {
291                        m_pOutputPin->StopPin();
292                }
293                const CObjectPtr<COutputPin>& GetOutputPin() const throw()
294                {
295                        return m_pOutputPin;
296                }
297                VOID Initialize(const CWaveFormatEx* pWaveFormatEx, REFERENCE_TIME nLength)
298                {
299                        m_pOutputPin->InitializeData(pWaveFormatEx, nLength);
300                }
301        };
302
303private:
304        CWaveFormatEx m_WaveFormatEx;
305        REFERENCE_TIME m_nLength;
306        CPath m_sPath;
307
308public:
309// CModule
310        CModule() throw()
311        {
312#if defined(_DEBUG)
313                AtlTraceLoadSettings(NULL);
314#endif // defined(_DEBUG)
315                _Z4(atlTraceRefcount, 4, _T("this 0x%p\n"), this);
316                ZeroMemory(&m_WaveFormatEx, sizeof m_WaveFormatEx);
317                m_nLength = 0;
318        }
319        ~CModule() throw()
320        {
321                _Z4(atlTraceRefcount, 4, _T("this 0x%p\n"), this);
322        }
323        VOID SetSampleRate(UINT nSampleRate)
324        {
325                __D(nSampleRate > 0, E_INVALIDARG);
326                m_WaveFormatEx.nSamplesPerSec = (DWORD) nSampleRate;
327        }
328        VOID SetChannelCount(UINT nChannelCount)
329        {
330                __D(nChannelCount > 0, E_INVALIDARG);
331                m_WaveFormatEx.nChannels = (WORD) nChannelCount;
332        }
333        VOID SetSampleBitCount(UINT nSampleBitCount)
334        {
335                __D(nSampleBitCount == 8 || nSampleBitCount == 16, E_INVALIDARG);
336                m_WaveFormatEx.wBitsPerSample = (WORD) nSampleBitCount;
337        }
338        VOID SetLength(REFERENCE_TIME nLength)
339        {
340                __D(nLength > 0, E_INVALIDARG);
341                m_nLength = nLength;
342        }
343        VOID SetPath(LPCTSTR pszPath)
344        {
345                __D(_tcslen(pszPath), E_INVALIDARG);
346                __D(!_tcslen(m_sPath), E_INVALIDARG);
347                m_sPath = pszPath;
348        }
349        HRESULT PreMessageLoop(INT nShowCommand)
350        {
351                __C(__super::PreMessageLoop(nShowCommand));
352                return S_OK;
353        }
354        VOID RunMessageLoop()
355        {
356                #pragma region Input Validation and Syntax
357                if(!m_WaveFormatEx.nSamplesPerSec || !m_WaveFormatEx.nChannels || !m_WaveFormatEx.wBitsPerSample || !m_nLength || !_tcslen(m_sPath))
358                {
359                        _tprintf(_T("Syntax: GeneratePcmWavFile <options> <output-path>\n"));
360                        _tprintf(_T("  /s:N: Sampling Rate N, Hz\n"));
361                        _tprintf(_T("  /c:N: Channel Count N\n"));
362                        _tprintf(_T("  /b:N: Sample Bit Count N, 8 or 16\n"));
363                        _tprintf(_T("  /t:N: Length N, seconds\n"));
364                        __C(S_FALSE);
365                }
366                #pragma endregion
367                #pragma region Complete Audio Format
368                m_WaveFormatEx.wFormatTag = WAVE_FORMAT_PCM;
369                m_WaveFormatEx.nBlockAlign = m_WaveFormatEx.nChannels * ((m_WaveFormatEx.wBitsPerSample + 7) >> 3);
370                m_WaveFormatEx.nAvgBytesPerSec = m_WaveFormatEx.nSamplesPerSec * m_WaveFormatEx.nBlockAlign;
371                CWaveFormatEx* pWaveFormatEx = &m_WaveFormatEx;
372                // SUGG: Replace with WAVEFORMATEXTENSIBLE where appropriate
373                #pragma endregion
374                #pragma region Filter Graph
375                CGenericFilterGraph GenericFilterGraph(TRUE);
376                GenericFilterGraph.CoCreateInstance();
377                CObjectPtr<CSourceFilter> pSourceFilter;
378                pSourceFilter.Construct()->Initialize(pWaveFormatEx, m_nLength);
379                __C(GenericFilterGraph->AddFilter(pSourceFilter, CT2CW(_T("Source"))));
380                CComPtr<IPin> pCurrentOutputPin = pSourceFilter->GetOutputPin();
381                #pragma region WAV Dest
382                CComPtr<IBaseFilter> pWavDestBaseFilter;
383                _ATLTRY
384                {
385                        __C(pWavDestBaseFilter.CoCreateInstance(_PersistHelper::ClassIdentifierFromString(CT2CW(_T("{3C78B8E2-6C4D-11D1-ADE2-0000F8754B99}")))));
386                        __C(GenericFilterGraph->AddFilter(pWavDestBaseFilter, CT2CW(_T(".WAV Multiplexer"))));
387                        __C(GenericFilterGraph->Connect(pCurrentOutputPin, _FilterGraphHelper::GetFilterPin(pWavDestBaseFilter, PINDIR_INPUT)));
388                        pCurrentOutputPin = _FilterGraphHelper::GetFilterPin(pWavDestBaseFilter, PINDIR_OUTPUT);
389                }
390                _ATLCATCH(Exception)
391                {
392                        _tprintf(_T("There was an error trying to instantiate WAV Dest filter, Exception 0x%08X\n"), (HRESULT) Exception);
393                        _ATLRETHROW;
394                }
395                #pragma endregion
396                #pragma region File Writer
397                CComPtr<IBaseFilter> pFileWriterBaseFilter;
398                __C(pFileWriterBaseFilter.CoCreateInstance(CLSID_FileWriter));
399                CComQIPtr<IFileSinkFilter2> pFileSinkFilter2 = pFileWriterBaseFilter;
400                __D(pFileSinkFilter2, E_NOINTERFACE);
401                _tprintf(_T("Writing into \"%s\"...\n"), m_sPath);
402                __C(pFileSinkFilter2->SetFileName(CT2CW(m_sPath), NULL));
403                __C(pFileSinkFilter2->SetMode(AM_FILE_OVERWRITE));
404                __C(GenericFilterGraph->AddFilter(pFileWriterBaseFilter, CT2CW(_T("File Writer"))));
405                __C(GenericFilterGraph->Connect(pCurrentOutputPin, _FilterGraphHelper::GetFilterPin(pFileWriterBaseFilter, PINDIR_INPUT)));
406                pCurrentOutputPin.Release();
407                #pragma endregion
408                _A(!pCurrentOutputPin);
409                _A(GenericFilterGraph.m_pMediaFilter);
410                __C(GenericFilterGraph.m_pMediaFilter->SetSyncSource(NULL));
411                #pragma endregion
412                _tprintf(_T("Running...\n"));
413                _A(GenericFilterGraph.m_pMediaControl);
414                __C(GenericFilterGraph.m_pMediaControl->Run());
415                GenericFilterGraph.SetShowDestructorMessageBox(FALSE);
416                _A(GenericFilterGraph.m_pMediaEventEx);
417                LONG nEventCode;
418                __C(GenericFilterGraph.m_pMediaEventEx->WaitForCompletion(INFINITE, &nEventCode));
419                _tprintf(_T("Complete, nEventCode 0x%02X\n"), nEventCode);
420                __C(GenericFilterGraph.m_pMediaControl->Stop());
421        }
422};
423
424////////////////////////////////////////////////////////////
425// Main
426
427int _tmain(int argc, _TCHAR* argv[])
428{
429        _ATLTRY
430        {
431                CModule Module;
432                #pragma region Parse Command Line
433                for(INT nIndex = 1; nIndex < argc; nIndex++)
434                {
435                        CString sArgument = argv[nIndex];
436                        _A(!sArgument.IsEmpty());
437                        #pragma region Switches
438                        if(_tcschr(_T("-/"), sArgument[0]))
439                        {
440                                sArgument.Delete(0);
441                                #pragma region Switch Value/Specification
442                                CString sArgumentValue;
443                                if(sArgument.GetLength() > 1)
444                                {
445                                        SIZE_T nIndex = 1;
446                                        if(sArgument[1] == _T(':'))
447                                                nIndex++;
448                                        sArgumentValue = (LPCTSTR) sArgument + nIndex;
449                                }
450                                INT nIntegerArgumentValue = 0;
451                                const BOOL bIntegerArgumentValueValid = !sArgumentValue.IsEmpty() ? AtlStringToInteger(sArgumentValue, nIntegerArgumentValue) : FALSE;
452                                #pragma endregion
453                                if(_tcschr(_T("Ss"), sArgument[0])) // Sample Rate
454                                {
455                                        __D(bIntegerArgumentValueValid, E_INVALIDARG);
456                                        //_tprintf(_T("Option: Sample Rate, %d\n"), nIntegerArgumentValue);
457                                        Module.SetSampleRate(nIntegerArgumentValue);
458                                } else
459                                if(_tcschr(_T("Cc"), sArgument[0])) // Channel Count
460                                {
461                                        __D(bIntegerArgumentValueValid, E_INVALIDARG);
462                                        //_tprintf(_T("Option: Channel Count, %d\n"), nIntegerArgumentValue);
463                                        Module.SetChannelCount(nIntegerArgumentValue);
464                                } else
465                                if(_tcschr(_T("Bb"), sArgument[0])) // Sample Bit Count
466                                {
467                                        __D(bIntegerArgumentValueValid, E_INVALIDARG);
468                                        //_tprintf(_T("Option: Sample Bit Count, %d\n"), nIntegerArgumentValue);
469                                        Module.SetSampleBitCount(nIntegerArgumentValue);
470                                } else
471                                if(_tcschr(_T("Tt"), sArgument[0])) // Length, seconds
472                                {
473                                        __D(bIntegerArgumentValueValid, E_INVALIDARG);
474                                        //_tprintf(_T("Option: Length, %d\n"), nIntegerArgumentValue);
475                                        Module.SetLength(nIntegerArgumentValue * 1000 * 10000i64);
476                                }
477                                continue;
478                        }
479                        #pragma endregion
480                        if(sArgument.GetLength() >= 2 && sArgument[0] == _T('"') && sArgument[sArgument.GetLength() - 1] == _T('"'))
481                                sArgument = sArgument.Mid(1, sArgument.GetLength() - 2);
482                        Module.SetPath(sArgument);
483                }
484                #pragma endregion
485                Module.WinMain(SW_SHOWNORMAL);
486        }
487        _ATLCATCH(Exception)
488        {
489                if(FAILED((HRESULT) Exception))
490                        _tprintf(_T("Error 0x%08x: %s\n"), (HRESULT) Exception, AtlFormatSystemMessage(Exception).TrimRight(_T("\t\n\r .")));
491                return (INT) (HRESULT) Exception;
492        }
493        _ATLCATCHALL()
494        {
495                _tprintf(_T("Fatal Error\n"));
496                return (INT) E_FAIL;
497        }
498        return 0;
499}
Note: See TracBrowser for help on using the repository browser.