From f9cab3270f8eca2092fe9720cbad5927525852ca Mon Sep 17 00:00:00 2001 From: Zion Nimchuk Date: Wed, 14 Mar 2018 13:27:56 -0700 Subject: [PATCH] Initial work on cellMic --- rpcs3/Emu/Cell/Modules/cellAvconfExt.cpp | 24 +- rpcs3/Emu/Cell/Modules/cellMic.cpp | 364 ++++++++++++++++++----- rpcs3/Emu/Cell/Modules/cellMic.h | 95 ++++-- 3 files changed, 392 insertions(+), 91 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellAvconfExt.cpp b/rpcs3/Emu/Cell/Modules/cellAvconfExt.cpp index 91f3e93068..f748f87bf8 100644 --- a/rpcs3/Emu/Cell/Modules/cellAvconfExt.cpp +++ b/rpcs3/Emu/Cell/Modules/cellAvconfExt.cpp @@ -38,6 +38,17 @@ s32 cellVideoOutSetupDisplay() s32 cellAudioInGetDeviceInfo(u32 deviceNumber, u32 deviceIndex, vm::ptr info) { cellAvconfExt.todo("cellAudioInGetDeviceInfo(deviceNumber=0x%x, deviceIndex=0x%x, info=*0x%x)", deviceNumber, deviceIndex, info); + info->portType = CELL_AUDIO_IN_PORT_USB; + info->availableModeCount = 1; + info->state = CELL_AUDIO_IN_DEVICE_STATE_AVAILABLE; + info->deviceNumber = 0; + // Some games check if deviceId and type are the same. + info->deviceId = 0xDEADBEEF; + info->type = 0xBEEFDEAD; + info->availableModes[0].type = CELL_AUDIO_IN_CODING_TYPE_LPCM; + info->availableModes[0].channel = CELL_AUDIO_IN_CHNUM_NONE; + info->availableModes[0].fs = CELL_AUDIO_IN_FS_8KHZ | CELL_AUDIO_IN_FS_12KHZ | CELL_AUDIO_IN_FS_16KHZ | CELL_AUDIO_IN_FS_24KHZ | CELL_AUDIO_IN_FS_32KHZ | CELL_AUDIO_IN_FS_48KHZ; + strcpy(info->name, "USB Camera"); return CELL_OK; } @@ -65,7 +76,18 @@ s32 cellVideoOutGetGamma(u32 videoOut, vm::ptr gamma) s32 cellAudioInGetAvailableDeviceInfo(u32 count, vm::ptr info) { cellAvconfExt.todo("cellAudioInGetAvailableDeviceInfo(count=0x%x, info=*0x%x)", count, info); - return 0; // number of available devices + info->portType = CELL_AUDIO_IN_PORT_USB; + info->availableModeCount = 1; + info->state = CELL_AUDIO_IN_DEVICE_STATE_AVAILABLE; + info->deviceNumber = 0; + // Some games check if deviceId and type are the same. + info->deviceId = 0xDEADBEEF; + info->type = 0xBEEFDEAD; + info->availableModes[0].type = CELL_AUDIO_IN_CODING_TYPE_LPCM; + info->availableModes[0].channel = CELL_AUDIO_IN_CHNUM_NONE; + info->availableModes[0].fs = CELL_AUDIO_IN_FS_8KHZ | CELL_AUDIO_IN_FS_12KHZ | CELL_AUDIO_IN_FS_16KHZ | CELL_AUDIO_IN_FS_24KHZ | CELL_AUDIO_IN_FS_32KHZ | CELL_AUDIO_IN_FS_48KHZ; + strcpy(info->name, "USB Camera"); + return 1; // number of available devices } s32 cellAudioOutGetAvailableDeviceInfo(u32 count, vm::ptr info) diff --git a/rpcs3/Emu/Cell/Modules/cellMic.cpp b/rpcs3/Emu/Cell/Modules/cellMic.cpp index 2c1be4507a..f5988c800b 100644 --- a/rpcs3/Emu/Cell/Modules/cellMic.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMic.cpp @@ -3,62 +3,198 @@ #include "Emu/Cell/PPUModule.h" #include "cellMic.h" +#include +#include +#include LOG_CHANNEL(cellMic); +void mic_thread::on_init(const std::shared_ptr& _this) +{ + named_thread::on_init(_this); +} + +void mic_thread::on_task() +{ + while (micInited && !Emu.IsStopped()) + { + if (Emu.IsPaused()) + { + std::this_thread::sleep_for(1ms); // hack from cellAudio + continue; + } + if (!micOpened || !micStarted) + continue; + + // If event queue is not set, then we can't send any events + if (eventQueueKey == 0) + continue; + + std::this_thread::sleep_for(1s); + + // Make sure the mic thread wasn't stopped while we were sleeping + if (!micInited) + break; + + auto micQueue = lv2_event_queue::find(eventQueueKey); + + micQueue->send(0, CELL_MIC_DATA, 0, 0); + } +} + +/// Initialization/Shutdown Functions + s32 cellMicInit() { - cellMic.warning("cellMicInit()"); + cellMic.trace("cellMicInit()"); + const auto micThread = fxm::make(); + micInited = true; + if (!micThread) + return CELL_MIC_ERROR_ALREADY_INIT; return CELL_OK; } s32 cellMicEnd() { - cellMic.warning("cellMicEnd()"); + cellMic.trace("cellMicEnd()"); + const auto micThread = fxm::withdraw(); + micInited = false; + if (!micThread) + return CELL_MIC_ERROR_NOT_INIT; return CELL_OK; } -s32 cellMicOpen() +/// Open/Close Microphone Functions + +s32 cellMicOpen(u32 deviceNumber, u32 sampleRate) { - UNIMPLEMENTED_FUNC(cellMic); + cellMic.todo("cellMicOpen(deviceNumber=%um sampleRate=%u)", deviceNumber, sampleRate); + const auto micThread = fxm::get(); + if (!micThread) + return CELL_MIC_ERROR_NOT_INIT; + + if (micThread->micOpened) + return CELL_MIC_ERROR_ALREADY_OPEN; + + micThread->DspFrequency = sampleRate; + micThread->micOpened = true; return CELL_OK; } -s32 cellMicClose() +s32 cellMicOpenRaw(u32 deviceNumber, u32 sampleRate, u32 maxChannels) { - UNIMPLEMENTED_FUNC(cellMic); + cellMic.todo("cellMicOpenRaw(deviceNumber=%d, sampleRate=%d, maxChannels=%d)", deviceNumber, sampleRate, maxChannels); + const auto micThread = fxm::get(); + if (!micThread) + return CELL_MIC_ERROR_NOT_INIT; + + if (micThread->micOpened) + return CELL_MIC_ERROR_ALREADY_OPEN; + + micThread->rawFrequency = sampleRate; + micThread->micOpened = true; return CELL_OK; } -s32 cellMicGetDeviceGUID() +s32 cellMicOpenEx(u32 deviceNumber, u32 rawSampleRate, u32 rawChannel, u32 DSPSampleRate, u32 bufferSizeMS, u8 signalType) { - UNIMPLEMENTED_FUNC(cellMic); + cellMic.todo("cellMicOpenEx(deviceNumber=%d, rawSampleRate=%d, rawChannel=%d, DSPSampleRate=%d, bufferSizeMS=%d, signalType=0x%x)", deviceNumber, rawSampleRate, rawChannel, DSPSampleRate, + bufferSizeMS, signalType); + const auto micThread = fxm::get(); + if (!micThread) + return CELL_MIC_ERROR_NOT_INIT; + + if (micThread->micOpened) + return CELL_MIC_ERROR_ALREADY_OPEN; + + micThread->rawFrequency = rawSampleRate; + micThread->DspFrequency = DSPSampleRate; + micThread->micOpened = true; return CELL_OK; } -s32 cellMicGetType() +u8 cellMicIsOpen(u32 deviceNumber) { - UNIMPLEMENTED_FUNC(cellMic); + cellMic.warning("cellMicIsOpen(deviceNumber=%d)", deviceNumber); + const auto micThread = fxm::get(); + if (!micThread) + return false; + + return micThread->micOpened; +} + +s32 cellMicIsAttached(u32 deviceNumber) +{ + cellMic.warning("cellMicIsAttached(deviceNumber=%d)", deviceNumber); + return 1; +} + +s32 cellMicClose(u32 deviceNumber) +{ + cellMic.warning("cellMicClose(deviceNumber=%d)", deviceNumber); + const auto micThread = fxm::get(); + if (!micThread) + return CELL_MIC_ERROR_NOT_INIT; + + micThread->micOpened = false; + return CELL_OK; } -s32 cellMicIsAttached() +/// Starting/Stopping Microphone Functions + +s32 cellMicStart(u32 deviceNumber) { - UNIMPLEMENTED_FUNC(cellMic); + cellMic.warning("cellMicStart(deviceNumber=%d)", deviceNumber); + const auto micThread = fxm::get(); + if (!micThread) + return CELL_MIC_ERROR_NOT_INIT; + + if (!micThread->micOpened) + return CELL_MIC_ERROR_NOT_OPEN; + + micThread->micStarted = true; + return CELL_OK; } -s32 cellMicIsOpen() +s32 cellMicStartEx(u32 deviceNumber, u32 flags) { - UNIMPLEMENTED_FUNC(cellMic); + cellMic.todo("cellMicStartEx(deviceNumber=%d, flags=%d)", deviceNumber, flags); + const auto micThread = fxm::get(); + if (!micThread) + return CELL_MIC_ERROR_NOT_INIT; + + if (!micThread->micOpened) + return CELL_MIC_ERROR_NOT_OPEN; + + micThread->micStarted = true; + return CELL_OK; } -s32 cellMicGetDeviceAttr() +s32 cellMicStop(u32 deviceNumber) { - UNIMPLEMENTED_FUNC(cellMic); + cellMic.todo("cellMicStop(deviceNumber=%d)", deviceNumber); + const auto micThread = fxm::get(); + if (!micThread) + return CELL_MIC_ERROR_NOT_INIT; + + if (!micThread->micOpened) + return CELL_MIC_ERROR_NOT_OPEN; + + micThread->micStarted = false; + + return CELL_OK; +} + +/// Microphone Attributes/States Functions + +s32 cellMicGetDeviceAttr(u32 deviceNumber, vm::ptr deviceAttributes, vm::ptr arg1, vm::ptr arg2) +{ + cellMic.todo("cellMicGetDeviceAttr(deviceNumber=%d, deviceAttribute=*0x%x, arg1=*0x%x, arg2=*0x%x)", deviceNumber, deviceAttributes, arg1, arg2); return CELL_OK; } @@ -80,94 +216,158 @@ s32 cellMicSetSignalAttr() return CELL_OK; } -s32 cellMicGetSignalState() +s32 cellMicGetSignalState(u32 deviceNumber, CellMicSignalState signalState, vm::ptr value) { - UNIMPLEMENTED_FUNC(cellMic); + cellMic.todo("cellMicGetSignalState(deviceNumber=%d, signalSate=%d, value=0x%x)", deviceNumber, (int)signalState, value); + const auto micThread = fxm::get(); + if (!micThread) + return CELL_MIC_ERROR_NOT_INIT; + + be_t* ival = (be_t*)value.get_ptr(); + be_t* fval = (be_t*)value.get_ptr(); + + switch (signalState) + { + case CELL_MIC_SIGSTATE_LOCTALK: + *ival = 9; // Someone is probably talking + break; + case CELL_MIC_SIGSTATE_FARTALK: + // TODO + break; + case CELL_MIC_SIGSTATE_NSR: + // TODO + break; + case CELL_MIC_SIGSTATE_AGC: + // TODO + break; + case CELL_MIC_SIGSTATE_MICENG: + *fval = 40.0f; // 40 decibels + break; + case CELL_MIC_SIGSTATE_SPKENG: + // TODO + break; + } + return CELL_OK; } -s32 cellMicStart() +s32 cellMicGetFormatRaw(u32 deviceNumber, vm::ptr format) { - UNIMPLEMENTED_FUNC(cellMic); + cellMic.warning("cellMicGetFormatRaw(deviceNumber=%d, format=0x%x)", deviceNumber, format); + const auto micThread = fxm::get(); + if (!micThread) + return CELL_MIC_ERROR_NOT_INIT; + + format->channelNum = 4; + format->subframeSize = 2; + format->bitResolution = micThread->bitResolution; + format->dataType = 1; + format->sampleRate = micThread->rawFrequency; + return CELL_OK; } -s32 cellMicRead() +s32 cellMicGetFormatAux(u32 deviceNumber, vm::ptr format) { - UNIMPLEMENTED_FUNC(cellMic); + cellMic.warning("cellMicGetFormatAux(deviceNumber=%d, format=0x%x)", deviceNumber, format); + const auto micThread = fxm::get(); + if (!micThread) + return CELL_MIC_ERROR_NOT_INIT; + + format->channelNum = 4; + format->subframeSize = 2; + format->bitResolution = micThread->bitResolution; + format->dataType = 1; + format->sampleRate = micThread->AuxFrequency; + return CELL_OK; } -s32 cellMicStop() +s32 cellMicGetFormatDsp(u32 deviceNumber, vm::ptr format) { - UNIMPLEMENTED_FUNC(cellMic); + cellMic.warning("cellMicGetFormatDsp(deviceNumber=%d, format=0x%x)", deviceNumber, format); + const auto micThread = fxm::get(); + if (!micThread) + return CELL_MIC_ERROR_NOT_INIT; + + format->channelNum = 4; + format->subframeSize = 2; + format->bitResolution = micThread->bitResolution; + format->dataType = 1; + format->sampleRate = micThread->DspFrequency; + return CELL_OK; } -s32 cellMicReset() +/// Event Queue Functions + +s32 cellMicSetNotifyEventQueue(u64 key) { - UNIMPLEMENTED_FUNC(cellMic); + cellMic.warning("cellMicSetNotifyEventQueue(key=0x%llx)", key); + const auto micThread = fxm::get(); + if (!micThread) + return CELL_MIC_ERROR_NOT_INIT; + + // default mic queue size = 4 + auto micQueue = lv2_event_queue::find(key); + micQueue->send(0, CELL_MIC_ATTACH, 0, 0); + micThread->eventQueueKey = key; + return CELL_OK; } -s32 cellMicSetNotifyEventQueue() +s32 cellMicSetNotifyEventQueue2(u64 key, u64 source) { - UNIMPLEMENTED_FUNC(cellMic); + // TODO: Actually do things with the source variable + cellMic.todo("cellMicSetNotifyEventQueue2(key=0x%llx, source=0x%llx", key, source); + const auto micThread = fxm::get(); + if (!micThread) + return CELL_MIC_ERROR_NOT_INIT; + + // default mic queue size = 4 + auto micQueue = lv2_event_queue::find(key); + micQueue->send(0, CELL_MIC_ATTACH, 0, 0); + micThread->eventQueueKey = key; + return CELL_OK; } -s32 cellMicSetNotifyEventQueue2() +s32 cellMicRemoveNotifyEventQueue(u64 key) { - UNIMPLEMENTED_FUNC(cellMic); + cellMic.warning("cellMicRemoveNotifyEventQueue(key=0x%llx)", key); + const auto micThread = fxm::get(); + if (!micThread) + return CELL_MIC_ERROR_NOT_INIT; + + micThread->eventQueueKey = 0; + return CELL_OK; } -s32 cellMicRemoveNotifyEventQueue() +/// Reading Functions + +s32 cellMicRead(u32 deviceNumber, vm::ptr data, u32 maxBytes) { - UNIMPLEMENTED_FUNC(cellMic); - return CELL_OK; + cellMic.warning("cellMicRead(deviceNumber=%d, data=0x%x, maxBytes=0x%x)", deviceNumber, data, maxBytes); + const auto micThread = fxm::get(); + if (!micThread) + return CELL_MIC_ERROR_NOT_INIT; + + const s32 size = std::min(maxBytes, bufferSize); + + return size; } -s32 cellMicOpenEx() +s32 cellMicReadRaw(u32 deviceNumber, vm::ptr data, int maxBytes) { - UNIMPLEMENTED_FUNC(cellMic); - return CELL_OK; -} + cellMic.warning("cellMicReadRaw(deviceNumber=%d, data=0x%x, maxBytes=%d)", deviceNumber, data, maxBytes); + const auto micThread = fxm::get(); + if (!micThread) + return CELL_MIC_ERROR_NOT_INIT; -s32 cellMicStartEx() -{ - UNIMPLEMENTED_FUNC(cellMic); - return CELL_OK; -} + const s32 size = std::min(maxBytes, bufferSize); -s32 cellMicGetFormatRaw() -{ - UNIMPLEMENTED_FUNC(cellMic); - return CELL_OK; -} - -s32 cellMicGetFormatAux() -{ - UNIMPLEMENTED_FUNC(cellMic); - return CELL_OK; -} - -s32 cellMicGetFormatDsp() -{ - UNIMPLEMENTED_FUNC(cellMic); - return CELL_OK; -} - -s32 cellMicOpenRaw() -{ - UNIMPLEMENTED_FUNC(cellMic); - return CELL_OK; -} - -s32 cellMicReadRaw() -{ - UNIMPLEMENTED_FUNC(cellMic); - return CELL_OK; + return size; } s32 cellMicReadAux() @@ -182,6 +382,26 @@ s32 cellMicReadDsp() return CELL_OK; } +/// Unimplemented Functions + +s32 cellMicReset() +{ + UNIMPLEMENTED_FUNC(cellMic); + return CELL_OK; +} + +s32 cellMicGetDeviceGUID() +{ + UNIMPLEMENTED_FUNC(cellMic); + return CELL_OK; +} + +s32 cellMicGetType() +{ + UNIMPLEMENTED_FUNC(cellMic); + return CELL_OK; +} + s32 cellMicGetStatus() { UNIMPLEMENTED_FUNC(cellMic); @@ -259,8 +479,8 @@ s32 cellMicGetDeviceIdentifier() return CELL_OK; } -DECLARE(ppu_module_manager::cellMic)("cellMic", []() -{ +DECLARE(ppu_module_manager::cellMic) +("cellMic", []() { REG_FUNC(cellMic, cellMicInit); REG_FUNC(cellMic, cellMicEnd); REG_FUNC(cellMic, cellMicOpen); diff --git a/rpcs3/Emu/Cell/Modules/cellMic.h b/rpcs3/Emu/Cell/Modules/cellMic.h index 2a64e4d0f2..1390b0c356 100644 --- a/rpcs3/Emu/Cell/Modules/cellMic.h +++ b/rpcs3/Emu/Cell/Modules/cellMic.h @@ -1,25 +1,84 @@ #pragma once - +#include "Utilities/Thread.h" // Error Codes enum { - CELL_MICIN_ERROR_ALREADY_INIT = 0x80140101, - CELL_MICIN_ERROR_SYSTEM = 0x80140102, - CELL_MICIN_ERROR_NOT_INIT = 0x80140103, - CELL_MICIN_ERROR_PARAM = 0x80140104, - CELL_MICIN_ERROR_PORT_FULL = 0x80140105, - CELL_MICIN_ERROR_ALREADY_OPEN = 0x80140106, - CELL_MICIN_ERROR_NOT_OPEN = 0x80140107, - CELL_MICIN_ERROR_NOT_RUN = 0x80140108, - CELL_MICIN_ERROR_TRANS_EVENT = 0x80140109, - CELL_MICIN_ERROR_OPEN = 0x8014010a, - CELL_MICIN_ERROR_SHAREDMEMORY = 0x8014010b, - CELL_MICIN_ERROR_MUTEX = 0x8014010c, - CELL_MICIN_ERROR_EVENT_QUEUE = 0x8014010d, - CELL_MICIN_ERROR_DEVICE_NOT_FOUND = 0x8014010e, - CELL_MICIN_ERROR_SYSTEM_NOT_FOUND = 0x8014010e, - CELL_MICIN_ERROR_FATAL = 0x8014010f, - CELL_MICIN_ERROR_DEVICE_NOT_SUPPORT = 0x80140110, + CELL_MIC_ERROR_ALREADY_INIT = 0x80140101, + CELL_MIC_ERROR_SYSTEM = 0x80140102, + CELL_MIC_ERROR_NOT_INIT = 0x80140103, + CELL_MIC_ERROR_PARAM = 0x80140104, + CELL_MIC_ERROR_PORT_FULL = 0x80140105, + CELL_MIC_ERROR_ALREADY_OPEN = 0x80140106, + CELL_MIC_ERROR_NOT_OPEN = 0x80140107, + CELL_MIC_ERROR_NOT_RUN = 0x80140108, + CELL_MIC_ERROR_TRANS_EVENT = 0x80140109, + CELL_MIC_ERROR_OPEN = 0x8014010a, + CELL_MIC_ERROR_SHAREDMEMORY = 0x8014010b, + CELL_MIC_ERROR_MUTEX = 0x8014010c, + CELL_MIC_ERROR_EVENT_QUEUE = 0x8014010d, + CELL_MIC_ERROR_DEVICE_NOT_FOUND = 0x8014010e, + CELL_MIC_ERROR_SYSTEM_NOT_FOUND = 0x8014010e, + CELL_MIC_ERROR_FATAL = 0x8014010f, + CELL_MIC_ERROR_DEVICE_NOT_SUPPORT = 0x80140110, +}; + +struct CellMicInputFormat +{ + u8 channelNum; + u8 subframeSize; + u8 bitResolution; + u8 dataType; + be_t sampleRate; +}; + +enum CellMicSignalState +{ + CELL_MIC_SIGSTATE_LOCTALK = 0, + CELL_MIC_SIGSTATE_FARTALK = 1, + CELL_MIC_SIGSTATE_NSR = 3, + CELL_MIC_SIGSTATE_AGC = 4, + CELL_MIC_SIGSTATE_MICENG = 5, + CELL_MIC_SIGSTATE_SPKENG = 6, +}; + +enum CellMicCommand +{ + CELL_MIC_ATTACH = 2, + CELL_MIC_DATA = 5, +}; + +// TODO: generate this from input from an actual microphone +const u32 bufferSize = 1; + +bool micInited = false; + +class mic_thread final : public named_thread +{ +private: + void on_task() override; + std::string get_name() const override { return "Mic Thread"; } +public: + void on_init(const std::shared_ptr&) override; + // Default value of 48000 for no particular reason + u32 DspFrequency = 48000; // DSP is the default type + u32 rawFrequency = 48000; + u32 AuxFrequency = 48000; + u8 bitResolution = 32; + bool micOpened = false; + bool micStarted = false; + u64 eventQueueKey = 0; + + u32 signalStateLocalTalk = 9; // value is in range 0-10. 10 indicates talking, 0 indicating none. + u32 signalStateFarTalk = 0; // value is in range 0-10. 10 indicates talking from far away, 0 indicating none. + f32 signalStateNoiseSupression; // value is in decibels + f32 signalStateGainControl; + f32 signalStateMicSignalLevel; // value is in decibels + f32 signalStateSpeakerSignalLevel; // value is in decibels + mic_thread() = default; + ~mic_thread() + { + micInited = false; + } };