// Copyright (c) 2019- PPSSPP Project. // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, version 2.0 or later versions. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License 2.0 for more details. // A copy of the GPL 2.0 should have been included with the program. // If not, see http://www.gnu.org/licenses/ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include #include "base/NativeApp.h" #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeFuncs.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" #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", 0, 1); if (!s) { return; } Do(p, numNeedSamples); Do(p, waitingThreads); Do(p, isNeedInput); Do(p, curSampleRate); Do(p, curChannels); Do(p, 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 })); } } } 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, &WrapI_V, "sceUsbMicPollInputEnd", 'i', "" }, {0x2E6DCDCD, &WrapI_UUU, "sceUsbMicInputBlocking", 'i', "xxx" }, {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() { RegisterModule("sceUsbMic", ARRAY_SIZE(sceUsbMic), sceUsbMic); }