diff --git a/Core/Config.h b/Core/Config.h index 03e3b34584..497e7eae20 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -142,6 +142,7 @@ public: std::string sVulkanDevice; std::string sD3D11Device; // Windows only std::string sCameraDevice; + std::string sMicDevice; bool bSoftwareRendering; bool bHardwareTransform; // only used in the GLES backend diff --git a/Core/HLE/sceAudio.cpp b/Core/HLE/sceAudio.cpp index f3c355b655..f75b66604c 100644 --- a/Core/HLE/sceAudio.cpp +++ b/Core/HLE/sceAudio.cpp @@ -23,6 +23,7 @@ #include "Core/HLE/FunctionWrappers.h" #include "Core/HLE/sceKernelThread.h" #include "Core/HLE/sceAudio.h" +#include "Core/HLE/sceUsbMic.h" #include "Core/HLE/__sceAudio.h" #include "Core/Reporting.h" @@ -471,6 +472,11 @@ static u32 sceAudioSRCOutputBlocking(u32 vol, u32 buf) { return hleLogSuccessI(SCEAUDIO, result); } +static int sceAudioInputBlocking(u32 maxSamples, u32 sampleRate, u32 bufAddr) { + ERROR_LOG(HLE, "UNIMPL sceAudioInputBlocking: maxSamples: %d, samplerate: %d, bufAddr: %08x", maxSamples, sampleRate, bufAddr); + return __MicInputBlocking(maxSamples, sampleRate, bufAddr); +} + static u32 sceAudioRoutingSetMode(u32 mode) { ERROR_LOG_REPORT(SCEAUDIO, "sceAudioRoutingSetMode(%08x)", mode); int previousMode = defaultRoutingMode; @@ -535,7 +541,7 @@ const HLEFunction sceAudio[] = {0X7DE61688, nullptr, "sceAudioInputInit", '?', "" }, {0XE926D3FB, nullptr, "sceAudioInputInitEx", '?', "" }, {0X6D4BEC68, nullptr, "sceAudioInput", '?', "" }, - {0X086E5895, nullptr, "sceAudioInputBlocking", '?', "" }, + {0X086E5895, &WrapI_UUU, "sceAudioInputBlocking", 'i', "xxx" }, {0XA708C6A6, nullptr, "sceAudioGetInputLength", '?', "" }, {0XA633048E, nullptr, "sceAudioPollInputEnd", '?', "" }, {0X87B2E651, nullptr, "sceAudioWaitInputEnd", '?', "" }, diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index ca92dc8557..f03eb5f8c3 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -77,12 +77,13 @@ #include "sceImpose.h" #include "sceUsb.h" #include "sceUsbGps.h" +#include "sceUsbCam.h" +#include "sceUsbMic.h" #include "scePspNpDrm_user.h" #include "sceVaudio.h" #include "sceHeap.h" #include "sceDmac.h" #include "sceMp4.h" -#include "sceUsbCam.h" #include "../Util/PPGeDraw.h" @@ -146,6 +147,7 @@ void __KernelInit() __VideoPmpInit(); __UsbGpsInit(); __UsbCamInit(); + __UsbMicInit(); SaveState::Init(); // Must be after IO, as it may create a directory Reporting::Init(); @@ -170,6 +172,7 @@ void __KernelShutdown() kernelObjects.Clear(); __UsbCamShutdown(); + __UsbMicShutdown(); __UsbGpsShutdown(); __AudioCodecShutdown(); @@ -278,6 +281,7 @@ void __KernelDoState(PointerWrap &p) __VideoPmpDoState(p); __AACDoState(p); __UsbGpsDoState(p); + __UsbMicDoState(p); // IMPORTANT! Add new sections last! } diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 83c8fb6635..29c256b3db 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -79,6 +79,7 @@ const WaitTypeNames waitTypeNames[] = { { WAITTYPE_TLSPL, "TLS" }, { WAITTYPE_VMEM, "Volatile Mem" }, { WAITTYPE_ASYNCIO, "AsyncIO" }, + { WAITTYPE_MICINPUT, "Microphone input"}, }; const char *getWaitTypeName(WaitType type) diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index 4c3041f65a..5c85ba661b 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -106,6 +106,7 @@ enum WaitType : int WAITTYPE_TLSPL = 21, WAITTYPE_VMEM = 22, WAITTYPE_ASYNCIO = 23, + WAITTYPE_MICINPUT = 24, // fake NUM_WAITTYPES }; diff --git a/Core/HLE/sceUsb.cpp b/Core/HLE/sceUsb.cpp index cd2e5e5e16..c29ef6e6b2 100644 --- a/Core/HLE/sceUsb.cpp +++ b/Core/HLE/sceUsb.cpp @@ -83,7 +83,7 @@ static int sceUsbGetState() { | (usbConnected ? USB_STATUS_CONNECTED : USB_STATUS_DISCONNECTED) | (usbActivated ? USB_STATUS_ACTIVATED : USB_STATUS_DEACTIVATED); } - INFO_LOG(HLE, "sceUsbGetState: 0x%x", state); + DEBUG_LOG(HLE, "sceUsbGetState: 0x%x", state); return state; } diff --git a/Core/HLE/sceUsbCam.cpp b/Core/HLE/sceUsbCam.cpp index c2d0ed1609..a5c4653335 100644 --- a/Core/HLE/sceUsbCam.cpp +++ b/Core/HLE/sceUsbCam.cpp @@ -23,6 +23,7 @@ #include "Common/ChunkFile.h" #include "Core/HLE/HLE.h" #include "Core/HLE/sceUsbCam.h" +#include "Core/HLE/sceUsbMic.h" #include "Core/HW/Camera.h" #include "Core/MemMapHelpers.h" @@ -112,7 +113,7 @@ static int getCameraResolution(Camera::ConfigType type, int *width, int *height) static int sceUsbCamSetupMic(u32 paramAddr, u32 workareaAddr, int wasize) { - INFO_LOG(HLE, "UNIMPL sceUsbCamSetupMic"); + INFO_LOG(HLE, "sceUsbCamSetupMic"); if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupMicParam))) { Memory::ReadStruct(paramAddr, &config->micParam); } @@ -131,13 +132,7 @@ static int sceUsbCamStopMic() { static int sceUsbCamReadMicBlocking(u32 bufAddr, u32 size) { INFO_LOG(HLE, "UNIMPL sceUsbCamReadMicBlocking: size: %d", size); - for (unsigned int i = 0; i < size; i++) { - if (Memory::IsValidAddress(bufAddr + i)) { - Memory::Write_U8(i & 0xFF, bufAddr + i); - } - } - hleEatMicro(1000000 / config->micParam.frequency * (size / 2)); - return size; + return __MicInputBlocking(size >> 1, config->micParam.frequency, bufAddr); } static int sceUsbCamSetupVideo(u32 paramAddr, u32 workareaAddr, int wasize) { diff --git a/Core/HLE/sceUsbMic.cpp b/Core/HLE/sceUsbMic.cpp index 306f527da1..13a041a71e 100644 --- a/Core/HLE/sceUsbMic.cpp +++ b/Core/HLE/sceUsbMic.cpp @@ -21,29 +21,408 @@ #include "Common/ChunkFile.h" #include "Core/HLE/HLE.h" #include "Core/HLE/FunctionWrappers.h" +#include "Core/HLE/sceKernelThread.h" #include "Core/HLE/sceUsbMic.h" +#include "Core/CoreTiming.h" #include "Core/MemMapHelpers.h" -static int sceUsbMicInputBlocking(u32 size, u32 samplerate, u32 bufAddr) { - INFO_LOG(HLE, "UNIMPL sceUsbMicInputBlocking: size: %d, samplerate: %d", size, samplerate); - for (unsigned int i = 0; i < size; i++) { - if (Memory::IsValidAddress(bufAddr + i)) { - Memory::Write_U8(i & 0xFF, bufAddr + i); +#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) && !defined(__LIBRETRO__) +#define HAVE_WIN32_MICROPHONE +#endif + +#ifdef HAVE_WIN32_MICROPHONE +#include "Windows/CaptureDevice.h" +#endif + +enum { + SCE_USBMIC_ERROR_INVALID_MAX_SAMPLES = 0x80243806, + SCE_USBMIC_ERROR_INVALID_SAMPLERATE = 0x8024380A, +}; + +int eventUsbMicAudioUpdate = -1; + +QueueBuf *audioBuf = nullptr; +u32 numNeedSamples; +static std::vector waitingThreads; +std::mutex wtMutex; +bool isNeedInput; +u32 curSampleRate; +u32 curChannels; +int micState; // 0 means stopped, 1 means started, for save state. + +static void __UsbMicAudioUpdate(u64 userdata, int cyclesLate) { + SceUID threadID = (SceUID)userdata; + u32 error; + int count = 0; + std::unique_lock lock(wtMutex); + for (auto waitingThread : waitingThreads) { + if (waitingThread.threadID == threadID) { + SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_MICINPUT, error); + if (waitID == 0) + continue; + if (Microphone::isHaveDevice()) { + if (Microphone::availableAudioBufSize() >= waitingThread.needSize) { + u8 *tempbuf8 = new u8[waitingThread.needSize]; + Microphone::getAudioData(tempbuf8, waitingThread.needSize); + Memory::Memcpy(waitingThread.addr, tempbuf8, waitingThread.needSize); + delete[] tempbuf8; + u32 ret = __KernelGetWaitValue(threadID, error); + DEBUG_LOG(HLE, "sceUsbMic: Waking up thread(%d)", (int)waitingThread.threadID); + __KernelResumeThreadFromWait(threadID, ret); + waitingThreads.erase(waitingThreads.begin() + count); + if (waitingThreads.size() == 0) + isNeedInput = false; + } else { + u64 waitTimeus = (waitingThread.needSize - Microphone::availableAudioBufSize()) * 1000000 / 2 / waitingThread.sampleRate; + CoreTiming::ScheduleEvent(usToCycles(waitTimeus), eventUsbMicAudioUpdate, userdata); + } + } else { + for (u32 i = 0; i < waitingThread.needSize; i++) { + if (Memory::IsValidAddress(waitingThread.addr + i)) { + Memory::Write_U8(i & 0xFF, waitingThread.addr + i); + } + } + u32 ret = __KernelGetWaitValue(threadID, error); + DEBUG_LOG(HLE, "sceUsbMic: Waking up thread(%d)", (int)waitingThread.threadID); + __KernelResumeThreadFromWait(threadID, ret); + waitingThreads.erase(waitingThreads.begin() + count); + if (waitingThreads.size() == 0) + isNeedInput = false; + } + } + ++count; + } + lock.unlock(); +} + +void __UsbMicInit() { + if (audioBuf) { + delete audioBuf; + audioBuf = nullptr; + } + numNeedSamples = 0; + waitingThreads.clear(); + isNeedInput = false; + curSampleRate = 44100; + curChannels = 1; + micState = 0; + eventUsbMicAudioUpdate = CoreTiming::RegisterEvent("UsbMicAudioUpdate", &__UsbMicAudioUpdate); +} + +void __UsbMicShutdown() { + if (audioBuf) { + delete audioBuf; + audioBuf = nullptr; + } + Microphone::stopMic(); +} + +void __UsbMicDoState(PointerWrap &p) { + auto s = p.Section("sceUsbMic", 1, 1); + if (!s) { + return; + } + p.Do(numNeedSamples); + p.Do(waitingThreads); + p.Do(isNeedInput); + p.Do(curSampleRate); + p.Do(curChannels); + p.Do(micState); + // Maybe also need to save the state of audioBuf. + if (waitingThreads.size() != 0 && p.mode == PointerWrap::MODE_READ) { + u64 waitTimeus = (waitingThreads[0].needSize - Microphone::availableAudioBufSize()) * 1000000 / 2 / waitingThreads[0].sampleRate; + CoreTiming::ScheduleEvent(usToCycles(waitTimeus), eventUsbMicAudioUpdate, waitingThreads[0].threadID); + } + if (micState == 0) { + if (Microphone::isMicStarted()) + Microphone::stopMic(); + } else if (micState == 1) { + if (Microphone::isMicStarted()) { + Microphone::stopMic(); + Microphone::startMic(new std::vector({ curSampleRate, curChannels })); + } else { + Microphone::startMic(new std::vector({ curSampleRate, curChannels })); } } - hleEatMicro(1000000 / samplerate * (size / 2)); +} + +QueueBuf::QueueBuf(u32 size) : start(0), end(0), capacity(size) { + buf_ = new u8[size]; +} + +QueueBuf::~QueueBuf() { + delete[] buf_; +} + +QueueBuf::QueueBuf(const QueueBuf &buf) { + buf_ = new u8[buf.capacity]; + memcpy(buf_, buf.buf_, buf.capacity); + start = buf.start; + end = buf.end; + capacity = buf.capacity; +} + +QueueBuf& QueueBuf::operator=(const QueueBuf &buf) { + if (capacity < buf.capacity) { + resize(buf.capacity); + } + std::unique_lock lock(mutex); + memcpy(buf_, buf.buf_, buf.capacity); + start = buf.start; + end = buf.end; + lock.unlock(); + return *this; +} + +void QueueBuf::push(u8 *buf, u32 size) { + if (getRemainingSize() < size) { + resize((capacity + size - getRemainingSize()) * 3 / 2); + } + std::unique_lock lock(mutex); + if (end + size <= capacity) { + memcpy(buf_ + end, buf, size); + end += size; + } else { + memcpy(buf_ + end, buf, capacity - end); + size -= capacity - end; + memcpy(buf_, buf + capacity - end, size); + end = size; + } + lock.unlock(); +} + +u32 QueueBuf::pop(u8 *buf, u32 size) { + u32 ret = 0; + if (getAvailableSize() < size) + size = getAvailableSize(); + ret = size; + + std::unique_lock lock(mutex); + if (start + size <= capacity) { + memcpy(buf, buf_ + start, size); + start += size; + } else { + memcpy(buf, buf_ + start, capacity - start); + size -= capacity - start; + memcpy(buf + capacity - start, buf_, size); + start = size; + } + lock.unlock(); + return ret; +} + +void QueueBuf::resize(u32 newSize) { + if (capacity >= newSize) { + return; + } + u32 availableSize = getAvailableSize(); + u8 *oldbuf = buf_; + + std::unique_lock lock(mutex); + buf_ = new u8[newSize]; + if (end >= start) { + memcpy(buf_, oldbuf + start, availableSize); + } else { + memcpy(buf_, oldbuf + start, capacity - start); + memcpy(buf_ + capacity - start, oldbuf, availableSize - (capacity - start)); + } + start = 0; + end = availableSize; + capacity = newSize; + delete[] oldbuf; + lock.unlock(); +} + +void QueueBuf::flush() { + std::unique_lock lock(mutex); + start = 0; + end = 0; + lock.unlock(); +} + +u32 QueueBuf::getAvailableSize() { + u32 availableSize = 0; + if (end >= start) { + availableSize = end - start; + } else { + availableSize = end + capacity - start; + } + return availableSize; +} + +u32 QueueBuf::getRemainingSize() { + return capacity - getAvailableSize(); +} + +static int sceUsbMicPollInputEnd() { + ERROR_LOG(HLE, "UNIMPL sceUsbMicPollInputEnd"); + return 0; +} + +static int sceUsbMicInputBlocking(u32 maxSamples, u32 sampleRate, u32 bufAddr) { + INFO_LOG(HLE, "sceUsbMicInputBlocking: maxSamples: %d, samplerate: %d, bufAddr: %08x", maxSamples, sampleRate, bufAddr); + if (maxSamples <= 0 || (maxSamples & 0x3F) != 0) { + return SCE_USBMIC_ERROR_INVALID_MAX_SAMPLES; + } + + if (sampleRate != 44100 && sampleRate != 22050 && sampleRate != 11025) { + return SCE_USBMIC_ERROR_INVALID_SAMPLERATE; + } + curSampleRate = sampleRate; + curChannels = 1; + return __MicInputBlocking(maxSamples, sampleRate, bufAddr); +} + +static int sceUsbMicInputInitEx(u32 paramAddr) { + ERROR_LOG(HLE, "UNIMPL sceUsbMicInputInitEx: %08x", paramAddr); + return 0; +} + +static int sceUsbMicInput() { + ERROR_LOG(HLE, "UNIMPL sceUsbMicInput"); + return 0; +} +static int sceUsbMicGetInputLength() { + int ret = Microphone::availableAudioBufSize() / 2; + ERROR_LOG(HLE, "UNTEST sceUsbMicGetInputLength(ret: %d)", ret); + return ret; +} + +static int sceUsbMicInputInit(int unknown1, int inputVolume, int unknown2) { + ERROR_LOG(HLE, "UNIMPL sceUsbMicInputInit(unknown1: %d, inputVolume: %d, unknown2: %d)", unknown1, inputVolume, unknown2); + return 0; +} + +static int sceUsbMicWaitInputEnd() { + ERROR_LOG(HLE, "UNIMPL sceUsbMicWaitInputEnd"); + return 0; +} + +int Microphone::startMic(void *param) { +#ifdef HAVE_WIN32_MICROPHONE + if (winMic) + winMic->sendMessage({ CAPTUREDEVIDE_COMMAND::START, param }); +#endif + micState = 1; + return 0; +} + +int Microphone::stopMic() { +#ifdef HAVE_WIN32_MICROPHONE + if (winMic) + winMic->sendMessage({ CAPTUREDEVIDE_COMMAND::STOP, nullptr }); +#endif + micState = 0; + return 0; +} + +bool Microphone::isHaveDevice() { +#ifdef HAVE_WIN32_MICROPHONE + return winMic->getDeviceCounts() >= 1; +#endif + return false; +} + +bool Microphone::isMicStarted() { +#ifdef HAVE_WIN32_MICROPHONE + if(winMic) + return winMic->isStarted(); +#endif + return false; +} + +bool Microphone::isNeedInput() { + return ::isNeedInput; +} + +u32 Microphone::numNeedSamples() { + return ::numNeedSamples; +} + +u32 Microphone::availableAudioBufSize() { + return audioBuf->getAvailableSize(); +} + +int Microphone::addAudioData(u8 *buf, u32 size) { + if (audioBuf) + audioBuf->push(buf, size); + else + return 0; + return size; } +u32 Microphone::getAudioData(u8 *buf, u32 size) { + if(audioBuf) + return audioBuf->pop(buf, size); + + return 0; +} + +void Microphone::flushAudioData() { + audioBuf->flush(); +} + +std::vector Microphone::getDeviceList() { +#ifdef HAVE_WIN32_MICROPHONE + if (winMic) { + return winMic->getDeviceList(); + } +#endif + return std::vector(); +} + +void Microphone::onMicDeviceChange() { + if (Microphone::isMicStarted()) { + Microphone::stopMic(); + // Just use the last param. + Microphone::startMic(nullptr); + } +} + +u32 __MicInputBlocking(u32 maxSamples, u32 sampleRate, u32 bufAddr) { + u32 size = maxSamples << 1; + if (size > numNeedSamples << 1) { + if (!audioBuf) { + audioBuf = new QueueBuf(size); + } else { + audioBuf->resize(size); + } + } + if (!audioBuf) + return 0; + + numNeedSamples = maxSamples; + Microphone::flushAudioData(); + if (!Microphone::isMicStarted()) { + std::vector *param = new std::vector({ sampleRate, 1 }); + Microphone::startMic(param); + } + u64 waitTimeus = 0; + if (Microphone::availableAudioBufSize() < size) { + waitTimeus = (size - Microphone::availableAudioBufSize()) * 1000000 / 2 / sampleRate; + isNeedInput = true; + } + CoreTiming::ScheduleEvent(usToCycles(waitTimeus), eventUsbMicAudioUpdate, __KernelGetCurThread()); + MicWaitInfo waitInfo = { __KernelGetCurThread(), bufAddr, size, sampleRate }; + std::unique_lock lock(wtMutex); + waitingThreads.push_back(waitInfo); + lock.unlock(); + DEBUG_LOG(HLE, "MicInputBlocking: blocking thread(%d)", (int)__KernelGetCurThread()); + __KernelWaitCurThread(WAITTYPE_MICINPUT, 1, size, 0, false, "blocking microphone"); + + return maxSamples; +} + const HLEFunction sceUsbMic[] = { - {0x06128E42, nullptr, "sceUsbMicPollInputEnd", '?', "" }, + {0x06128E42, &WrapI_V, "sceUsbMicPollInputEnd", 'i', "" }, {0x2E6DCDCD, &WrapI_UUU, "sceUsbMicInputBlocking", 'i', "xxx" }, - {0x45310F07, nullptr, "sceUsbMicInputInitEx", '?', "" }, - {0x5F7F368D, nullptr, "sceUsbMicInput", '?', "" }, - {0x63400E20, nullptr, "sceUsbMicGetInputLength", '?', "" }, - {0xB8E536EB, nullptr, "sceUsbMicInputInit", '?', "" }, - {0xF899001C, nullptr, "sceUsbMicWaitInputEnd", '?', "" }, + {0x45310F07, &WrapI_U, "sceUsbMicInputInitEx", 'i', "x" }, + {0x5F7F368D, &WrapI_V , "sceUsbMicInput", 'i', "" }, + {0x63400E20, &WrapI_V, "sceUsbMicGetInputLength", 'i', "" }, + {0xB8E536EB, &WrapI_III, "sceUsbMicInputInit", 'i', "iii" }, + {0xF899001C, &WrapI_V, "sceUsbMicWaitInputEnd", 'i', "" }, }; void Register_sceUsbMic() diff --git a/Core/HLE/sceUsbMic.h b/Core/HLE/sceUsbMic.h index 35e1132861..c073d4cf56 100644 --- a/Core/HLE/sceUsbMic.h +++ b/Core/HLE/sceUsbMic.h @@ -16,4 +16,61 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #pragma once + +#include "sceKernel.h" +#include + void Register_sceUsbMic(); + +void __UsbMicInit(); +void __UsbMicShutdown(); +void __UsbMicDoState(PointerWrap &p); + +struct MicWaitInfo { + SceUID threadID; + u32 addr; + u32 needSize; + u32 sampleRate; +}; + +class QueueBuf { +public: + QueueBuf(u32 size); + ~QueueBuf(); + + QueueBuf(const QueueBuf &buf); + QueueBuf& operator=(const QueueBuf &buf); + + void push(u8 *buf, u32 size); + u32 pop(u8 *buf, u32 size); + void resize(u32 newSize); + void flush(); + u32 getAvailableSize(); + u32 getRemainingSize(); + +private: + u32 start; + u32 end; + u32 capacity; + u8 *buf_; + std::mutex mutex; +}; + +namespace Microphone { + int startMic(void *param); + int stopMic(); + bool isHaveDevice(); + bool isMicStarted(); + bool isNeedInput(); + u32 numNeedSamples(); + u32 availableAudioBufSize(); + + int addAudioData(u8 *buf, u32 size); + u32 getAudioData(u8 *buf, u32 size); + void flushAudioData(); + + std::vector getDeviceList(); + void onMicDeviceChange(); +} + +u32 __MicInputBlocking(u32 maxSamples, u32 sampleRate, u32 bufAddr); diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 41b143e5bd..a9ae9f891f 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -60,6 +60,7 @@ #include "Core/TextureReplacer.h" #include "Core/WebServer.h" #include "Core/HLE/sceUsbCam.h" +#include "Core/HLE/sceUsbMic.h" #include "GPU/Common/PostShader.h" #include "android/jni/TestRunner.h" #include "GPU/GPUInterface.h" @@ -547,6 +548,13 @@ void GameSettingsScreen::CreateViews() { } #endif + std::vector micList = Microphone::getDeviceList(); + if (micList.size() >= 1) { + audioSettings->Add(new ItemHeader(gr->T("Microphone"))); + PopupMultiChoiceDynamic *MicChoice = audioSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sMicDevice, gr->T("Microphone Device"), micList, nullptr, screenManager())); + MicChoice->OnChoice.Handle(this, &GameSettingsScreen::OnMicDeviceChange); + } + #if defined(SDL) std::vector audioDeviceList; SplitString(System_GetProperty(SYSPROP_AUDIO_DEVICE_LIST), '\0', audioDeviceList); @@ -1312,6 +1320,11 @@ UI::EventReturn GameSettingsScreen::OnCameraDeviceChange(UI::EventParams& e) { return UI::EVENT_DONE; } +UI::EventReturn GameSettingsScreen::OnMicDeviceChange(UI::EventParams& e) { + Microphone::onMicDeviceChange(); + return UI::EVENT_DONE; +} + UI::EventReturn GameSettingsScreen::OnAudioDevice(UI::EventParams &e) { auto a = GetI18NCategory("Audio"); if (g_Config.sAudioDevice == a->T("Auto")) { diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index 2279b620ce..f2f27bc206 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -103,6 +103,7 @@ private: UI::EventReturn OnRenderingDevice(UI::EventParams &e); UI::EventReturn OnInflightFramesChoice(UI::EventParams &e); UI::EventReturn OnCameraDeviceChange(UI::EventParams& e); + UI::EventReturn OnMicDeviceChange(UI::EventParams& e); UI::EventReturn OnAudioDevice(UI::EventParams &e); UI::EventReturn OnJitAffectingSetting(UI::EventParams &e); #if PPSSPP_PLATFORM(ANDROID) diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 20bddb0d1f..1689e333cd 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -864,6 +864,8 @@ bool NativeInitGraphics(GraphicsContext *graphicsContext) { if (IsWin7OrHigher()) { winCamera = new WindowsCaptureDevice(CAPTUREDEVIDE_TYPE::VIDEO); winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::INITIALIZE, nullptr }); + winMic = new WindowsCaptureDevice(CAPTUREDEVIDE_TYPE::AUDIO); + winMic->sendMessage({ CAPTUREDEVIDE_COMMAND::INITIALIZE, nullptr }); } #endif @@ -937,6 +939,12 @@ void NativeShutdownGraphics() { delete winCamera; winCamera = nullptr; } + if (winMic) { + winMic->sendMessage({ CAPTUREDEVIDE_COMMAND::SHUTDOWN, nullptr }); + while (!winMic->isShutDown()) {};// Wait for shutting down. + delete winMic; + winMic = nullptr; + } #endif UIBackgroundShutdown(); diff --git a/Windows/CaptureDevice.cpp b/Windows/CaptureDevice.cpp index d6c453c05d..6ae0733e51 100644 --- a/Windows/CaptureDevice.cpp +++ b/Windows/CaptureDevice.cpp @@ -24,8 +24,6 @@ #include "Core/HLE/sceUsbCam.h" #include "Core/Config.h" -bool isDeviceChanged = false; - namespace MFAPI { HINSTANCE Mflib; HINSTANCE Mfplatlib; @@ -87,6 +85,7 @@ bool unRegisterCMPTMFApis() { } WindowsCaptureDevice *winCamera; +WindowsCaptureDevice *winMic; // TODO: Add more formats, but need some tests. VideoFormatTransform g_VideoFormats[] = @@ -97,19 +96,28 @@ VideoFormatTransform g_VideoFormats[] = { MFVideoFormat_NV12, AV_PIX_FMT_NV12 } }; -const int g_cVideoFormats = 4; +AudioFormatTransform g_AudioFormats[] = { + { MFAudioFormat_PCM, 8, AV_SAMPLE_FMT_U8 }, + { MFAudioFormat_PCM, 16, AV_SAMPLE_FMT_S16 }, + { MFAudioFormat_PCM, 32, AV_SAMPLE_FMT_S32 }, + { MFAudioFormat_Float, 32, AV_SAMPLE_FMT_FLT } +}; -MediaParam defaultVideoParam = { 640, 480, 0, MFVideoFormat_RGB24 }; -MediaParam defaultAudioParam = { 44100, 2, 0, MFAudioFormat_PCM }; +const int g_cVideoFormats = ARRAYSIZE(g_VideoFormats); +const int g_cAudioFormats = ARRAYSIZE(g_AudioFormats); + +MediaParam defaultVideoParam = { 640, 480, 0, MFVideoFormat_RGB24 }; +MediaParam defaultAudioParam = { 44100, 2, 16, MFAudioFormat_PCM }; HRESULT GetDefaultStride(IMFMediaType *pType, LONG *plStride); -ReaderCallback::ReaderCallback(WindowsCaptureDevice *device): img_convert_ctx(nullptr){ +ReaderCallback::ReaderCallback(WindowsCaptureDevice *device): img_convert_ctx(nullptr), resample_ctx(nullptr){ this->device = device; } ReaderCallback::~ReaderCallback() { sws_freeContext(img_convert_ctx); + swr_free(&resample_ctx); } HRESULT ReaderCallback::QueryInterface(REFIID riid, void** ppv) @@ -130,7 +138,6 @@ HRESULT ReaderCallback::OnReadSample( IMFSample *pSample) { HRESULT hr = S_OK; IMFMediaBuffer *pBuffer = nullptr; - LONG lStride = 0; std::lock_guard lock(device->sdMutex); if (device->isShutDown()) return hr; @@ -151,6 +158,7 @@ HRESULT ReaderCallback::OnReadSample( int imgJpegSize = device->imgJpegSize; unsigned char* invertedSrcImg = nullptr; LONG srcPadding = 0; + LONG lStride = 0; UINT32 srcW = device->deviceParam.width; UINT32 srcH = device->deviceParam.height; @@ -207,14 +215,53 @@ HRESULT ReaderCallback::OnReadSample( nullptr ); } - delete videoBuffer; break; } - case CAPTUREDEVIDE_TYPE::AUDIO: - // TODO: + case CAPTUREDEVIDE_TYPE::AUDIO: { + BYTE *sampleBuf = nullptr; + DWORD length = 0; + u32 sizeAfterResample = 0; + // pSample can be null, in this case ReadSample still should be called to request next frame. + if (pSample && Microphone::isNeedInput()) { + pBuffer->Lock(&sampleBuf, nullptr, &length); + if (!device->rawAudioBuf) { + device->rawAudioBuf = new QueueBuf(length * 2); // Alloc enough space. + } + device->rawAudioBuf->push(sampleBuf, length); + if (device->needResample()) { + sizeAfterResample = device->rawAudioBuf->getAvailableSize() * device->targetMediaParam.sampleRate / device->deviceParam.sampleRate / device->deviceParam.channels; + // Wait until have enough audio data. + if (sizeAfterResample + Microphone::availableAudioBufSize() >= Microphone::numNeedSamples() * 2) { + u32 rawAudioBufSize = device->rawAudioBuf->getAvailableSize(); + u8 *tempbuf = new u8[rawAudioBufSize]; + device->rawAudioBuf->pop(tempbuf, rawAudioBufSize); + sizeAfterResample = doResample( + &device->resampleBuf, device->targetMediaParam.sampleRate, device->targetMediaParam.channels, &device->resampleBufSize, + tempbuf, device->deviceParam.sampleRate, device->deviceParam.channels, device->deviceParam.audioFormat, rawAudioBufSize, device->deviceParam.bitsPerSample); + delete[] tempbuf; + if (device->resampleBuf) + Microphone::addAudioData(device->resampleBuf, sizeAfterResample); + } + } else { + Microphone::addAudioData(sampleBuf, length); + } + pBuffer->Unlock(); + } + // Request the next frame. + if (SUCCEEDED(hr)) { + hr = device->m_pReader->ReadSample( + (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, + 0, + nullptr, + nullptr, + nullptr, + nullptr + ); + } break; } + } } SafeRelease(&pBuffer); @@ -229,6 +276,14 @@ AVPixelFormat ReaderCallback::getAVVideoFormatbyMFVideoFormat(const GUID &MFVide return AV_PIX_FMT_RGB24; } +AVSampleFormat ReaderCallback::getAVAudioFormatbyMFAudioFormat(const GUID &MFAudioFormat, const u32 &bitsPerSample) { + for (int i = 0; i < g_cAudioFormats; i++) { + if (MFAudioFormat == g_AudioFormats[i].MFAudioFormat && bitsPerSample == g_AudioFormats[i].bitsPerSample) + return g_AudioFormats[i].AVAudioFormat; + } + return AV_SAMPLE_FMT_S16; +} + void ReaderCallback::imgConvert( unsigned char *dst, unsigned int &dstW, unsigned int &dstH, int dstLineSizes[4], unsigned char *src, const unsigned int &srcW, const unsigned int &srcH, const GUID &srcFormat, @@ -237,9 +292,9 @@ void ReaderCallback::imgConvert( unsigned char *pSrc[4]; unsigned char *pDst[4]; - AVPixelFormat srcAvFormat = getAVVideoFormatbyMFVideoFormat(srcFormat); + AVPixelFormat srcAVFormat = getAVVideoFormatbyMFVideoFormat(srcFormat); - av_image_fill_linesizes(srcLineSizes, srcAvFormat, srcW); + av_image_fill_linesizes(srcLineSizes, srcAVFormat, srcW); // Is this correct? if (srcPadding != 0) { @@ -249,16 +304,14 @@ void ReaderCallback::imgConvert( } } - av_image_fill_pointers(pSrc, srcAvFormat, srcH, src, srcLineSizes); + av_image_fill_pointers(pSrc, srcAVFormat, srcH, src, srcLineSizes); av_image_fill_pointers(pDst, AV_PIX_FMT_RGB24, dstH, dst, dstLineSizes); - - if (img_convert_ctx == nullptr) { img_convert_ctx = sws_getContext( srcW, srcH, - srcAvFormat, + srcAVFormat, dstW, dstH, AV_PIX_FMT_RGB24, @@ -348,28 +401,68 @@ void ReaderCallback::imgInvertNV12(unsigned char *dst, int &dstStride, unsigned } } +u32 ReaderCallback::doResample(u8 **dst, u32 &dstSampleRate, u32 &dstChannels, u32 *dstSize, u8 *src, const u32 &srcSampleRate, const u32 &srcChannels, const GUID &srcFormat, const u32& srcSize, const u32& srcBitsPerSample) { + AVSampleFormat srcAVFormat = getAVAudioFormatbyMFAudioFormat(srcFormat, srcBitsPerSample); + int outSamplesCount = 0; + if (resample_ctx == nullptr) { + resample_ctx = swr_alloc_set_opts(nullptr, + av_get_default_channel_layout(dstChannels), + AV_SAMPLE_FMT_S16, + dstSampleRate, + av_get_default_channel_layout(srcChannels), + srcAVFormat, + srcSampleRate, + 0, + nullptr); + if (resample_ctx == nullptr || swr_init(resample_ctx) < 0) { + swr_free(&resample_ctx); + return 0; + } + } + int srcSamplesCount = srcSize / srcChannels / av_get_bytes_per_sample(srcAVFormat); // per channel. + int outCount = srcSamplesCount * dstSampleRate / srcSampleRate + 256; + unsigned int outSize = av_samples_get_buffer_size(nullptr, dstChannels, outCount, AV_SAMPLE_FMT_S16, 0); + + if (!*dst) { + *dst = (u8 *)av_malloc(outSize); + *dstSize = outSize; + } + if (!*dst) + return 0; + + if(*dstSize < outSize) + av_fast_malloc(dst, dstSize, outSize); + + outSamplesCount = swr_convert(resample_ctx, dst, outCount, (const uint8_t **)&src, srcSamplesCount); + if (outSamplesCount < 0) + return 0; + return av_samples_get_buffer_size(nullptr, dstChannels, outSamplesCount, AV_SAMPLE_FMT_S16, 0); +} + WindowsCaptureDevice::WindowsCaptureDevice(CAPTUREDEVIDE_TYPE type) : type(type), m_pCallback(nullptr), m_pSource(nullptr), m_pReader(nullptr), + imageRGB(nullptr), + imageJpeg(nullptr), + imgJpegSize(0), + resampleBuf(nullptr), + resampleBufSize(0), + rawAudioBuf(nullptr), error(CAPTUREDEVIDE_ERROR_NO_ERROR), errorMessage(""), + isDeviceChanged(false), state(CAPTUREDEVIDE_STATE::UNINITIALIZED) { param = { 0 }; + deviceParam = { 0 }; switch (type) { case CAPTUREDEVIDE_TYPE::VIDEO: targetMediaParam = defaultVideoParam; - imageRGB = (unsigned char*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_RGB24, targetMediaParam.width, targetMediaParam.height, 1)); - imgJpegSize = av_image_get_buffer_size(AV_PIX_FMT_YUVJ411P, targetMediaParam.width, targetMediaParam.height, 1); - imageJpeg = (unsigned char*)av_malloc(imgJpegSize); break; case CAPTUREDEVIDE_TYPE::AUDIO: - // TODO: targetMediaParam = defaultAudioParam; - imageRGB = nullptr; - imageJpeg = nullptr; break; } @@ -380,11 +473,12 @@ WindowsCaptureDevice::WindowsCaptureDevice(CAPTUREDEVIDE_TYPE type) : WindowsCaptureDevice::~WindowsCaptureDevice() { switch (type) { case CAPTUREDEVIDE_TYPE::VIDEO: - av_free(imageRGB); - av_free(imageJpeg); + av_freep(&imageRGB); + av_freep(&imageJpeg); break; case CAPTUREDEVIDE_TYPE::AUDIO: - // TODO: + av_freep(&resampleBuf); + delete rawAudioBuf; break; } } @@ -412,7 +506,7 @@ bool WindowsCaptureDevice::init() { return true; } -bool WindowsCaptureDevice::start(UINT32 width, UINT32 height) { +bool WindowsCaptureDevice::start(void *startParam) { HRESULT hr = S_OK; IMFAttributes *pAttributes = nullptr; IMFMediaType *pType = nullptr; @@ -434,16 +528,14 @@ bool WindowsCaptureDevice::start(UINT32 width, UINT32 height) { return false; } - targetMediaParam.width = width; - targetMediaParam.height = height; - av_image_fill_linesizes(imgRGBLineSizes, AV_PIX_FMT_RGB24, targetMediaParam.width); - m_pCallback = new ReaderCallback(this); + std::string selectedDeviceName = type == CAPTUREDEVIDE_TYPE::VIDEO ? g_Config.sCameraDevice : g_Config.sMicDevice; + switch (state) { case CAPTUREDEVIDE_STATE::STOPPED: for (auto &name : deviceList) { - if (name == g_Config.sCameraDevice) { + if (name == selectedDeviceName) { selection = count; break; } @@ -477,7 +569,21 @@ bool WindowsCaptureDevice::start(UINT32 width, UINT32 height) { if (SUCCEEDED(hr)) { switch (type) { - case CAPTUREDEVIDE_TYPE::VIDEO: + case CAPTUREDEVIDE_TYPE::VIDEO: { + if (startParam) { + std::vector *resolution = static_cast*>(startParam); + targetMediaParam.width = resolution->at(0); + targetMediaParam.height = resolution->at(1); + delete resolution; + } + + av_freep(&imageRGB); + av_freep(&imageJpeg); + imageRGB = (unsigned char*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_RGB24, targetMediaParam.width, targetMediaParam.height, 1)); + imgJpegSize = av_image_get_buffer_size(AV_PIX_FMT_YUVJ411P, targetMediaParam.width, targetMediaParam.height, 1); + imageJpeg = (unsigned char*)av_malloc(imgJpegSize); + av_image_fill_linesizes(imgRGBLineSizes, AV_PIX_FMT_RGB24, targetMediaParam.width); + for (DWORD i = 0; ; i++) { hr = m_pReader->GetNativeMediaType( (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, @@ -512,13 +618,45 @@ bool WindowsCaptureDevice::start(UINT32 width, UINT32 height) { nullptr ); } - break; + } - case CAPTUREDEVIDE_TYPE::AUDIO: - // TODO: + case CAPTUREDEVIDE_TYPE::AUDIO: { + if (startParam) { + std::vector *micParam = static_cast*>(startParam); + targetMediaParam.sampleRate = micParam->at(0); + targetMediaParam.channels = micParam->at(1); + delete micParam; + } + + for (DWORD i = 0; ; i++) { + hr = m_pReader->GetNativeMediaType( + (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, + i, + &pType + ); + + if (FAILED(hr)) { break; } + + hr = setDeviceParam(pType); + + if (SUCCEEDED(hr)) + break; + } + + if (SUCCEEDED(hr)) { + hr = m_pReader->ReadSample( + (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, + 0, + nullptr, + nullptr, + nullptr, + nullptr + ); + } break; } + } } if (FAILED(hr)) { @@ -597,13 +735,13 @@ std::vector WindowsCaptureDevice::getDeviceList(bool forceEnum, int if (SUCCEEDED(hr)) { // Get the size needed first - dwMinSize = WideCharToMultiByte(CP_OEMCP, NULL, pwstrName, -1, nullptr, 0, nullptr, FALSE); + dwMinSize = WideCharToMultiByte(CP_UTF8, NULL, pwstrName, -1, nullptr, 0, nullptr, FALSE); if (dwMinSize == 0) hr = -1; } if (SUCCEEDED(hr)) { cstrName = new char[dwMinSize]; - WideCharToMultiByte(CP_OEMCP, NULL, pwstrName, -1, cstrName, dwMinSize, NULL, FALSE); + WideCharToMultiByte(CP_UTF8, NULL, pwstrName, -1, cstrName, dwMinSize, NULL, FALSE); strName = cstrName; delete[] cstrName; @@ -670,7 +808,46 @@ HRESULT WindowsCaptureDevice::setDeviceParam(IMFMediaType *pType) { break; case CAPTUREDEVIDE_TYPE::AUDIO: - // TODO: + hr = pType->GetGUID(MF_MT_SUBTYPE, &subtype); + if (FAILED(hr)) + break; + + for (int i = 0; i < g_cVideoFormats; i++) { + if (subtype == g_AudioFormats[i].MFAudioFormat) { + deviceParam.audioFormat = subtype; + getFormat = true; + break; + } + } + + if (!getFormat) { + for (int i = 0; i < g_cAudioFormats; i++) { + hr = pType->SetGUID(MF_MT_SUBTYPE, g_AudioFormats[i].MFAudioFormat); + if (FAILED(hr)) + continue; + + hr = m_pReader->SetCurrentMediaType( + (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, + NULL, + pType + ); + + if (SUCCEEDED(hr)) { + deviceParam.audioFormat = g_AudioFormats[i].MFAudioFormat; + getFormat = true; + break; + } + } + } + if (SUCCEEDED(hr)) + hr = pType->GetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, &deviceParam.sampleRate); + + if (SUCCEEDED(hr)) + hr = pType->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, &deviceParam.channels); + + if (SUCCEEDED(hr)) + hr = pType->GetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, (UINT32 *)&deviceParam.bitsPerSample); + break; } @@ -701,10 +878,11 @@ void WindowsCaptureDevice::messageHandler() { CoInitializeEx(NULL, COINIT_MULTITHREADED); MFStartup(MF_VERSION); CAPTUREDEVIDE_MESSAGE message; - std::vector* resolution; if (type == CAPTUREDEVIDE_TYPE::VIDEO) { setCurrentThreadName("Camera"); + } else if (type == CAPTUREDEVIDE_TYPE::AUDIO) { + setCurrentThreadName("Microphone"); } while ((message = getMessage()).command != CAPTUREDEVIDE_COMMAND::SHUTDOWN) { @@ -713,8 +891,7 @@ void WindowsCaptureDevice::messageHandler() { init(); break; case CAPTUREDEVIDE_COMMAND::START: - resolution = static_cast*>(message.opacity); - start(resolution->at(0), resolution->at(1)); + start(message.opacity); break; case CAPTUREDEVIDE_COMMAND::STOP: stop(); @@ -781,6 +958,13 @@ HRESULT WindowsCaptureDevice::enumDevices() { return hr; } +bool WindowsCaptureDevice::needResample() { + return deviceParam.sampleRate != targetMediaParam.sampleRate || + deviceParam.channels != targetMediaParam.channels || + deviceParam.audioFormat != targetMediaParam.audioFormat || + deviceParam.bitsPerSample != targetMediaParam.bitsPerSample; +} + //----------------------------------------------------------------------------- // GetDefaultStride // diff --git a/Windows/CaptureDevice.h b/Windows/CaptureDevice.h index aa0d0dc744..60f9b61b05 100644 --- a/Windows/CaptureDevice.h +++ b/Windows/CaptureDevice.h @@ -26,10 +26,13 @@ #include #include +#include "Core/HLE/sceUsbMic.h" + #ifdef __cplusplus extern "C" { #include "libavformat/avformat.h" #include "libswscale/swscale.h" +#include "libswresample/swresample.h" #include "libavutil/imgutils.h" } #endif // __cplusplus @@ -39,6 +42,12 @@ struct VideoFormatTransform { AVPixelFormat AVVideoFormat; }; +struct AudioFormatTransform { + GUID MFAudioFormat; + u32 bitsPerSample; + AVSampleFormat AVAudioFormat; +}; + enum class CAPTUREDEVIDE_TYPE { VIDEO, AUDIO @@ -90,8 +99,8 @@ union MediaParam { struct { UINT32 sampleRate; UINT32 channels; - LONG padding; - GUID audioFomat; + LONG bitsPerSample; + GUID audioFormat; }; }; @@ -127,9 +136,10 @@ public: STDMETHODIMP OnFlush(DWORD) { return S_OK; } AVPixelFormat getAVVideoFormatbyMFVideoFormat(const GUID &MFVideoFormat); + AVSampleFormat getAVAudioFormatbyMFAudioFormat(const GUID &MFAudioFormat, const u32 &bitsPerSample); /* - * Always convet the image to RGB24 + * Always convert the image to RGB24 * @param dst/src pointer to destination/source image * @param dstW/srcW, dstH/srcH destination/source image's width and height in pixels * @param dstLineSizes get the linesize of each plane by av_image_fill_linesizes() @@ -149,10 +159,20 @@ public: void imgInvertYUY2(unsigned char *dst, int &dstStride, unsigned char *src, const int &srcStride, const int &h); void imgInvertNV12(unsigned char *dst, int &dstStride, unsigned char *src, const int &srcStride, const int &h); + /* + * Always resample to uncompressed signed 16bits + * @param dst pointer to pointer of dst buffer could be a nullptr, would be overwritten by a new pointer if space too small or a nullptr, should be freed by av_free/av_freep by caller + * @param dstSize pointer to size of the dst buffer, could be modified after this func + * @param srcFormat MF_MT_SUBTYPE attribute of the source audio data + * @param srcSize size of valid data in the source buffer, in bytes + * @return size of output in bytes + */ + u32 doResample(u8 **dst, u32 &dstSampleRate, u32 &dstChannels, u32 *dstSize, u8 *src, const u32 &srcSampleRate, const u32 &srcChannels, const GUID &srcFormat, const u32 &srcSize, const u32& srcBitsPerSamples); protected: WindowsCaptureDevice *device; SwsContext *img_convert_ctx; + SwrContext *resample_ctx; }; class WindowsCaptureDevice { @@ -160,10 +180,10 @@ public: WindowsCaptureDevice(CAPTUREDEVIDE_TYPE type); ~WindowsCaptureDevice(); - static void CheckDevices(); + void CheckDevices(); bool init(); - bool start(UINT32 width, UINT32 height); + bool start(void *startParam); bool stop(); CAPTUREDEVIDE_ERROR getError() const { return error; } @@ -178,12 +198,15 @@ public: HRESULT setDeviceParam(IMFMediaType *pType); bool isShutDown() const { return state == CAPTUREDEVIDE_STATE::SHUTDOWN; } + bool isStarted() const { return state == CAPTUREDEVIDE_STATE::STARTED; } void sendMessage(CAPTUREDEVIDE_MESSAGE message); CAPTUREDEVIDE_MESSAGE getMessage(); HRESULT enumDevices(); + bool needResample(); + friend class ReaderCallback; protected: @@ -199,6 +222,8 @@ protected: CAPTUREDEVIDE_ERROR error; std::string errorMessage; + bool isDeviceChanged; + // MF interface. ReaderCallback *m_pCallback; IMFSourceReader *m_pReader; @@ -220,6 +245,12 @@ protected: int imgRGBLineSizes[4]; unsigned char *imageJpeg; int imgJpegSize; + +//Microphone only + u8 *resampleBuf; + u32 resampleBufSize; + QueueBuf *rawAudioBuf; }; extern WindowsCaptureDevice *winCamera; +extern WindowsCaptureDevice *winMic; diff --git a/Windows/MainWindow.cpp b/Windows/MainWindow.cpp index 3d835c9ea0..a735878ba0 100644 --- a/Windows/MainWindow.cpp +++ b/Windows/MainWindow.cpp @@ -849,7 +849,10 @@ namespace MainWindow #ifndef _M_ARM DinputDevice::CheckDevices(); #endif - WindowsCaptureDevice::CheckDevices(); + if (winCamera) + winCamera->CheckDevices(); + if (winMic) + winMic->CheckDevices(); return DefWindowProc(hWnd, message, wParam, lParam); case WM_VERYSLEEPY_MSG: