static HRESULT CreateAudioMediaType(const WWMFPcmFormat &fmt, IMFMediaType** ppMediaType) { HRESULT hr; IMFMediaType *pMediaType = NULL; *ppMediaType = NULL; HRG(MFCreateMediaType(&pMediaType) ); HRG(pMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio)); HRG(pMediaType->SetGUID(MF_MT_SUBTYPE, (fmt.sampleFormat == WWMFBitFormatInt) ? MFAudioFormat_PCM : MFAudioFormat_Float)); HRG(pMediaType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, fmt.nChannels)); HRG(pMediaType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, fmt.sampleRate)); HRG(pMediaType->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, fmt.FrameBytes())); HRG(pMediaType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, fmt.BytesPerSec())); HRG(pMediaType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, fmt.bits)); HRG(pMediaType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE)); if (0 != fmt.dwChannelMask) { HRG(pMediaType->SetUINT32(MF_MT_AUDIO_CHANNEL_MASK, fmt.dwChannelMask)); } if (fmt.bits != fmt.validBitsPerSample) { HRG(pMediaType->SetUINT32(MF_MT_AUDIO_VALID_BITS_PER_SAMPLE, fmt.validBitsPerSample)); } *ppMediaType = pMediaType; pMediaType = NULL; //< prevent release end: SafeRelease(&pMediaType); return hr; }
static HRESULT WriteWavHeader(FILE *fpw, WWMFPcmFormat &format, DWORD dataBytes) { HRESULT hr = E_FAIL; int dataChunkSize = ((dataBytes+1)&(~1)) + 4; HRG(WriteBytes(fpw, "RIFF", 4U)); HRG(WriteInt32(fpw, dataChunkSize + 0x24)); HRG(WriteBytes(fpw, "WAVE", 4U)); HRG(WriteBytes(fpw, "fmt ", 4U)); HRG(WriteInt32(fpw, 16)); // fmt audioFormat size==2 1==int 3==float switch (format.sampleFormat) { case WWMFBitFormatInt: HRG(WriteInt16(fpw, 1)); break; case WWMFBitFormatFloat: HRG(WriteInt16(fpw, 3)); break; default: goto end; } // fmt numChannels size==2 HRG(WriteInt16(fpw, format.nChannels)); // fmt sampleRate size==4 HRG(WriteInt32(fpw, format.sampleRate)); // fmt byteRate size==4 HRG(WriteInt32(fpw, format.BytesPerSec())); // fmt blockAlign size==2 HRG(WriteInt16(fpw, format.FrameBytes())); // fmt bitspersample size==2 HRG(WriteInt16(fpw, format.bits)); HRG(WriteBytes(fpw, "data", 4U)); HRG(WriteInt32(fpw, dataChunkSize)); end: return hr; }
int wmain(int argc, wchar_t *argv[]) { // _CrtSetBreakAlloc(35); // COM leak cannot be detected by debug heap manager ... _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); HRESULT hr = S_OK; bool bCoInitialize = false; FILE *fpr = NULL; FILE *fpw = NULL; errno_t ercd; BYTE *buff = NULL; DWORD buffBytes = 0; DWORD readBytes = 0; DWORD remainBytes = 0; DWORD expectedOutputDataBytes = 0; DWORD result = 0; DWORD writeDataTotalBytes = 0; int conversionQuality = 60; WWMFResampler resampler; WWMFPcmFormat inputFormat; WWMFPcmFormat outputFormat; WWMFSampleData sampleData; if (argc != 6) { PrintUsage(argv[0]); return 1; } HRG(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)); bCoInitialize = true; ercd = _wfopen_s(&fpr, argv[1], L"rb"); if (0 != ercd) { printf("file open error %S\n", argv[1]); PrintUsage(argv[0]); hr = E_FAIL; goto end; } ercd = _wfopen_s(&fpw, argv[2], L"wb"); if (0 != ercd) { printf("file open error %S\n", argv[2]); PrintUsage(argv[0]); hr = E_FAIL; goto end; } HRG(ReadWavHeader(fpr, &inputFormat, &remainBytes)); outputFormat = inputFormat; outputFormat.sampleRate = _wtoi(argv[3]); outputFormat.bits = (short)_wtoi(argv[4]); conversionQuality = _wtoi(argv[5]); if (0 == outputFormat.sampleRate || 0 == conversionQuality) { PrintUsage(argv[0]); hr = E_FAIL; goto end; } outputFormat.validBitsPerSample = outputFormat.bits; switch (outputFormat.bits) { case 16: case 24: outputFormat.sampleFormat = WWMFBitFormatInt; break; case 32: outputFormat.sampleFormat = WWMFBitFormatFloat; break; default: PrintUsage(argv[0]); hr = E_FAIL; goto end; } expectedOutputDataBytes = (int64_t)remainBytes * outputFormat.BytesPerSec() / inputFormat .BytesPerSec(); HRG(WriteWavHeader(fpw, outputFormat, expectedOutputDataBytes)); HRG(resampler.Initialize(inputFormat, outputFormat, conversionQuality)); buffBytes = 128 * 1024 * inputFormat.FrameBytes(); buff = new BYTE[buffBytes]; for (;;) { // read PCM data from file readBytes = buffBytes; if (remainBytes < readBytes) { readBytes = remainBytes; } remainBytes -= readBytes; result = fread(buff, 1, readBytes, fpr); if (result != readBytes) { printf("file read error\n"); hr = E_FAIL; goto end; } // convert HRG(resampler.Resample(buff, readBytes, &sampleData)); // write to file result = fwrite(sampleData.data, 1, sampleData.bytes, fpw); if (result != sampleData.bytes) { printf("file write error\n"); hr = E_FAIL; goto end; } writeDataTotalBytes += sampleData.bytes; sampleData.Release(); if (remainBytes == 0) { // end HRG(resampler.Drain(buffBytes, &sampleData)); // write remaining PCM data to file result = fwrite(sampleData.data, 1, sampleData.bytes, fpw); if (result != sampleData.bytes) { printf("file write error\n"); hr = E_FAIL; goto end; } writeDataTotalBytes += sampleData.bytes; sampleData.Release(); break; } } // data chunk align is 2 bytes if (writeDataTotalBytes & 1) { if (0 != fputc(0, fpw)) { printf("file write error\n"); hr = E_FAIL; goto end; } ++writeDataTotalBytes; } HRG(FixWavHeader(fpw, writeDataTotalBytes)); hr = S_OK; end: resampler.Finalize(); if (bCoInitialize) { CoUninitialize(); bCoInitialize = false; } delete[] buff; buff = NULL; if (fpw != NULL) { fclose(fpw); fpw = NULL; } if (fpr != NULL) { fclose(fpr); fpr = NULL; } return SUCCEEDED(hr) ? 0 : 1; }
TBool AudioDriver::CheckMixFormat(TUint aSampleRate, TUint aNumChannels, TUint aBitDepth) { HRESULT hr; WAVEFORMATEX *closestMix; WAVEFORMATEX savedMixFormat; TBool retVal = false; // Complete any previous resampling session. if (iResamplingInput) { WWMFSampleData sampleData; hr = iResampler.Drain((iBufferSize * iFrameSize), &sampleData); if (hr == S_OK) { Log::Print("Resampler drained correctly [%d bytes].\n", sampleData.bytes); sampleData.Release(); } else { Log::Print("Resample drain failed.\n"); } iResampler.Finalize(); } iResamplingInput = false; // Verify the Audio Engine supports the stream format. if (iMixFormat == NULL) { hr = iAudioClient->GetMixFormat(&iMixFormat); if (hr != S_OK) { Log::Print("ERROR: Could not obtain mix system format.\n"); return false; } } savedMixFormat = *iMixFormat; iMixFormat->wFormatTag = WAVE_FORMAT_PCM; iMixFormat->nChannels = (WORD)aNumChannels; iMixFormat->nSamplesPerSec = aSampleRate; iMixFormat->nBlockAlign = WORD((aNumChannels * aBitDepth)/8); iMixFormat->nAvgBytesPerSec = DWORD(aSampleRate * iMixFormat->nBlockAlign); iMixFormat->wBitsPerSample = (WORD)aBitDepth; iMixFormat->cbSize = 0; hr = iAudioClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, iMixFormat, &closestMix); if (hr != S_OK) { // The stream format isn't suitable as it stands. // // Use a media foundation translation to convert to the current // mix format. // // Load the active mix format. // CoTaskMemFree(iMixFormat); hr = iAudioClient->GetMixFormat(&iMixFormat); if (hr == S_OK) { iMixFormat->wFormatTag = WAVE_FORMAT_PCM; iMixFormat->cbSize = 0; // Confirm the mix format s valid. CoTaskMemFree(closestMix); hr = iAudioClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, iMixFormat, &closestMix); if (hr != S_OK) { Log::Print("ERROR: Cannot obtain valid mix format for stream " "translation\n"); retVal = false; goto end; } // Setup the translation. // Input stream format. WWMFPcmFormat inputFormat; inputFormat.sampleFormat = WWMFBitFormatInt; inputFormat.nChannels = (WORD)aNumChannels; inputFormat.sampleRate = aSampleRate; inputFormat.bits = (WORD)aBitDepth; inputFormat.validBitsPerSample = (WORD)aBitDepth; // System mix format. WWMFPcmFormat outputFormat; outputFormat.sampleFormat = WWMFBitFormatInt; outputFormat.nChannels = iMixFormat->nChannels; outputFormat.sampleRate = iMixFormat->nSamplesPerSec; outputFormat.bits = iMixFormat->wBitsPerSample; outputFormat.validBitsPerSample = iMixFormat->wBitsPerSample; // Store bytes per second values for later calculations around // the amount of data generated by the translation. iResampleInputBps = inputFormat.BytesPerSec(); iResampleOutputBps = outputFormat.BytesPerSec(); if (iResampler.Initialize(inputFormat, outputFormat, 60) == S_OK) { iResamplingInput = true; retVal = true; } else { Log::Print("ERROR: Stream Transaltion Failed.\n"); Log::Print("Transalte From:\n\n"); Log::Print("\tSample Rate: %6u\n", aSampleRate); Log::Print("\tNumber Of Channels: %6u\n", aNumChannels); Log::Print("\tBit Depth: %6u\n", aBitDepth); Log::Print("Translate To:\n\n"); Log::Print("\tSample Rate: %6u\n", iMixFormat->nSamplesPerSec); Log::Print("\tNumber Of Channels: %6u\n", iMixFormat->nChannels); Log::Print("\tBit Depth: %6u\n", iMixFormat->wBitsPerSample); } } } else { retVal = true; } end: CoTaskMemFree(closestMix); return retVal; }