void DisplayFile(std::string Filename, const int Position, const int Size) { int ItemIndex = SendMessage(lsFiles, LB_GETCOUNT, 0, 0) -1; if (ItemIndex >= 0) { std::wstring wsFilename(Filename.begin(), Filename.end()); // Loop through the list in reverse until a match is found while (ItemIndex >= 0) { int Length = (int)SendMessage(lsFiles, LB_GETTEXTLEN, (WPARAM)ItemIndex, 0); if (Length > 0) { // Get text of item std::vector<WCHAR> Buffer(Length + 1); SendMessage(lsFiles, LB_GETTEXT, (WPARAM)ItemIndex, (LPARAM)&Buffer[0]); std::wstring ItemText(&Buffer[0]); // If matched filename, then update file completion if (ItemText.substr(0, wsFilename.size()) == wsFilename) { std::wstringstream Text; Text << wsFilename << L" - (" << Position << " / " << Size << L") [" << (int)(Position * 100 / Size) << "% Complete]"; SendMessage(lsFiles, LB_DELETESTRING, (WPARAM)ItemIndex, 0); SendMessage(lsFiles, LB_INSERTSTRING, (WPARAM)ItemIndex, (LPARAM)Text.str().c_str()); break; } } --ItemIndex; } } };
void ParsePacket(Packet &Data) { // Only process if receiving PKT_USERINFO or the user is already logged in if ((Data.Header() == PKT_USERINFO) || (m_Username.length())) switch (Data.Header()) { case PKT_USERINFO: { AddLog(L"Received PKT_USERINFO from socket " + m_wsSocket + L"."); char Result = 0; std::wstring Username = Data.RemoveNTString(); std::wstring Password = Data.RemoveNTString(); for (int i = 0; i < USERS_COUNT; ++i) // Check if Username and Password matches if (MatchString(Username, Users[i].Username) && MatchString(Password, Users[i].Password, true)) { // If user already logged in then fail new login attempt if (Users[i].LoggedIn) break; Users[i].LoggedIn = true; m_Username = Users[i].Username; Result = 1; break; } Packet UserInfo(PKT_USERINFO); UserInfo.InsertByte(Result); UserInfo.SendPacket(m_Socket); AddLog(L"Sent PKT_USERINFO to " + m_wsSocket + L"."); int Index = FindIndex(m_Socket); if (Index >= 0) { SendMessage(lsClients, LB_DELETESTRING, (WPARAM)Index, 0); SendMessage(lsClients, LB_INSERTSTRING, (WPARAM)Index, (LPARAM)std::wstring(m_wsSocket + L" - " + m_Username).c_str()); } } break; case PKT_FILE: { // Read filename std::wstring wsFilename(Data.RemoveNTString()); std::string sFilename(wsFilename.begin(), wsFilename.end()); // Check if Filename is in Files list already File *CurrentFile = NULL; std::list<File *>::const_iterator FindFile = Files.begin(); while (FindFile != Files.end()) { std::string CheckFile = ((File *)*FindFile)->Filename(); if (CheckFile == sFilename) { CurrentFile = *FindFile; break; } ++FindFile; } // If file not found then create new file and add it to Files list if (!CurrentFile) { CurrentFile = new File(sFilename, (int)Data.RemoveDWORD()); Files.push_back(CurrentFile); } // Write to file CurrentFile->Write(Data.RawData()); // if completed writing then close file and delete file object if (CurrentFile->Complete()) { // Remove file from Files list and delete File object Files.erase(FindFile); delete CurrentFile; } } break; } };
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); }