268 lines
8.9 KiB
C++
268 lines
8.9 KiB
C++
|
// Copyright (c) 1997 Microsoft Corporation. All Rights Reserved.
|
||
|
#include <windows.h>
|
||
|
#include <mmsystem.h>
|
||
|
#include <amstream.h>
|
||
|
#include <stdio.h>
|
||
|
#include <initguid.h>
|
||
|
#include <atlbase.h>
|
||
|
#include <atlimpl.cpp>
|
||
|
|
||
|
#define CHECK_ERROR(x) \
|
||
|
if (FAILED(hr = (x))) { \
|
||
|
printf(#x " failed with HRESULT(0x%8.8X)\n", hr); \
|
||
|
goto Exit; \
|
||
|
}
|
||
|
|
||
|
HRESULT GetAudioFormat(IMultiMediaStream *pMMStream, WAVEFORMATEX *pwfx)
|
||
|
{
|
||
|
CComPtr<IMediaStream> pStream;
|
||
|
CComPtr<IAudioMediaStream> pAudioStream;
|
||
|
HRESULT hr = pMMStream->GetMediaStream(MSPID_PrimaryAudio, &pStream);
|
||
|
if (FAILED(hr)) {
|
||
|
return hr;
|
||
|
}
|
||
|
hr = pStream->QueryInterface(IID_IAudioMediaStream, (void **)&pAudioStream);
|
||
|
if (FAILED(hr)) {
|
||
|
return hr;
|
||
|
}
|
||
|
return pAudioStream->GetFormat(pwfx);
|
||
|
}
|
||
|
|
||
|
/* Read a section from an MMStream into a buffer */
|
||
|
HRESULT ReadSection(
|
||
|
IMultiMediaStream *pMMStream,
|
||
|
DWORD dwBufferIncrement,
|
||
|
REFERENCE_TIME rtStart,
|
||
|
REFERENCE_TIME rtStop,
|
||
|
REFERENCE_TIME *prtStart,
|
||
|
REFERENCE_TIME *prtStop,
|
||
|
HLOCAL *phData,
|
||
|
DWORD *dwLength
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
*phData = NULL;
|
||
|
CComPtr<IMediaStream> pStream;
|
||
|
CComPtr<IAudioMediaStream> pAudioStream;
|
||
|
CComPtr<IAudioData> pAudioData;
|
||
|
CComPtr<IAudioStreamSample> pSample;
|
||
|
WAVEFORMATEX wfx;
|
||
|
_ASSERTE(rtStart <= rtStop);
|
||
|
DWORD dwSize = 0;
|
||
|
HLOCAL hData = LocalAlloc(LMEM_MOVEABLE, dwBufferIncrement);
|
||
|
bool bFirst = true;
|
||
|
|
||
|
#define SEEKDELTA 1000000
|
||
|
CHECK_ERROR(pMMStream->Seek(rtStart < SEEKDELTA ? 0 : rtStart - SEEKDELTA));
|
||
|
CHECK_ERROR(pMMStream->GetMediaStream(MSPID_PrimaryAudio, &pStream));
|
||
|
CHECK_ERROR(pStream->QueryInterface(IID_IAudioMediaStream, (void **)&pAudioStream));
|
||
|
CHECK_ERROR(pAudioStream->GetFormat(&wfx));
|
||
|
CHECK_ERROR(CoCreateInstance(CLSID_AMAudioData, NULL, CLSCTX_INPROC_SERVER,
|
||
|
IID_IAudioData, (void **)&pAudioData));
|
||
|
CHECK_ERROR(pAudioData->SetFormat(&wfx));
|
||
|
CHECK_ERROR(pAudioStream->CreateSample(pAudioData, 0, &pSample));
|
||
|
|
||
|
for (; ; ) {
|
||
|
_ASSERTE(hData != NULL);
|
||
|
PBYTE pbData = (PBYTE)LocalLock(hData);
|
||
|
pAudioData->SetBuffer(dwBufferIncrement, pbData + dwSize, 0);
|
||
|
HRESULT hr = pSample->Update(0, NULL, NULL, 0);
|
||
|
LocalUnlock(hData);
|
||
|
if (S_OK != hr) {
|
||
|
printf("Update returned 0x%8.8x\n", hr);
|
||
|
break;
|
||
|
}
|
||
|
CHECK_ERROR(pSample->GetSampleTimes(bFirst ? prtStart : NULL, prtStop, NULL));
|
||
|
DWORD dwLength;
|
||
|
pAudioData->GetInfo(NULL, NULL, &dwLength);
|
||
|
dwSize += dwLength;
|
||
|
hData = LocalReAlloc(hData,
|
||
|
LocalSize(hData) + dwBufferIncrement,
|
||
|
LMEM_MOVEABLE);
|
||
|
bFirst = false;
|
||
|
if (*prtStop >= rtStop) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
*dwLength = dwSize;
|
||
|
*phData = hData;
|
||
|
Exit:
|
||
|
if (FAILED(hr)) {
|
||
|
LocalFree(hData);
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
HRESULT RenderFileToMMStream(WCHAR * pszFileName, IMultiMediaStream **ppMMStream)
|
||
|
{
|
||
|
IAMMultiMediaStream *pAMStream;
|
||
|
CoCreateInstance(CLSID_AMMultiMediaStream, NULL, CLSCTX_INPROC_SERVER,
|
||
|
IID_IAMMultiMediaStream, (void **)&pAMStream);
|
||
|
pAMStream->Initialize(STREAMTYPE_READ, 0, NULL);
|
||
|
pAMStream->AddMediaStream(NULL, &MSPID_PrimaryAudio, 0, NULL);
|
||
|
pAMStream->OpenFile(pszFileName, AMMSF_RUN | AMMSF_NOCLOCK);
|
||
|
*ppMMStream = pAMStream;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
typedef struct {
|
||
|
REFERENCE_TIME rtStartIntended;
|
||
|
REFERENCE_TIME rtEndIntended;
|
||
|
REFERENCE_TIME rtStart;
|
||
|
REFERENCE_TIME rtEnd;
|
||
|
DWORD dwByteStart;
|
||
|
DWORD dwByteEnd;
|
||
|
DWORD dwLength;
|
||
|
HGLOBAL hData;
|
||
|
} AUDIO_SECTION;
|
||
|
|
||
|
int _CRTAPI1 main(int argc, char *argv[])
|
||
|
{
|
||
|
if (argc < 4) {
|
||
|
printf("Usage : audtest foo.bar n foo.out\n"
|
||
|
" splits up foo.bar into n sections and writes\n"
|
||
|
" it out in PCM format to foo.out\n");
|
||
|
exit(0);
|
||
|
}
|
||
|
IMultiMediaStream *pMMStream;
|
||
|
CoInitialize(NULL);
|
||
|
WCHAR wszName[1000];
|
||
|
MultiByteToWideChar(CP_ACP, 0, argv[1], -1, wszName,
|
||
|
sizeof(wszName) / sizeof(wszName[0]));
|
||
|
RenderFileToMMStream(wszName, &pMMStream);
|
||
|
int NumSections = atoi(argv[2]);
|
||
|
|
||
|
STREAM_TIME Duration;
|
||
|
pMMStream->GetDuration(&Duration);
|
||
|
|
||
|
WAVEFORMATEX wfx;
|
||
|
GetAudioFormat(pMMStream, &wfx);
|
||
|
|
||
|
DWORD dwDataSize = 0;
|
||
|
|
||
|
/* Read 2 sections */
|
||
|
AUDIO_SECTION *Sections = new AUDIO_SECTION[NumSections];
|
||
|
for (int i = 0; i < NumSections; i++) {
|
||
|
Sections[i].rtStartIntended = Duration * i / NumSections;
|
||
|
Sections[i].rtEndIntended = Duration * (i + 1) / NumSections;
|
||
|
ReadSection(pMMStream,
|
||
|
5000,
|
||
|
Sections[i].rtStartIntended,
|
||
|
Sections[i].rtEndIntended,
|
||
|
&Sections[i].rtStart,
|
||
|
&Sections[i].rtEnd,
|
||
|
&Sections[i].hData,
|
||
|
&Sections[i].dwLength);
|
||
|
Sections[i].dwByteStart = 0;
|
||
|
Sections[i].dwByteEnd = Sections[i].dwLength;
|
||
|
if (Sections[i].rtStart < Sections[i].rtStartIntended) {
|
||
|
DWORD dwOffset = (DWORD)(
|
||
|
(Sections[i].rtStartIntended -
|
||
|
Sections[i].rtStart) * wfx.nSamplesPerSec /
|
||
|
10000000);
|
||
|
dwOffset *= wfx.nBlockAlign;
|
||
|
if (dwOffset > Sections[i].dwLength) {
|
||
|
dwOffset = Sections[i].dwLength;
|
||
|
}
|
||
|
Sections[i].dwByteStart = dwOffset;
|
||
|
}
|
||
|
if (Sections[i].rtEnd > Sections[i].rtEndIntended) {
|
||
|
DWORD dwOffset = (DWORD)(
|
||
|
(Sections[i].rtEnd -
|
||
|
Sections[i].rtEndIntended) * wfx.nSamplesPerSec /
|
||
|
10000000);
|
||
|
dwOffset *= wfx.nBlockAlign;
|
||
|
if (dwOffset > Sections[i].dwLength) {
|
||
|
dwOffset = Sections[i].dwLength;
|
||
|
}
|
||
|
Sections[i].dwByteEnd -= dwOffset;
|
||
|
if (Sections[i].dwByteEnd < Sections[i].dwByteStart) {
|
||
|
Sections[i].dwByteEnd = Sections[i].dwByteStart;
|
||
|
}
|
||
|
}
|
||
|
dwDataSize += Sections[i].dwByteEnd - Sections[i].dwByteStart;
|
||
|
}
|
||
|
//#define DUMP_SECTIONS
|
||
|
#ifdef DUMP_SECTIONS
|
||
|
printf("File is %d milliseconds long\n", (int)(Duration / 10000));
|
||
|
for (i = 0; i < NumSections; i++) {
|
||
|
printf("Section %d Bytes %d Bytes time %d\n\n"
|
||
|
" Start %d Stop %d\n"
|
||
|
" Start Actual %d Stop Actual %d\n\n",
|
||
|
i, Sections[i].dwByteEnd - Sections[i].dwByteStart,
|
||
|
MulDiv(Sections[i].dwByteEnd - Sections[i].dwByteStart,
|
||
|
1000,
|
||
|
wfx.nAvgBytesPerSec),
|
||
|
(int)(Sections[i].rtStartIntended / 10000),
|
||
|
(int)(Sections[i].rtEndIntended / 10000),
|
||
|
(int)(Sections[i].rtStart / 10000),
|
||
|
(int)(Sections[i].rtEnd / 10000));
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/* Now output a wave file */
|
||
|
HANDLE hFileWrite = CreateFile(argv[3],
|
||
|
GENERIC_WRITE,
|
||
|
0,
|
||
|
NULL,
|
||
|
CREATE_ALWAYS,
|
||
|
0,
|
||
|
NULL);
|
||
|
|
||
|
if (INVALID_HANDLE_VALUE == hFileWrite) {
|
||
|
printf("Could not open output file %s\n", argv[3]);
|
||
|
exit(0);
|
||
|
}
|
||
|
|
||
|
DWORD dwBytesWritten;
|
||
|
#pragma pack(1)
|
||
|
typedef struct tagMyHeader {
|
||
|
DWORD dwRIFF;
|
||
|
DWORD cbSize;
|
||
|
DWORD dwWAVE;
|
||
|
DWORD dwfmt;
|
||
|
DWORD cbSizeFormat;
|
||
|
WAVEFORMATEX Format;
|
||
|
DWORD dwDATA;
|
||
|
DWORD cbData;
|
||
|
} MyHeader;
|
||
|
MyHeader Header;
|
||
|
#pragma pack()
|
||
|
Header.dwRIFF = MAKEFOURCC('R','I','F','F');
|
||
|
Header.cbSize = dwDataSize +
|
||
|
(sizeof(Header) - FIELD_OFFSET(MyHeader, dwWAVE));
|
||
|
Header.dwWAVE = MAKEFOURCC('W','A','V','E');
|
||
|
Header.dwfmt = MAKEFOURCC('f','m','t',' ');
|
||
|
Header.cbSizeFormat = sizeof(Header.Format);
|
||
|
Header.Format = wfx;
|
||
|
Header.dwDATA = MAKEFOURCC('d','a','t','a');
|
||
|
Header.cbData = dwDataSize;
|
||
|
WriteFile(hFileWrite, &Header, sizeof(Header),&dwBytesWritten,
|
||
|
NULL);
|
||
|
if (dwBytesWritten != sizeof(Header)) {
|
||
|
printf("couldn't write output file %s\n", argv[3]);
|
||
|
exit(0);
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < NumSections; i++) {
|
||
|
PBYTE pbData = (PBYTE)LocalLock(Sections[i].hData);
|
||
|
WriteFile(hFileWrite,
|
||
|
pbData + Sections[i].dwByteStart,
|
||
|
Sections[i].dwByteEnd - Sections[i].dwByteStart,
|
||
|
&dwBytesWritten,
|
||
|
NULL);
|
||
|
}
|
||
|
|
||
|
CloseHandle(hFileWrite);
|
||
|
|
||
|
pMMStream->Release();
|
||
|
for (i = 0; i < NumSections; i++) {
|
||
|
LocalFree(Sections[i].hData);
|
||
|
}
|
||
|
delete [] Sections;
|
||
|
CoUninitialize();
|
||
|
return 0;
|
||
|
}
|