mirror of
https://github.com/HIllya51/LunaTranslator.git
synced 2024-12-29 16:44:13 +08:00
record
This commit is contained in:
parent
4d2ac7bf10
commit
38f5cd864f
@ -3066,7 +3066,8 @@ def getalistname(parent, callback, skipid=False, skipidid=None):
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
else:
|
elif len(__uid):
|
||||||
|
|
||||||
callback(__uid[0])
|
callback(__uid[0])
|
||||||
|
|
||||||
|
|
||||||
|
@ -347,28 +347,8 @@ class AnkiWindow(QWidget):
|
|||||||
|
|
||||||
@threader
|
@threader
|
||||||
def simulate_key(self, i):
|
def simulate_key(self, i):
|
||||||
def __internal__keystring(i):
|
|
||||||
try:
|
try:
|
||||||
for _ in (0,):
|
keystring = globalconfig["ankiconnect"]["simulate_key"][i]["keystring"]
|
||||||
|
|
||||||
if not gobject.baseobject.textsource:
|
|
||||||
break
|
|
||||||
|
|
||||||
gameuid = gobject.baseobject.textsource.gameuid
|
|
||||||
if not gameuid:
|
|
||||||
break
|
|
||||||
if savehook_new_data[gameuid]["follow_default_ankisettings"]:
|
|
||||||
break
|
|
||||||
if not savehook_new_data[gameuid][f"anki_simulate_key_{i}_use"]:
|
|
||||||
return None
|
|
||||||
return savehook_new_data[gameuid][
|
|
||||||
f"anki_simulate_key_{i}_keystring"
|
|
||||||
]
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
return globalconfig["ankiconnect"]["simulate_key"][i]["keystring"]
|
|
||||||
try:
|
|
||||||
keystring = __internal__keystring(i)
|
|
||||||
except:
|
except:
|
||||||
return
|
return
|
||||||
if not keystring:
|
if not keystring:
|
||||||
@ -387,10 +367,10 @@ class AnkiWindow(QWidget):
|
|||||||
for mode in modes:
|
for mode in modes:
|
||||||
windows.keybd_event(mode, 0, windows.KEYEVENTF_KEYUP, 0)
|
windows.keybd_event(mode, 0, windows.KEYEVENTF_KEYUP, 0)
|
||||||
|
|
||||||
def startorendrecord(self, target: QLineEdit, idx):
|
def startorendrecord(self, ii, target: QLineEdit, idx):
|
||||||
if idx == 1:
|
if idx == 1:
|
||||||
self.recorder = loopbackrecorder()
|
self.recorder = loopbackrecorder()
|
||||||
self.simulate_key(idx)
|
self.simulate_key(ii)
|
||||||
else:
|
else:
|
||||||
self.recorder.end(callback=target.setText)
|
self.recorder.end(callback=target.setText)
|
||||||
|
|
||||||
@ -428,11 +408,11 @@ class AnkiWindow(QWidget):
|
|||||||
self.remarks = FQPlainTextEdit()
|
self.remarks = FQPlainTextEdit()
|
||||||
recordbtn1 = statusbutton(icons=["fa.microphone", "fa.stop"], colors=["", ""])
|
recordbtn1 = statusbutton(icons=["fa.microphone", "fa.stop"], colors=["", ""])
|
||||||
recordbtn1.statuschanged.connect(
|
recordbtn1.statuschanged.connect(
|
||||||
functools.partial(self.startorendrecord, self.audiopath)
|
functools.partial(self.startorendrecord, 1, self.audiopath)
|
||||||
)
|
)
|
||||||
recordbtn2 = statusbutton(icons=["fa.microphone", "fa.stop"], colors=["", ""])
|
recordbtn2 = statusbutton(icons=["fa.microphone", "fa.stop"], colors=["", ""])
|
||||||
recordbtn2.statuschanged.connect(
|
recordbtn2.statuschanged.connect(
|
||||||
functools.partial(self.startorendrecord, self.audiopath_sentence)
|
functools.partial(self.startorendrecord, 2, self.audiopath_sentence)
|
||||||
)
|
)
|
||||||
self.recordbtn1 = recordbtn1
|
self.recordbtn1 = recordbtn1
|
||||||
self.recordbtn2 = recordbtn2
|
self.recordbtn2 = recordbtn2
|
||||||
|
@ -5,6 +5,8 @@ import socket, gobject, uuid, subprocess, functools
|
|||||||
import ctypes, importlib, json
|
import ctypes, importlib, json
|
||||||
import ctypes.wintypes
|
import ctypes.wintypes
|
||||||
from qtsymbols import *
|
from qtsymbols import *
|
||||||
|
from ctypes import CDLL, c_void_p, CFUNCTYPE, c_size_t, cast, c_char, POINTER
|
||||||
|
from ctypes.wintypes import HANDLE
|
||||||
from traceback import print_exc
|
from traceback import print_exc
|
||||||
from myutils.config import (
|
from myutils.config import (
|
||||||
globalconfig,
|
globalconfig,
|
||||||
@ -759,36 +761,65 @@ def checkmd5reloadmodule(filename, module):
|
|||||||
return False, globalcachedmodule.get(key, {}).get("module", None)
|
return False, globalcachedmodule.get(key, {}).get("module", None)
|
||||||
|
|
||||||
|
|
||||||
|
class audiocapture:
|
||||||
|
def __datacollect(self, ptr, size):
|
||||||
|
self.data = cast(ptr, POINTER(c_char))[:size]
|
||||||
|
self.stoped.release()
|
||||||
|
|
||||||
|
def __mutexcb(self, mutex):
|
||||||
|
self.mutex = mutex
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
_ = self.mutex
|
||||||
|
if _:
|
||||||
|
self.mutex = None
|
||||||
|
self.StopCaptureAsync(_)
|
||||||
|
self.stoped.acquire()
|
||||||
|
return self.data
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.stop()
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
|
||||||
|
loopbackaudio = CDLL(gobject.GetDllpath("loopbackaudio.dll"))
|
||||||
|
StartCaptureAsync = loopbackaudio.StartCaptureAsync
|
||||||
|
StartCaptureAsync.argtypes = c_void_p, c_void_p
|
||||||
|
StartCaptureAsync.restype = HANDLE
|
||||||
|
StopCaptureAsync = loopbackaudio.StopCaptureAsync
|
||||||
|
StopCaptureAsync.argtypes = (HANDLE,)
|
||||||
|
self.StopCaptureAsync = StopCaptureAsync
|
||||||
|
self.mutex = None
|
||||||
|
self.stoped = threading.Lock()
|
||||||
|
self.stoped.acquire()
|
||||||
|
self.data = None
|
||||||
|
self.cb1 = CFUNCTYPE(None, c_void_p, c_size_t)(self.__datacollect)
|
||||||
|
self.cb2 = CFUNCTYPE(None, c_void_p)(self.__mutexcb)
|
||||||
|
threading.Thread(target=StartCaptureAsync, args=(self.cb1, self.cb2)).start()
|
||||||
|
|
||||||
|
|
||||||
class loopbackrecorder:
|
class loopbackrecorder:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.file = gobject.gettempdir(str(time.time()) + ".wav")
|
|
||||||
try:
|
try:
|
||||||
self.waitsignal = str(time.time())
|
self.capture = audiocapture()
|
||||||
cmd = './files/plugins/loopbackaudio.exe "{}" "{}"'.format(
|
|
||||||
self.file, self.waitsignal
|
|
||||||
)
|
|
||||||
self.engine = subproc_w(cmd, name=str(uuid.uuid4()))
|
|
||||||
except:
|
except:
|
||||||
print_exc()
|
self.capture = None
|
||||||
|
|
||||||
@threader
|
@threader
|
||||||
def end(self, callback):
|
def end(self, callback):
|
||||||
windows.SetEvent(
|
if not self.capture:
|
||||||
windows.AutoHandle(windows.CreateEvent(False, False, self.waitsignal))
|
return callback("")
|
||||||
)
|
wav = self.capture.stop()
|
||||||
self.engine.wait()
|
if not wav:
|
||||||
filewav = self.file
|
return callback("")
|
||||||
if os.path.exists(filewav) == False:
|
|
||||||
callback("")
|
|
||||||
return
|
|
||||||
with open(filewav, "rb") as ff:
|
|
||||||
wav = ff.read()
|
|
||||||
mp3 = winsharedutils.encodemp3(wav)
|
mp3 = winsharedutils.encodemp3(wav)
|
||||||
if mp3:
|
if not mp3:
|
||||||
filemp3 = filewav[:-3] + "mp3"
|
file = gobject.gettempdir(str(time.time()) + ".wav")
|
||||||
with open(filemp3, "wb") as ff:
|
with open(file, "wb") as ff:
|
||||||
ff.write(mp3)
|
ff.write(wav)
|
||||||
os.remove(filewav)
|
callback(file)
|
||||||
callback(filemp3)
|
|
||||||
else:
|
else:
|
||||||
callback(filewav)
|
file = gobject.gettempdir(str(time.time()) + ".mp3")
|
||||||
|
with open(file, "wb") as ff:
|
||||||
|
ff.write(mp3)
|
||||||
|
callback(file)
|
||||||
|
@ -28,8 +28,8 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/version)
|
|||||||
include(generate_product_version)
|
include(generate_product_version)
|
||||||
|
|
||||||
set(VERSION_MAJOR 5)
|
set(VERSION_MAJOR 5)
|
||||||
set(VERSION_MINOR 25)
|
set(VERSION_MINOR 26)
|
||||||
set(VERSION_PATCH 1)
|
set(VERSION_PATCH 0)
|
||||||
|
|
||||||
add_library(pch pch.cpp)
|
add_library(pch pch.cpp)
|
||||||
target_precompile_headers(pch PUBLIC pch.h)
|
target_precompile_headers(pch PUBLIC pch.h)
|
||||||
|
@ -12,11 +12,6 @@ generate_product_version(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
if(${CMAKE_SIZEOF_VOID_P} EQUAL 8)
|
add_library(loopbackaudio MODULE runer.cpp LoopbackCapture.cpp ${versioninfo})
|
||||||
|
|
||||||
else()
|
|
||||||
|
|
||||||
add_executable(loopbackaudio runer.cpp LoopbackCapture.cpp ${versioninfo})
|
|
||||||
target_precompile_headers(loopbackaudio REUSE_FROM pch)
|
target_precompile_headers(loopbackaudio REUSE_FROM pch)
|
||||||
target_link_libraries(loopbackaudio Mfplat mfuuid )
|
target_link_libraries(loopbackaudio Mfplat mfuuid )
|
||||||
endif()
|
|
||||||
|
@ -47,13 +47,12 @@ CLoopbackCapture::~CLoopbackCapture()
|
|||||||
MFUnlockWorkQueue(m_dwQueueID);
|
MFUnlockWorkQueue(m_dwQueueID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
typedef HRESULT (STDAPICALLTYPE *ActivateAudioInterfaceAsync_t)(
|
typedef HRESULT(STDAPICALLTYPE *ActivateAudioInterfaceAsync_t)(
|
||||||
_In_ LPCWSTR deviceInterfacePath,
|
_In_ LPCWSTR deviceInterfacePath,
|
||||||
_In_ REFIID riid,
|
_In_ REFIID riid,
|
||||||
_In_opt_ PROPVARIANT *activationParams,
|
_In_opt_ PROPVARIANT *activationParams,
|
||||||
_In_ IActivateAudioInterfaceCompletionHandler *completionHandler,
|
_In_ IActivateAudioInterfaceCompletionHandler *completionHandler,
|
||||||
_COM_Outptr_ IActivateAudioInterfaceAsyncOperation **activationOperation
|
_COM_Outptr_ IActivateAudioInterfaceAsyncOperation **activationOperation);
|
||||||
);
|
|
||||||
HRESULT CLoopbackCapture::ActivateAudioInterface(DWORD processId, bool includeProcessTree)
|
HRESULT CLoopbackCapture::ActivateAudioInterface(DWORD processId, bool includeProcessTree)
|
||||||
{
|
{
|
||||||
return SetDeviceStateErrorIfFailed([&]() -> HRESULT
|
return SetDeviceStateErrorIfFailed([&]() -> HRESULT
|
||||||
@ -77,8 +76,7 @@ HRESULT CLoopbackCapture::ActivateAudioInterface(DWORD processId, bool includePr
|
|||||||
// Wait for activation completion
|
// Wait for activation completion
|
||||||
m_hActivateCompleted.wait();
|
m_hActivateCompleted.wait();
|
||||||
|
|
||||||
return m_activateResult;
|
return m_activateResult; }());
|
||||||
}());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -87,9 +85,9 @@ HRESULT CLoopbackCapture::ActivateAudioInterface(DWORD processId, bool includePr
|
|||||||
// Callback implementation of ActivateAudioInterfaceAsync function. This will be called on MTA thread
|
// Callback implementation of ActivateAudioInterfaceAsync function. This will be called on MTA thread
|
||||||
// when results of the activation are available.
|
// when results of the activation are available.
|
||||||
//
|
//
|
||||||
HRESULT CLoopbackCapture::ActivateCompleted(IActivateAudioInterfaceAsyncOperation* operation)
|
HRESULT CLoopbackCapture::ActivateCompleted(IActivateAudioInterfaceAsyncOperation *operation)
|
||||||
{
|
{
|
||||||
m_activateResult = SetDeviceStateErrorIfFailed([&]()->HRESULT
|
m_activateResult = SetDeviceStateErrorIfFailed([&]() -> HRESULT
|
||||||
{
|
{
|
||||||
// Check for a successful activation result
|
// Check for a successful activation result
|
||||||
HRESULT hrActivateResult = E_UNEXPECTED;
|
HRESULT hrActivateResult = E_UNEXPECTED;
|
||||||
@ -135,8 +133,7 @@ HRESULT CLoopbackCapture::ActivateCompleted(IActivateAudioInterfaceAsyncOperatio
|
|||||||
// Everything is ready.
|
// Everything is ready.
|
||||||
m_DeviceState = DeviceState::Initialized;
|
m_DeviceState = DeviceState::Initialized;
|
||||||
|
|
||||||
return S_OK;
|
return S_OK; }());
|
||||||
}());
|
|
||||||
|
|
||||||
// Let ActivateAudioInterface know that m_activateResult has the result of the activation attempt.
|
// Let ActivateAudioInterface know that m_activateResult has the result of the activation attempt.
|
||||||
m_hActivateCompleted.SetEvent();
|
m_hActivateCompleted.SetEvent();
|
||||||
@ -150,10 +147,8 @@ HRESULT CLoopbackCapture::ActivateCompleted(IActivateAudioInterfaceAsyncOperatio
|
|||||||
//
|
//
|
||||||
HRESULT CLoopbackCapture::CreateWAVFile()
|
HRESULT CLoopbackCapture::CreateWAVFile()
|
||||||
{
|
{
|
||||||
return SetDeviceStateErrorIfFailed([&]()->HRESULT
|
return SetDeviceStateErrorIfFailed([&]() -> HRESULT
|
||||||
{
|
{
|
||||||
m_hFile.reset(CreateFile(m_outputFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL));
|
|
||||||
RETURN_LAST_ERROR_IF(!m_hFile);
|
|
||||||
|
|
||||||
// Create and write the WAV header
|
// Create and write the WAV header
|
||||||
|
|
||||||
@ -166,25 +161,23 @@ HRESULT CLoopbackCapture::CreateWAVFile()
|
|||||||
sizeof(m_CaptureFormat) // Size of fmt chunk
|
sizeof(m_CaptureFormat) // Size of fmt chunk
|
||||||
};
|
};
|
||||||
DWORD dwBytesWritten = 0;
|
DWORD dwBytesWritten = 0;
|
||||||
RETURN_IF_WIN32_BOOL_FALSE(WriteFile(m_hFile.get(), header, sizeof(header), &dwBytesWritten, NULL));
|
std::lock_guard _(bufferlock);
|
||||||
|
buffer+=std::string((char*)header, sizeof(header));
|
||||||
m_cbHeaderSize += dwBytesWritten;
|
m_cbHeaderSize += sizeof(header);
|
||||||
|
|
||||||
// 2. The fmt sub-chunk
|
// 2. The fmt sub-chunk
|
||||||
WI_ASSERT(m_CaptureFormat.cbSize == 0);
|
WI_ASSERT(m_CaptureFormat.cbSize == 0);
|
||||||
RETURN_IF_WIN32_BOOL_FALSE(WriteFile(m_hFile.get(), &m_CaptureFormat, sizeof(m_CaptureFormat), &dwBytesWritten, NULL));
|
buffer+=std::string((char*) &m_CaptureFormat, sizeof(m_CaptureFormat));
|
||||||
m_cbHeaderSize += dwBytesWritten;
|
m_cbHeaderSize += sizeof(m_CaptureFormat);
|
||||||
|
|
||||||
// 3. The data sub-chunk
|
// 3. The data sub-chunk
|
||||||
DWORD data[] = { FCC('data'), 0 }; // Start of 'data' chunk
|
DWORD data[] = { FCC('data'), 0 }; // Start of 'data' chunk
|
||||||
RETURN_IF_WIN32_BOOL_FALSE(WriteFile(m_hFile.get(), data, sizeof(data), &dwBytesWritten, NULL));
|
buffer+=std::string((char*) data, sizeof(data));
|
||||||
m_cbHeaderSize += dwBytesWritten;
|
m_cbHeaderSize += sizeof(data);
|
||||||
|
|
||||||
return S_OK;
|
return S_OK; }());
|
||||||
}());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// FixWAVHeader()
|
// FixWAVHeader()
|
||||||
//
|
//
|
||||||
@ -192,29 +185,24 @@ HRESULT CLoopbackCapture::CreateWAVFile()
|
|||||||
//
|
//
|
||||||
HRESULT CLoopbackCapture::FixWAVHeader()
|
HRESULT CLoopbackCapture::FixWAVHeader()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
std::lock_guard _(bufferlock);
|
||||||
// Write the size of the 'data' chunk first
|
// Write the size of the 'data' chunk first
|
||||||
DWORD dwPtr = SetFilePointer(m_hFile.get(), m_cbHeaderSize - sizeof(DWORD), NULL, FILE_BEGIN);
|
auto offset = m_cbHeaderSize - sizeof(DWORD);
|
||||||
RETURN_LAST_ERROR_IF(INVALID_SET_FILE_POINTER == dwPtr);
|
memcpy(buffer.data() + offset, &m_cbDataSize, sizeof(DWORD));
|
||||||
|
|
||||||
DWORD dwBytesWritten = 0;
|
|
||||||
RETURN_IF_WIN32_BOOL_FALSE(WriteFile(m_hFile.get(), &m_cbDataSize, sizeof(DWORD), &dwBytesWritten, NULL));
|
|
||||||
|
|
||||||
// Write the total file size, minus RIFF chunk and size
|
// Write the total file size, minus RIFF chunk and size
|
||||||
// sizeof(DWORD) == sizeof(FOURCC)
|
// sizeof(DWORD) == sizeof(FOURCC)
|
||||||
RETURN_LAST_ERROR_IF(INVALID_SET_FILE_POINTER == SetFilePointer(m_hFile.get(), sizeof(DWORD), NULL, FILE_BEGIN));
|
|
||||||
|
|
||||||
DWORD cbTotalSize = m_cbDataSize + m_cbHeaderSize - 8;
|
DWORD cbTotalSize = m_cbDataSize + m_cbHeaderSize - 8;
|
||||||
RETURN_IF_WIN32_BOOL_FALSE(WriteFile(m_hFile.get(), &cbTotalSize, sizeof(DWORD), &dwBytesWritten, NULL));
|
|
||||||
|
|
||||||
RETURN_IF_WIN32_BOOL_FALSE(FlushFileBuffers(m_hFile.get()));
|
offset = sizeof(DWORD);
|
||||||
|
memcpy(buffer.data() + offset, &cbTotalSize, sizeof(DWORD));
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT CLoopbackCapture::StartCaptureAsync(DWORD processId, bool includeProcessTree, PCWSTR outputFileName)
|
HRESULT CLoopbackCapture::StartCaptureAsync(DWORD processId, bool includeProcessTree)
|
||||||
{
|
{
|
||||||
m_outputFileName = outputFileName;
|
|
||||||
auto resetOutputFileName = wil::scope_exit([&] { m_outputFileName = nullptr; });
|
|
||||||
|
|
||||||
RETURN_IF_FAILED(InitializeLoopbackCapture());
|
RETURN_IF_FAILED(InitializeLoopbackCapture());
|
||||||
RETURN_IF_FAILED(ActivateAudioInterface(processId, includeProcessTree));
|
RETURN_IF_FAILED(ActivateAudioInterface(processId, includeProcessTree));
|
||||||
@ -234,9 +222,9 @@ HRESULT CLoopbackCapture::StartCaptureAsync(DWORD processId, bool includeProcess
|
|||||||
//
|
//
|
||||||
// Callback method to start capture
|
// Callback method to start capture
|
||||||
//
|
//
|
||||||
HRESULT CLoopbackCapture::OnStartCapture(IMFAsyncResult* pResult)
|
HRESULT CLoopbackCapture::OnStartCapture(IMFAsyncResult *pResult)
|
||||||
{
|
{
|
||||||
return SetDeviceStateErrorIfFailed([&]()->HRESULT
|
return SetDeviceStateErrorIfFailed([&]() -> HRESULT
|
||||||
{
|
{
|
||||||
// Start the capture
|
// Start the capture
|
||||||
RETURN_IF_FAILED(m_AudioClient->Start());
|
RETURN_IF_FAILED(m_AudioClient->Start());
|
||||||
@ -244,11 +232,9 @@ HRESULT CLoopbackCapture::OnStartCapture(IMFAsyncResult* pResult)
|
|||||||
m_DeviceState = DeviceState::Capturing;
|
m_DeviceState = DeviceState::Capturing;
|
||||||
MFPutWaitingWorkItem(m_SampleReadyEvent.get(), 0, m_SampleReadyAsyncResult.get(), &m_SampleReadyKey);
|
MFPutWaitingWorkItem(m_SampleReadyEvent.get(), 0, m_SampleReadyAsyncResult.get(), &m_SampleReadyKey);
|
||||||
|
|
||||||
return S_OK;
|
return S_OK; }());
|
||||||
}());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// StopCaptureAsync()
|
// StopCaptureAsync()
|
||||||
//
|
//
|
||||||
@ -274,7 +260,7 @@ HRESULT CLoopbackCapture::StopCaptureAsync()
|
|||||||
//
|
//
|
||||||
// Callback method to stop capture
|
// Callback method to stop capture
|
||||||
//
|
//
|
||||||
HRESULT CLoopbackCapture::OnStopCapture(IMFAsyncResult* pResult)
|
HRESULT CLoopbackCapture::OnStopCapture(IMFAsyncResult *pResult)
|
||||||
{
|
{
|
||||||
// Stop capture by cancelling Work Item
|
// Stop capture by cancelling Work Item
|
||||||
// Cancel the queued work item (if any)
|
// Cancel the queued work item (if any)
|
||||||
@ -307,7 +293,7 @@ HRESULT CLoopbackCapture::FinishCaptureAsync()
|
|||||||
// Because of the asynchronous nature of the MF Work Queues and the DataWriter, there could still be
|
// Because of the asynchronous nature of the MF Work Queues and the DataWriter, there could still be
|
||||||
// a sample processing. So this will get called to finalize the WAV header.
|
// a sample processing. So this will get called to finalize the WAV header.
|
||||||
//
|
//
|
||||||
HRESULT CLoopbackCapture::OnFinishCapture(IMFAsyncResult* pResult)
|
HRESULT CLoopbackCapture::OnFinishCapture(IMFAsyncResult *pResult)
|
||||||
{
|
{
|
||||||
// FixWAVHeader will set the DeviceStateStopped when all async tasks are complete
|
// FixWAVHeader will set the DeviceStateStopped when all async tasks are complete
|
||||||
HRESULT hr = FixWAVHeader();
|
HRESULT hr = FixWAVHeader();
|
||||||
@ -324,7 +310,7 @@ HRESULT CLoopbackCapture::OnFinishCapture(IMFAsyncResult* pResult)
|
|||||||
//
|
//
|
||||||
// Callback method when ready to fill sample buffer
|
// Callback method when ready to fill sample buffer
|
||||||
//
|
//
|
||||||
HRESULT CLoopbackCapture::OnSampleReady(IMFAsyncResult* pResult)
|
HRESULT CLoopbackCapture::OnSampleReady(IMFAsyncResult *pResult)
|
||||||
{
|
{
|
||||||
if (SUCCEEDED(OnAudioSampleRequested()))
|
if (SUCCEEDED(OnAudioSampleRequested()))
|
||||||
{
|
{
|
||||||
@ -351,7 +337,7 @@ HRESULT CLoopbackCapture::OnSampleReady(IMFAsyncResult* pResult)
|
|||||||
HRESULT CLoopbackCapture::OnAudioSampleRequested()
|
HRESULT CLoopbackCapture::OnAudioSampleRequested()
|
||||||
{
|
{
|
||||||
UINT32 FramesAvailable = 0;
|
UINT32 FramesAvailable = 0;
|
||||||
BYTE* Data = nullptr;
|
BYTE *Data = nullptr;
|
||||||
DWORD dwCaptureFlags;
|
DWORD dwCaptureFlags;
|
||||||
UINT64 u64DevicePosition = 0;
|
UINT64 u64DevicePosition = 0;
|
||||||
UINT64 u64QPCPosition = 0;
|
UINT64 u64QPCPosition = 0;
|
||||||
@ -401,17 +387,11 @@ HRESULT CLoopbackCapture::OnAudioSampleRequested()
|
|||||||
// Get sample buffer
|
// Get sample buffer
|
||||||
RETURN_IF_FAILED(m_AudioCaptureClient->GetBuffer(&Data, &FramesAvailable, &dwCaptureFlags, &u64DevicePosition, &u64QPCPosition));
|
RETURN_IF_FAILED(m_AudioCaptureClient->GetBuffer(&Data, &FramesAvailable, &dwCaptureFlags, &u64DevicePosition, &u64QPCPosition));
|
||||||
|
|
||||||
|
|
||||||
// Write File
|
// Write File
|
||||||
if (m_DeviceState != DeviceState::Stopping)
|
if (m_DeviceState != DeviceState::Stopping)
|
||||||
{
|
{
|
||||||
DWORD dwBytesWritten = 0;
|
std::lock_guard _(bufferlock);
|
||||||
RETURN_IF_WIN32_BOOL_FALSE(WriteFile(
|
buffer += std::string((char *)Data, cbBytesToCapture);
|
||||||
m_hFile.get(),
|
|
||||||
Data,
|
|
||||||
cbBytesToCapture,
|
|
||||||
&dwBytesWritten,
|
|
||||||
NULL));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release buffer back
|
// Release buffer back
|
||||||
|
@ -21,7 +21,7 @@ public:
|
|||||||
//CLoopbackCapture() = default;
|
//CLoopbackCapture() = default;
|
||||||
~CLoopbackCapture();
|
~CLoopbackCapture();
|
||||||
|
|
||||||
HRESULT StartCaptureAsync(DWORD processId, bool includeProcessTree, PCWSTR outputFileName);
|
HRESULT StartCaptureAsync(DWORD processId, bool includeProcessTree);
|
||||||
HRESULT StopCaptureAsync();
|
HRESULT StopCaptureAsync();
|
||||||
|
|
||||||
METHODASYNCCALLBACK(CLoopbackCapture, StartCapture, OnStartCapture);
|
METHODASYNCCALLBACK(CLoopbackCapture, StartCapture, OnStartCapture);
|
||||||
@ -32,6 +32,7 @@ public:
|
|||||||
// IActivateAudioInterfaceCompletionHandler
|
// IActivateAudioInterfaceCompletionHandler
|
||||||
STDMETHOD(ActivateCompleted)(IActivateAudioInterfaceAsyncOperation* operation);
|
STDMETHOD(ActivateCompleted)(IActivateAudioInterfaceAsyncOperation* operation);
|
||||||
|
|
||||||
|
std::string buffer;
|
||||||
private:
|
private:
|
||||||
// NB: All states >= Initialized will allow some methods
|
// NB: All states >= Initialized will allow some methods
|
||||||
// to be called successfully on the Audio Client
|
// to be called successfully on the Audio Client
|
||||||
@ -69,15 +70,13 @@ private:
|
|||||||
|
|
||||||
wil::unique_event_nothrow m_SampleReadyEvent;
|
wil::unique_event_nothrow m_SampleReadyEvent;
|
||||||
MFWORKITEM_KEY m_SampleReadyKey = 0;
|
MFWORKITEM_KEY m_SampleReadyKey = 0;
|
||||||
wil::unique_hfile m_hFile;
|
|
||||||
wil::critical_section m_CritSec;
|
wil::critical_section m_CritSec;
|
||||||
DWORD m_dwQueueID = 0;
|
DWORD m_dwQueueID = 0;
|
||||||
DWORD m_cbHeaderSize = 0;
|
DWORD m_cbHeaderSize = 0;
|
||||||
DWORD m_cbDataSize = 0;
|
DWORD m_cbDataSize = 0;
|
||||||
|
std::mutex bufferlock;
|
||||||
// These two members are used to communicate between the main thread
|
// These two members are used to communicate between the main thread
|
||||||
// and the ActivateCompleted callback.
|
// and the ActivateCompleted callback.
|
||||||
PCWSTR m_outputFileName = nullptr;
|
|
||||||
HRESULT m_activateResult = E_UNEXPECTED;
|
HRESULT m_activateResult = E_UNEXPECTED;
|
||||||
|
|
||||||
DeviceState m_DeviceState{ DeviceState::Uninitialized };
|
DeviceState m_DeviceState{ DeviceState::Uninitialized };
|
||||||
|
@ -1,12 +1,20 @@
|
|||||||
|
|
||||||
#include "LoopbackCapture.h"
|
#include "LoopbackCapture.h"
|
||||||
int wmain(int argc, wchar_t *argv[])
|
#define DECLARE extern "C" __declspec(dllexport)
|
||||||
|
|
||||||
|
DECLARE void StartCaptureAsync(void (*datacb)(void *ptr, size_t size), void (*handlecb)(HANDLE))
|
||||||
{
|
{
|
||||||
|
auto mutex = CreateSemaphoreW(NULL, 0, 1, NULL);
|
||||||
|
handlecb(mutex);
|
||||||
CLoopbackCapture loopbackCapture;
|
CLoopbackCapture loopbackCapture;
|
||||||
loopbackCapture.StartCaptureAsync(GetCurrentProcessId(), false, argv[1]);
|
loopbackCapture.StartCaptureAsync(GetCurrentProcessId(), false);
|
||||||
WaitForSingleObject(
|
WaitForSingleObject(mutex, INFINITE);
|
||||||
CreateEventW(&allAccess, FALSE, FALSE, argv[2]),
|
CloseHandle(mutex);
|
||||||
INFINITE);
|
|
||||||
loopbackCapture.StopCaptureAsync();
|
loopbackCapture.StopCaptureAsync();
|
||||||
return 0;
|
datacb(loopbackCapture.buffer.data(), loopbackCapture.buffer.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
DECLARE void StopCaptureAsync(HANDLE m)
|
||||||
|
{
|
||||||
|
ReleaseSemaphore(m, 1, NULL);
|
||||||
}
|
}
|
@ -2,12 +2,13 @@ import shutil,sys
|
|||||||
x86=int(sys.argv[1])
|
x86=int(sys.argv[1])
|
||||||
if x86:
|
if x86:
|
||||||
shutil.copy('../builds/_x86/shareddllproxy32.exe','../../LunaTranslator/files/plugins')
|
shutil.copy('../builds/_x86/shareddllproxy32.exe','../../LunaTranslator/files/plugins')
|
||||||
shutil.copy('../builds/_x86/loopbackaudio.exe','../../LunaTranslator/files/plugins')
|
shutil.copy('../builds/_x86/loopbackaudio.dll','../../LunaTranslator/files/plugins/DLL32')
|
||||||
shutil.copy('../builds/_x86/winrtutils32.dll','../../LunaTranslator/files/plugins/DLL32')
|
shutil.copy('../builds/_x86/winrtutils32.dll','../../LunaTranslator/files/plugins/DLL32')
|
||||||
shutil.copy('../builds/_x86/winsharedutils32.dll','../../LunaTranslator/files/plugins/DLL32')
|
shutil.copy('../builds/_x86/winsharedutils32.dll','../../LunaTranslator/files/plugins/DLL32')
|
||||||
shutil.copy('../builds/_x86/wcocr.dll','../../LunaTranslator/files/plugins/DLL32')
|
shutil.copy('../builds/_x86/wcocr.dll','../../LunaTranslator/files/plugins/DLL32')
|
||||||
else:
|
else:
|
||||||
shutil.copy('../builds/_x64/shareddllproxy64.exe','../../LunaTranslator/files/plugins')
|
shutil.copy('../builds/_x64/shareddllproxy64.exe','../../LunaTranslator/files/plugins')
|
||||||
|
shutil.copy('../builds/_x64/loopbackaudio.dll','../../LunaTranslator/files/plugins/DLL64')
|
||||||
shutil.copy('../builds/_x64/hookmagpie.dll','../../LunaTranslator/files/plugins')
|
shutil.copy('../builds/_x64/hookmagpie.dll','../../LunaTranslator/files/plugins')
|
||||||
shutil.copy('../builds/_x64/winrtutils64.dll','../../LunaTranslator/files/plugins/DLL64')
|
shutil.copy('../builds/_x64/winrtutils64.dll','../../LunaTranslator/files/plugins/DLL64')
|
||||||
shutil.copy('../builds/_x64/winsharedutils64.dll','../../LunaTranslator/files/plugins/DLL64')
|
shutil.copy('../builds/_x64/winsharedutils64.dll','../../LunaTranslator/files/plugins/DLL64')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user