Пример #1
0
bool UMP3Decoder::Decode(TArray<uint8> &OutBuffer)
{
	check(OutBuffer.Num() == 0);
	check(OutBuffer.GetAllocatedSize() >= WAV_HEADER_SIZE);
	OutBuffer.AddZeroed(WAV_HEADER_SIZE / OutBuffer.GetTypeSize());

	FDateTime tStart = FDateTime::Now();

	unsigned char* BlockBuffer = (unsigned char*)FMemory::Malloc(BlockBufferSize);
	size_t bytesRead = 0;
	size_t done = 0;
	int result;

	do
	{
		result = mpg123_read(Handle, BlockBuffer, BlockBufferSize, &done);
		bytesRead += done;
		OutBuffer.Append(BlockBuffer, done);
	} while (result == MPG123_OK);

	uint8 header[WAV_HEADER_SIZE];
	
	WriteWaveHeader(header, bytesRead, Samplerate, Channels);
	
	FMemory::Memcpy(OutBuffer.GetData(), header, WAV_HEADER_SIZE);
	FMemory::Free(BlockBuffer);

	SizeInBytes = bytesRead;
	bool bSuccess = result == MPG123_OK || result == MPG123_DONE;

	UE_LOG(MP3ImporterLog, Display, TEXT("Decoding finished. %s bytes in %d ms. Success: %s"), 
		*FString::FormatAsNumber((int32)bytesRead), (int32)(FDateTime::Now() - tStart).GetTotalMilliseconds(), bSuccess ? TEXT("True") : TEXT("False"));

	return bSuccess;
}
Пример #2
0
static FLAC__StreamDecoderWriteStatus
WriteCallback(const FLAC__StreamDecoder *decoder,
    const FLAC__Frame *frame, const FLAC__int32 * const buffer[],
    void *clientData)
{
    FlacDecodeArgs *args = (FlacDecodeArgs*)clientData;
    size_t i;

    if(args->totalSamples == 0) {
        return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
    }
    if(args->channels != 2
        || (args->bitsPerSample != 16
         && args->bitsPerSample != 24)) {
        return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
    }

    if(frame->header.number.sample_number == 0) {
        // 最初のデータが来た。WAVEヘッダを出力する。
        if (!WriteWaveHeader(args)) {
            return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
        }
    }

    if (args->bitsPerSample == 16) {
        for(i = 0; i < frame->header.blocksize; i++) {
            if (2 != fwrite(&buffer[0][i], 1, 2, args->fout)) {
                return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
            }
            if (2 != fwrite(&buffer[1][i], 1, 2, args->fout)) {
                return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
            }
        }
    }

    if (args->bitsPerSample == 24) {
        for(i = 0; i < frame->header.blocksize; i++) {
            if (3 != fwrite(&buffer[0][i], 1, 3, args->fout)) {
                return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
            }
            if (3 != fwrite(&buffer[1][i], 1, 3, args->fout)) {
                return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
            }
        }
    }

    return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
Пример #3
0
//write a wave file 
bool StereoBuffer16::WriteWave(const char* fname,unsigned int start, float level)
{
	//check to see if we have anything.
	if(NULL == m_left || NULL == m_right || 0 == mNumFrames)
		return false;
	
	FILE* fptr;
	fptr = WriteWaveHeader(fname, mNumFrames-start);
	
	if(NULL == fptr)
	{   //couldn't come through.
		return false;
	}
	
	//writing 1 short at a time might not be that fast- if this is 
	//an issue, we'll have to create a temp buff at the level instead
	//and do the entire buffer in one fwrite.
	short valuel,valuer;
	long *bufl = (long*)m_left->GetBuffer(0);
	long *bufr = (long*)m_right->GetBuffer(0);
	for(int i = start; i < mNumFrames;i++)
	{
		//have to pretend a short 
		valuel = mymax(mymin(bufl[i],SHORTLIMIT),-SHORTLIMIT) * level;
		valuer = mymax(mymin(bufr[i],SHORTLIMIT),-SHORTLIMIT) * level;
//#ifndef LITTLEENDIAN
#ifndef __LITTLE_ENDIAN__
		valuel = Swap_16(valuel);
		valuer = Swap_16(valuer);
#endif
		fwrite(&valuel,2,1,fptr);
		fwrite(&valuer,2,1,fptr);
	}
	fclose(fptr);
	printf("wrote the file %s\n", fname);
	return true;
	

}
Пример #4
0
static int
lame_decoder(lame_t gfp, FILE * outf, char *inPath, char *outPath)
{
    short int Buffer[2][1152];
    int     i, iread;
    double  wavsize;
    int     tmp_num_channels = lame_get_num_channels(gfp);
    int     skip_start = samples_to_skip_at_start();
    int     skip_end = samples_to_skip_at_end();
    DecoderProgress dp = 0;

    if (!(tmp_num_channels >= 1 && tmp_num_channels <= 2)) {
        error_printf("Internal error.  Aborting.");
        exit(-1);
    }

    if (global_ui_config.silent < 9) {
        console_printf("\rinput:  %s%s(%g kHz, %i channel%s, ",
                       strcmp(inPath, "-") ? inPath : "<stdin>",
                       strlen(inPath) > 26 ? "\n\t" : "  ",
                       lame_get_in_samplerate(gfp) / 1.e3,
                       tmp_num_channels, tmp_num_channels != 1 ? "s" : "");

        printInputFormat(gfp);

        console_printf(")\noutput: %s%s(16 bit, Microsoft WAVE)\n",
                       strcmp(outPath, "-") ? outPath : "<stdout>",
                       strlen(outPath) > 45 ? "\n\t" : "  ");

        if (skip_start > 0)
            console_printf("skipping initial %i samples (encoder+decoder delay)\n", skip_start);
        if (skip_end > 0)
            console_printf("skipping final %i samples (encoder padding-decoder delay)\n", skip_end);

        switch (global_reader.input_format) {
        case sf_mp3:
        case sf_mp2:
        case sf_mp1:
            dp = decoder_progress_init(lame_get_num_samples(gfp),
                                       global_decoder.mp3input_data.framesize);
            break;
        case sf_raw:
        case sf_wave:
        case sf_aiff:
        default:
            dp = decoder_progress_init(lame_get_num_samples(gfp),
                                       lame_get_in_samplerate(gfp) < 32000 ? 576 : 1152);
            break;
        }
    }

    if (0 == global_decoder.disable_wav_header)
        WriteWaveHeader(outf, 0x7FFFFFFF, lame_get_in_samplerate(gfp), tmp_num_channels, 16);
    /* unknown size, so write maximum 32 bit signed value */

    wavsize = 0;
    do {
        iread = get_audio16(gfp, Buffer); /* read in 'iread' samples */
        if (iread >= 0) {
            wavsize += iread;
            if (dp != 0) {
                decoder_progress(dp, &global_decoder.mp3input_data, iread);
            }
            put_audio16(outf, Buffer, iread, tmp_num_channels);
        }
    } while (iread > 0);

    i = (16 / 8) * tmp_num_channels;
    assert(i > 0);
    if (wavsize <= 0) {
        if (global_ui_config.silent < 10)
            error_printf("WAVE file contains 0 PCM samples\n");
        wavsize = 0;
    }
    else if (wavsize > 0xFFFFFFD0 / i) {
        if (global_ui_config.silent < 10)
            error_printf("Very huge WAVE file, can't set filesize accordingly\n");
        wavsize = 0xFFFFFFD0;
    }
    else {
        wavsize *= i;
    }
    /* if outf is seekable, rewind and adjust length */
    if (!global_decoder.disable_wav_header && strcmp("-", outPath)
        && !fseek(outf, 0l, SEEK_SET))
        WriteWaveHeader(outf, (int) wavsize, lame_get_in_samplerate(gfp), tmp_num_channels, 16);
    fclose(outf);
    close_infile();

    if (dp != 0)
        decoder_progress_finish(dp);
    return 0;
}
Пример #5
0
int
lame_decoder(lame_global_flags * gfp, FILE * outf, int skip, char *inPath,
             char *outPath)
{
    short int Buffer[2][1152];
    int     iread;
    double  wavsize;
    int     i;
    void    (*WriteFunction) (FILE * fp, char *p, int n);
    int tmp_num_channels = lame_get_num_channels( gfp );



    if (silent < 10) fprintf(stderr, "\rinput:  %s%s(%g kHz, %i channel%s, ",
            strcmp(inPath, "-") ? inPath : "<stdin>",
            strlen(inPath) > 26 ? "\n\t" : "  ",
            lame_get_in_samplerate( gfp ) / 1.e3,
            tmp_num_channels, tmp_num_channels != 1 ? "s" : "");

    switch (input_format) {
    case sf_mp3:
        if (skip==0) {
            if (enc_delay>-1) skip = enc_delay + 528+1;
            else skip=lame_get_encoder_delay(gfp)+528+1;
        }else{
            /* user specified a value of skip. just add for decoder */
            skip += 528+1; /* mp3 decoder has a 528 sample delay, plus user supplied "skip" */
        }

        if (silent < 10) fprintf(stderr, "MPEG-%u%s Layer %s", 2 - lame_get_version(gfp),
                lame_get_out_samplerate( gfp ) < 16000 ? ".5" : "", "III");
        break;
    case sf_mp2:
        skip += 240 + 1;
        if (silent < 10) fprintf(stderr, "MPEG-%u%s Layer %s", 2 - lame_get_version(gfp),
                lame_get_out_samplerate( gfp ) < 16000 ? ".5" : "", "II");
        break;
    case sf_mp1:
        skip += 240 + 1;
        if (silent < 10) fprintf(stderr, "MPEG-%u%s Layer %s", 2 - lame_get_version(gfp),
                lame_get_out_samplerate( gfp ) < 16000 ? ".5" : "", "I");
        break;
    case sf_raw:
        if (silent < 10) fprintf(stderr, "raw PCM data");
        mp3input_data.nsamp = lame_get_num_samples( gfp );
        mp3input_data.framesize = 1152;
        skip = 0;       /* other formats have no delay *//* is += 0 not better ??? */
        break;
    case sf_wave:
        if (silent < 10) fprintf(stderr, "Microsoft WAVE");
        mp3input_data.nsamp = lame_get_num_samples( gfp );
        mp3input_data.framesize = 1152;
        skip = 0;       /* other formats have no delay *//* is += 0 not better ??? */
        break;
    case sf_aiff:
        if (silent < 10) fprintf(stderr, "SGI/Apple AIFF");
        mp3input_data.nsamp = lame_get_num_samples( gfp );
        mp3input_data.framesize = 1152;
        skip = 0;       /* other formats have no delay *//* is += 0 not better ??? */
        break;
    default:
        if (silent < 10) fprintf(stderr, "unknown");
        mp3input_data.nsamp = lame_get_num_samples( gfp );
        mp3input_data.framesize = 1152;
        skip = 0;       /* other formats have no delay *//* is += 0 not better ??? */
        assert(0);
        break;
    }

    if (silent < 10) {
	fprintf(stderr, ")\noutput: %s%s(16 bit, Microsoft WAVE)\n",
		strcmp(outPath, "-") ? outPath : "<stdout>",
		strlen(outPath) > 45 ? "\n\t" : "  ");

	if (skip > 0)
	    fprintf(stderr,
		    "skipping initial %i samples (encoder+decoder delay)\n",
		    skip);
    }

    if ( 0 == disable_wav_header )
        WriteWaveHeader(outf, 0x7FFFFFFF, lame_get_in_samplerate( gfp ),
                        tmp_num_channels,
                        16);
    /* unknown size, so write maximum 32 bit signed value */

    wavsize = -skip;
    WriteFunction = swapbytes ? WriteBytesSwapped : WriteBytes;
    mp3input_data.totalframes = mp3input_data.nsamp / mp3input_data.framesize;

    assert(tmp_num_channels >= 1 && tmp_num_channels <= 2);

    do {
        iread = get_audio16(gfp, Buffer); /* read in 'iread' samples */
        mp3input_data.framenum += iread / mp3input_data.framesize;
        wavsize += iread;

        if (silent <= 0)
            decoder_progress(gfp, &mp3input_data);

        skip -= (i = skip < iread ? skip : iread); /* 'i' samples are to skip in this frame */

        for (; i < iread; i++) {
            if ( disable_wav_header ) {
                WriteFunction(outf, (char *) &Buffer[0][i], sizeof(short));
                if (tmp_num_channels == 2)
                    WriteFunction(outf, (char *) &Buffer[1][i], sizeof(short));
            }
            else {
                Write16BitsLowHigh(outf, Buffer[0][i]);
                if (tmp_num_channels == 2)
                    Write16BitsLowHigh(outf, Buffer[1][i]);
            }
        }
    } while (iread);

    i = (16 / 8) * tmp_num_channels;
    assert(i > 0);
    if (wavsize <= 0) {
        if (silent < 10) fprintf(stderr, "WAVE file contains 0 PCM samples\n");
        wavsize = 0;
    }
    else if (wavsize > 0xFFFFFFD0 / i) {
        if (silent < 10) fprintf(stderr,
                "Very huge WAVE file, can't set filesize accordingly\n");
        wavsize = 0xFFFFFFD0;
    }
    else {
        wavsize *= i;
    }

    if ( 0 == disable_wav_header )
        if (!fseek(outf, 0l, SEEK_SET)) /* if outf is seekable, rewind and adjust length */
            WriteWaveHeader(outf, (int)wavsize, lame_get_in_samplerate( gfp ),
                            tmp_num_channels, 16);
    fclose(outf);

    if (silent <= 0)
	decoder_progress_finish(gfp);
    return 0;
}
Пример #6
0
void LoopbackCaptureFor(IMMDevice* mmDevice, std::string filename, int secs)
{
    // open new file
    MMIOINFO mi = { 0 };

    // some flags cause mmioOpen write to this buffer
    // but not any that we're using
    std::wstring wsFilename(filename.begin(), filename.end()); // mmioOpen wants a wstring
    HMMIO file = mmioOpen(const_cast<LPWSTR>(wsFilename.c_str()), &mi, MMIO_WRITE | MMIO_CREATE);

    time_t startTime = time(nullptr);

    // activate an IAudioClient
    IAudioClient* audioClient;
    HRESULT hr = mmDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, (void**)&audioClient);
    if (FAILED(hr))
    {
        fprintf(stderr, "IMMDevice::Activate(IAudioClient) failed: hr = 0x%08x", hr);
        return;
    }

    // get the default device periodicity
    REFERENCE_TIME hnsDefaultDevicePeriod;
    hr = audioClient->GetDevicePeriod(&hnsDefaultDevicePeriod, nullptr);
    if (FAILED(hr))
    {
        fprintf(stderr, "IAudioClient::GetDevicePeriod failed: hr = 0x%08x\n", hr);
        audioClient->Release();
        return;
    }

    // get the default device format
    WAVEFORMATEX* waveform;
    hr = audioClient->GetMixFormat(&waveform);
    if (FAILED(hr))
    {
        fprintf(stderr, "IAudioClient::GetMixFormat failed: hr = 0x%08x\n", hr);
        CoTaskMemFree(waveform);
        audioClient->Release();
        return;
    }

    // coerce int-16 wave format
    // can do this in-place since we're not changing the size of the format
    // also, the engine will auto-convert from float to int for us
    switch (waveform->wFormatTag)
    {
        case WAVE_FORMAT_IEEE_FLOAT:
            waveform->wFormatTag = WAVE_FORMAT_PCM;
            waveform->wBitsPerSample = BITS_PER_SAMPLE;
            waveform->nBlockAlign = BLOCK_ALIGN;
            waveform->nAvgBytesPerSec = BYTE_RATE;
            break;
        case WAVE_FORMAT_EXTENSIBLE:
        {
            // naked scope for case-local variable
            PWAVEFORMATEXTENSIBLE pEx = reinterpret_cast<PWAVEFORMATEXTENSIBLE>(waveform);
            if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, pEx->SubFormat))
            {
                pEx->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
                pEx->Samples.wValidBitsPerSample = BITS_PER_SAMPLE;
                waveform->wBitsPerSample = BITS_PER_SAMPLE;
                waveform->nBlockAlign = waveform->nChannels * BYTE_PER_SAMPLE;
                waveform->nAvgBytesPerSec = waveform->nBlockAlign * waveform->nSamplesPerSec;
            }
            break;
        }
    }

    MMCKINFO ckRIFF = { 0 };
    MMCKINFO ckData = { 0 };
    hr = WriteWaveHeader(file, waveform, &ckRIFF, &ckData);

    // create a periodic waitable timer
    HANDLE hWakeUp = CreateWaitableTimer(nullptr, FALSE, nullptr);
    UINT32 nBlockAlign = waveform->nBlockAlign;

    // call IAudioClient::Initialize
    // note that AUDCLNT_STREAMFLAGS_LOOPBACK and AUDCLNT_STREAMFLAGS_EVENTCALLBACK
    // do not work together...
    // the "data ready" event never gets set
    // so we're going to do a timer-driven loop
    hr = audioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK, 0, 0, waveform, 0);
    if (FAILED(hr))
    {
        fprintf(stderr, "IAudioClient::Initialize failed: hr = 0x%08x\n", hr);
        CloseHandle(hWakeUp);
        audioClient->Release();
        return;
    }

    // free up waveform
    CoTaskMemFree(waveform);

    // activate an IAudioCaptureClient
    IAudioCaptureClient* audioCaptureClient;
    hr = audioClient->GetService(__uuidof(IAudioCaptureClient), (void**)&audioCaptureClient);

    // register with MMCSS
    DWORD nTaskIndex = 0;
    HANDLE hTask = AvSetMmThreadCharacteristics(L"Capture", &nTaskIndex);
    if (hTask == nullptr)
    {
        DWORD dwErr = GetLastError();
        fprintf(stderr, "AvSetMmThreadCharacteristics failed: last error = %u\n", dwErr);
        audioCaptureClient->Release();
        CloseHandle(hWakeUp);
        audioClient->Release();
        return;
    }

    // set the waitable timer
    LARGE_INTEGER liFirstFire;
    liFirstFire.QuadPart = -hnsDefaultDevicePeriod / 2; // negative means relative time
    LONG lTimeBetweenFires = (LONG)hnsDefaultDevicePeriod / 2 / (10 * 1000); // convert to milliseconds
    if (!SetWaitableTimer(hWakeUp, &liFirstFire, lTimeBetweenFires, nullptr, nullptr, FALSE))
    {
        DWORD dwErr = GetLastError();
        fprintf(stderr, "SetWaitableTimer failed: last error = %u\n", dwErr);
        AvRevertMmThreadCharacteristics(hTask);
        audioCaptureClient->Release();
        CloseHandle(hWakeUp);
        audioClient->Release();
        return;
    }

    // call IAudioClient::Start
    hr = audioClient->Start();

    // loopback capture loop
    DWORD dwWaitResult;

    UINT32 frames = 0;
    for (UINT32 passes = 0; ; passes++)
    {
        // drain data while it is available
        UINT32 nextPacketSize;
        for (hr = audioCaptureClient->GetNextPacketSize(&nextPacketSize);
            SUCCEEDED(hr) && nextPacketSize > 0;
            hr = audioCaptureClient->GetNextPacketSize(&nextPacketSize))
        {
            // get the captured data
            BYTE* data;
            UINT32 framesToRead;
            DWORD dwFlags;

            hr = audioCaptureClient->GetBuffer(&data, &framesToRead, &dwFlags, nullptr, nullptr);
            if (FAILED(hr))
            {
                fprintf(stderr, "IAudioCaptureClient::GetBuffer failed on pass %u after %u frames: hr = 0x%08x\n", passes, frames, hr);
                audioClient->Stop();
                CancelWaitableTimer(hWakeUp);
                AvRevertMmThreadCharacteristics(hTask);
                audioCaptureClient->Release();
                CloseHandle(hWakeUp);
                audioClient->Release();
                return;
            }

            // this type of error seems to happen often, ignore it
            if (dwFlags == AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY)
                ;
            else if (dwFlags != 0) {
                fprintf(stderr, "IAudioCaptureClient::GetBuffer set flags to 0x%08x on pass %u after %u frames\n", dwFlags, passes, frames);
                audioClient->Stop();
                CancelWaitableTimer(hWakeUp);
                AvRevertMmThreadCharacteristics(hTask);
                audioCaptureClient->Release();
                CloseHandle(hWakeUp);
                audioClient->Release();
                return;
            }

            if (framesToRead == 0)
            {
                fprintf(stderr, "IAudioCaptureClient::GetBuffer said to read 0 frames on pass %u after %u frames\n", passes, frames);
                audioClient->Stop();
                CancelWaitableTimer(hWakeUp);
                AvRevertMmThreadCharacteristics(hTask);
                audioCaptureClient->Release();
                CloseHandle(hWakeUp);
                audioClient->Release();
                return;
            }

            LONG lBytesToWrite = framesToRead * nBlockAlign;
#pragma prefast(suppress: __WARNING_INCORRECT_ANNOTATION, "IAudioCaptureClient::GetBuffer SAL annotation implies a 1-byte buffer")
            LONG lBytesWritten = mmioWrite(file, reinterpret_cast<PCHAR>(data), lBytesToWrite);
            if (lBytesToWrite != lBytesWritten)
            {
                fprintf(stderr, "mmioWrite wrote %u bytes on pass %u after %u frames: expected %u bytes\n", lBytesWritten, passes, frames, lBytesToWrite);
                audioClient->Stop();
                CancelWaitableTimer(hWakeUp);
                AvRevertMmThreadCharacteristics(hTask);
                audioCaptureClient->Release();
                CloseHandle(hWakeUp);
                audioClient->Release();
                return;
            }

            frames += framesToRead;

            hr = audioCaptureClient->ReleaseBuffer(framesToRead);
        }

        dwWaitResult = WaitForSingleObject(hWakeUp, INFINITE);

        if (time(nullptr) - startTime > secs)
            break;
    }

    FinishWaveFile(file, &ckData, &ckRIFF);
    audioClient->Stop();
    CancelWaitableTimer(hWakeUp);
    AvRevertMmThreadCharacteristics(hTask);
    audioCaptureClient->Release();
    CloseHandle(hWakeUp);
    audioClient->Release();


    // everything went well... fixup the fact chunk in the file
    MMRESULT result = mmioClose(file, 0);
    file = nullptr;
    if (result != MMSYSERR_NOERROR)
    {
        fprintf(stderr, "mmioClose failed: MMSYSERR = %u\n", result);
        return;
    }

    // reopen the file in read/write mode
    mi = { 0 };
    file = mmioOpen(const_cast<LPWSTR>(wsFilename.c_str()), &mi, MMIO_READWRITE);
    if (file == nullptr)
    {
        fprintf(stderr, "mmioOpen(\"%ls\", ...) failed. wErrorRet == %u\n", filename, mi.wErrorRet);
        return;
    }

    // descend into the RIFF/WAVE chunk
    ckRIFF = { 0 };
    ckRIFF.ckid = MAKEFOURCC('W', 'A', 'V', 'E'); // this is right for mmioDescend
    result = mmioDescend(file, &ckRIFF, nullptr, MMIO_FINDRIFF);
    if (result != MMSYSERR_NOERROR)
    {
        fprintf(stderr, "mmioDescend(\"WAVE\") failed: MMSYSERR = %u\n", result);
        return;
    }

    // descend into the fact chunk
    MMCKINFO ckFact = { 0 };
    ckFact.ckid = MAKEFOURCC('f', 'a', 'c', 't');
    result = mmioDescend(file, &ckFact, &ckRIFF, MMIO_FINDCHUNK);
    if (result != MMSYSERR_NOERROR) {
        fprintf(stderr, "mmioDescend(\"fact\") failed: MMSYSERR = %u\n", result);
        return;
    }

    // write the correct data to the fact chunk
    LONG lBytesWritten = mmioWrite(file, reinterpret_cast<PCHAR>(&frames), sizeof(frames));
    if (lBytesWritten != sizeof(frames))
    {
        fprintf(stderr, "Updating the fact chunk wrote %u bytes; expected %u\n", lBytesWritten, (UINT32)sizeof(frames));
        return;
    }

    // ascend out of the fact chunk
    result = mmioAscend(file, &ckFact, 0);
    if (result != MMSYSERR_NOERROR)
        fprintf(stderr, "mmioAscend(\"fact\") failed: MMSYSERR = %u\n", result);
}
//HRESULT LoopbackCapture(
//    IMMDevice *pMMDevice,
//    bool bInt16,
//    HANDLE hStartedEvent,
//    HANDLE hStopEvent,
//    PUINT32 pnFrames,
//	HMMIO hFile,
//	AudioBuffer *pBuffer
//)
HRESULT LoopbackCapture::Process()
{
    HRESULT hr;

    // activate an IAudioClient
    IAudioClient *pAudioClient;
    hr = pMMDevice->Activate(
             __uuidof(IAudioClient),
             CLSCTX_ALL, NULL,
             (void**)&pAudioClient
         );
    if (FAILED(hr)) {
        printf("IMMDevice::Activate(IAudioClient) failed: hr = 0x%08x", hr);
        return hr;
    }

    // get the default device periodicity
    REFERENCE_TIME hnsDefaultDevicePeriod;
    hr = pAudioClient->GetDevicePeriod(&hnsDefaultDevicePeriod, NULL);
    if (FAILED(hr)) {
        printf("IAudioClient::GetDevicePeriod failed: hr = 0x%08x\n", hr);
        pAudioClient->Release();
        return hr;
    }

    // get the default device format
    WAVEFORMATEX *pwfx;
    hr = pAudioClient->GetMixFormat(&pwfx);
    if (FAILED(hr)) {
        printf("IAudioClient::GetMixFormat failed: hr = 0x%08x\n", hr);
        CoTaskMemFree(pwfx);
        pAudioClient->Release();
        return hr;
    }

    if (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
    {
        PWAVEFORMATEXTENSIBLE pEx = reinterpret_cast<PWAVEFORMATEXTENSIBLE>(pwfx);
        //pEx->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
        printf("WAVE_FORMAT_EXTENSIBLE\n");
        if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, pEx->SubFormat))
        {
            printf("float\n");
        }//
        else if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_PCM, pEx->SubFormat))
        {
            printf("PCM\n");
        }//KSDATAFORMAT_SUBTYPE_WAVEFORMATEX
        else if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_WAVEFORMATEX, pEx->SubFormat))
        {
            printf("WAVEFORMATEX\n");
        }
    }

    if (bInt16) {
        // coerce int-16 wave format
        // can do this in-place since we're not changing the size of the format
        // also, the engine will auto-convert from float to int for us
        switch (pwfx->wFormatTag) {
        case WAVE_FORMAT_IEEE_FLOAT:
            pwfx->wFormatTag = WAVE_FORMAT_PCM;
            pwfx->wBitsPerSample = 16;
            pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8;
            pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;
            break;

        case WAVE_FORMAT_EXTENSIBLE:
        {
            // naked scope for case-local variable
            PWAVEFORMATEXTENSIBLE pEx = reinterpret_cast<PWAVEFORMATEXTENSIBLE>(pwfx);
            if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, pEx->SubFormat)) {
                pEx->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
                pEx->Samples.wValidBitsPerSample = 16;
                pwfx->wBitsPerSample = 16;
                pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8;
                pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;
            } else {
                printf("Don't know how to coerce mix format to int-16\n");
                CoTaskMemFree(pwfx);
                pAudioClient->Release();
                return E_UNEXPECTED;
            }
        }
        break;

        default:
            printf("Don't know how to coerce WAVEFORMATEX with wFormatTag = 0x%08x to int-16\n", pwfx->wFormatTag);
            CoTaskMemFree(pwfx);
            pAudioClient->Release();
            return E_UNEXPECTED;
        }
    }

    MMCKINFO ckRIFF = {0};
    MMCKINFO ckData = {0};
    if (hFile!=NULL)
        hr = WriteWaveHeader(hFile, pwfx, &ckRIFF, &ckData);
    if (pBuffer)
    {
        bool isFloat = false;
        switch (pwfx->wFormatTag) {
        case WAVE_FORMAT_IEEE_FLOAT:
            isFloat = true;
            break;

        case WAVE_FORMAT_EXTENSIBLE:
        {
            // naked scope for case-local variable
            PWAVEFORMATEXTENSIBLE pEx = reinterpret_cast<PWAVEFORMATEXTENSIBLE>(pwfx);
            if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, pEx->SubFormat)) {
                isFloat = true;
            }
        }
        break;
        default:
            break;
        }
        pBuffer->SetAudioInfo(pwfx->nSamplesPerSec,pwfx->nBlockAlign,pwfx->nChannels,pwfx->wBitsPerSample,isFloat);
    }

    if (FAILED(hr)) {
        // WriteWaveHeader does its own logging
        CoTaskMemFree(pwfx);
        pAudioClient->Release();
        return hr;
    }

    // create a periodic waitable timer
    HANDLE hWakeUp = CreateWaitableTimer(NULL, FALSE, NULL);
    if (NULL == hWakeUp) {
        DWORD dwErr = GetLastError();
        printf("CreateWaitableTimer failed: last error = %u\n", dwErr);
        CoTaskMemFree(pwfx);
        pAudioClient->Release();
        return HRESULT_FROM_WIN32(dwErr);
    }

    UINT32 nBlockAlign = pwfx->nBlockAlign;
    UINT32 nChannels = pwfx->nChannels;
    nFrames = 0;

    // call IAudioClient::Initialize
    // note that AUDCLNT_STREAMFLAGS_LOOPBACK and AUDCLNT_STREAMFLAGS_EVENTCALLBACK
    // do not work together...
    // the "data ready" event never gets set
    // so we're going to do a timer-driven loop
    hr = pAudioClient->Initialize(
             AUDCLNT_SHAREMODE_SHARED,
             AUDCLNT_STREAMFLAGS_LOOPBACK,
             0, 0, pwfx, 0
         );
    if (FAILED(hr)) {
        printf("IAudioClient::Initialize failed: hr = 0x%08x\n", hr);
        CloseHandle(hWakeUp);
        pAudioClient->Release();
        return hr;
    }
    CoTaskMemFree(pwfx);

    // activate an IAudioCaptureClient
    IAudioCaptureClient *pAudioCaptureClient;
    hr = pAudioClient->GetService(
             __uuidof(IAudioCaptureClient),
             (void**)&pAudioCaptureClient
         );
    if (FAILED(hr)) {
        printf("IAudioClient::GetService(IAudioCaptureClient) failed: hr 0x%08x\n", hr);
        CloseHandle(hWakeUp);
        pAudioClient->Release();
        return hr;
    }

    // register with MMCSS
    DWORD nTaskIndex = 0;
    HANDLE hTask = AvSetMmThreadCharacteristics(L"Capture", &nTaskIndex);
    if (NULL == hTask) {
        DWORD dwErr = GetLastError();
        printf("AvSetMmThreadCharacteristics failed: last error = %u\n", dwErr);
        pAudioCaptureClient->Release();
        CloseHandle(hWakeUp);
        pAudioClient->Release();
        return HRESULT_FROM_WIN32(dwErr);
    }

    // set the waitable timer
    LARGE_INTEGER liFirstFire;
    liFirstFire.QuadPart = -hnsDefaultDevicePeriod / 2; // negative means relative time
    LONG lTimeBetweenFires = (LONG)hnsDefaultDevicePeriod / 2 / (10 * 1000); // convert to milliseconds
    BOOL bOK = SetWaitableTimer(
                   hWakeUp,
                   &liFirstFire,
                   lTimeBetweenFires,
                   NULL, NULL, FALSE
               );
    if (!bOK) {
        DWORD dwErr = GetLastError();
        printf("SetWaitableTimer failed: last error = %u\n", dwErr);
        AvRevertMmThreadCharacteristics(hTask);
        pAudioCaptureClient->Release();
        CloseHandle(hWakeUp);
        pAudioClient->Release();
        return HRESULT_FROM_WIN32(dwErr);
    }

    // call IAudioClient::Start
    hr = pAudioClient->Start();
    if (FAILED(hr)) {
        printf("IAudioClient::Start failed: hr = 0x%08x\n", hr);
        AvRevertMmThreadCharacteristics(hTask);
        pAudioCaptureClient->Release();
        CloseHandle(hWakeUp);
        pAudioClient->Release();
        return hr;
    }
    SetEvent(hStartedEvent);

    // loopback capture loop
    HANDLE waitArray[2] = { hStopEvent, hWakeUp };
    DWORD dwWaitResult;
    DWORD immdState;

    bool bDone = false;
    bool bFirstPacket = true;
    for (UINT32 nPasses = 0; !bDone; nPasses++) {
        dwWaitResult = WaitForMultipleObjects(
                           ARRAYSIZE(waitArray), waitArray,
                           FALSE, INFINITE
                       );

        if (WAIT_OBJECT_0 == dwWaitResult) {
            //printf("Received stop event after %u passes and %u frames\n", nPasses, nFrames);
            bDone = true;
            continue; // exits loop
        }

        if (WAIT_OBJECT_0 + 1 != dwWaitResult) {
            printf("Unexpected WaitForMultipleObjects return value %u on pass %u after %u frames\n", dwWaitResult, nPasses, nFrames);
            pAudioClient->Stop();
            CancelWaitableTimer(hWakeUp);
            AvRevertMmThreadCharacteristics(hTask);
            pAudioCaptureClient->Release();
            CloseHandle(hWakeUp);
            pAudioClient->Release();
            return E_UNEXPECTED;
        }

        printf("'");

        // got a "wake up" event - see if there's data
        UINT32 nNextPacketSize;
        hr = pAudioCaptureClient->GetNextPacketSize(&nNextPacketSize);
        if (FAILED(hr)) {
            if (hr == AUDCLNT_E_SERVICE_NOT_RUNNING)
                printf("AUDCLNT_E_SERVICE_NOT_RUNNING : \n");
            else if (hr == AUDCLNT_E_DEVICE_INVALIDATED)
                printf("AUDCLNT_E_DEVICE_INVALIDATED : \n");
            else
                printf("UNKNOWN ERROR!!! : \n");

            printf("IAudioCaptureClient::GetNextPacketSize failed on pass %u after %u frames: hr = 0x%08x\n", nPasses, nFrames, hr);
            pAudioClient->Stop();
            CancelWaitableTimer(hWakeUp);
            AvRevertMmThreadCharacteristics(hTask);
            pAudioCaptureClient->Release();
            CloseHandle(hWakeUp);
            pAudioClient->Release();
            return hr;
        }

        if (0 == nNextPacketSize) {
            // no data yet
            continue;
        }

        // get the captured data
        BYTE *pData;
        UINT32 nNumFramesToRead;
        DWORD dwFlags;

        hr = pAudioCaptureClient->GetBuffer(
                 &pData,
                 &nNumFramesToRead,
                 &dwFlags,
                 NULL,
                 NULL
             );
        if (FAILED(hr)) {
            printf("IAudioCaptureClient::GetBuffer failed on pass %u after %u frames: hr = 0x%08x\n", nPasses, nFrames, hr);
            pAudioClient->Stop();
            CancelWaitableTimer(hWakeUp);
            AvRevertMmThreadCharacteristics(hTask);
            pAudioCaptureClient->Release();
            CloseHandle(hWakeUp);
            pAudioClient->Release();
            return hr;
        }

        if (bFirstPacket && AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY == dwFlags) {
            printf("Probably spurious glitch reported on first packet\n");
        }
        else if (dwFlags & AUDCLNT_BUFFERFLAGS_SILENT)
        {
            printf("#");
        }
        else if (dwFlags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY)
        {
            printf("!");
        }
        else if (0 != dwFlags) {
            printf("IAudioCaptureClient::GetBuffer set flags to 0x%08x on pass %u after %u frames\n", dwFlags, nPasses, nFrames);
            pAudioClient->Stop();
            CancelWaitableTimer(hWakeUp);
            AvRevertMmThreadCharacteristics(hTask);
            pAudioCaptureClient->Release();
            CloseHandle(hWakeUp);
            pAudioClient->Release();
            return E_UNEXPECTED;
        }

        if (0 == nNumFramesToRead) {
            // no data yet
            continue;
        }

        //if (0 == nNumFramesToRead) {
        //    printf("IAudioCaptureClient::GetBuffer said to read 0 frames on pass %u after %u frames\n", nPasses, nFrames);
        //    pAudioClient->Stop();
        //    CancelWaitableTimer(hWakeUp);
        //    AvRevertMmThreadCharacteristics(hTask);
        //    pAudioCaptureClient->Release();
        //    CloseHandle(hWakeUp);
        //    pAudioClient->Release();
        //    return E_UNEXPECTED;
        //}

        LONG lBytesToWrite = nNumFramesToRead * nBlockAlign;
#pragma prefast(suppress: __WARNING_INCORRECT_ANNOTATION, "IAudioCaptureClient::GetBuffer SAL annotation implies a 1-byte buffer")

        if (hFile!=NULL)
        {
            LONG lBytesWritten = mmioWrite(hFile, reinterpret_cast<PCHAR>(pData), lBytesToWrite);
            if (lBytesToWrite != lBytesWritten) {
                printf("mmioWrite wrote %u bytes on pass %u after %u frames: expected %u bytes\n", lBytesWritten, nPasses, nFrames, lBytesToWrite);
                pAudioClient->Stop();
                CancelWaitableTimer(hWakeUp);
                AvRevertMmThreadCharacteristics(hTask);
                pAudioCaptureClient->Release();
                CloseHandle(hWakeUp);
                pAudioClient->Release();
                return E_UNEXPECTED;
            }
        }
        if (pBuffer)
        {
            //switch (nBlockAlign/nChannels)
            //{
            //case 1:
            //	ShowPCM((unsigned char*)pData,nNumFramesToRead,nChannels,1024,60,"SYS_Byte");
            //	break;
            //case 2:
            //	ShowPCM((short*)pData,nNumFramesToRead,nChannels,1024,60,"SYS_Short");
            //	break;
            //case 4:
            //	ShowPCM((int*)pData,nNumFramesToRead,nChannels,1024,60,"SYS_Int");
            //	//ShowPCM((float*)pData,nNumFramesToRead,nChannels,1024,60,"SYS_float");
            //	break;
            //}


            pBuffer->PushBuffer(pData,lBytesToWrite);
        }
        nFrames += nNumFramesToRead;

        hr = pAudioCaptureClient->ReleaseBuffer(nNumFramesToRead);
        if (FAILED(hr)) {
            printf("IAudioCaptureClient::ReleaseBuffer failed on pass %u after %u frames: hr = 0x%08x\n", nPasses, nFrames, hr);
            pAudioClient->Stop();
            CancelWaitableTimer(hWakeUp);
            AvRevertMmThreadCharacteristics(hTask);
            pAudioCaptureClient->Release();
            CloseHandle(hWakeUp);
            pAudioClient->Release();
            return hr;
        }

        bFirstPacket = false;
    } // capture loop

    if (hFile!=NULL)
        hr = FinishWaveFile(hFile, &ckData, &ckRIFF);


    if (FAILED(hr)) {
        // FinishWaveFile does it's own logging
        pAudioClient->Stop();
        CancelWaitableTimer(hWakeUp);
        AvRevertMmThreadCharacteristics(hTask);
        pAudioCaptureClient->Release();
        CloseHandle(hWakeUp);
        pAudioClient->Release();
        return hr;
    }

    pAudioClient->Stop();
    CancelWaitableTimer(hWakeUp);
    AvRevertMmThreadCharacteristics(hTask);
    pAudioCaptureClient->Release();
    CloseHandle(hWakeUp);
    pAudioClient->Release();

    return hr;
}