Я пытаюсь воспроизвести XAudio2 с Python, используя Ctypes и COM в библиотеке dll. У меня уже воспроизводится звук из файла, и он работает просто отлично. Однако у меня возникают проблемы при попытке реализовать некоторые из асинхронных режимов c, которые требуют обратных вызовов. В частности, интерфейс IXAudio2VoiceCallback
требует, чтобы вы передавали его CreateSourceVoice
, если вы хотите получать обратные вызовы, когда что-то срабатывает. Как только он попытается прочитать буфер, моя программа будет работать с sh, я подозреваю, потому что у меня нет этой правильной настройки (onBufferStart?).
Мои вопросы: возможно ли для Python принимать вызовы из библиотек на Python таким образом? Это что-то поддерживает COM и Ctypes?
Если это так, как мне сопоставить вызовы этих функций с функциями Python? Я видел несколько примеров определения типа обратного вызова и присвоения его через callback(function)
(см. Пример ниже, пытаясь заставить его напечатать что-то), но я не уверен, как это работает, если он находится в структуре.
Вот интерфейс, определенный в заголовочном файле:
#undef INTERFACE
#define INTERFACE IXAudio2VoiceCallback
DECLARE_INTERFACE(IXAudio2VoiceCallback)
{
// Called just before this voice's processing pass begins.
STDMETHOD_(void, OnVoiceProcessingPassStart) (THIS_ UINT32 BytesRequired) PURE;
// Called just after this voice's processing pass ends.
STDMETHOD_(void, OnVoiceProcessingPassEnd) (THIS) PURE;
// Called when this voice has just finished playing a buffer stream
// (as marked with the XAUDIO2_END_OF_STREAM flag on the last buffer).
STDMETHOD_(void, OnStreamEnd) (THIS) PURE;
// Called when this voice is about to start processing a new buffer.
STDMETHOD_(void, OnBufferStart) (THIS_ void* pBufferContext) PURE;
// Called when this voice has just finished processing a buffer.
// The buffer can now be reused or destroyed.
STDMETHOD_(void, OnBufferEnd) (THIS_ void* pBufferContext) PURE;
// Called when this voice has just reached the end position of a loop.
STDMETHOD_(void, OnLoopEnd) (THIS_ void* pBufferContext) PURE;
// Called in the event of a critical error during voice processing,
// such as a failing xAPO or an error from the hardware XMA decoder.
// The voice may have to be destroyed and re-created to recover from
// the error. The callback arguments report which buffer was being
// processed when the error occurred, and its HRESULT code.
STDMETHOD_(void, OnVoiceError) (THIS_ void* pBufferContext, HRESULT Error) PURE;
};
Эта реализация интерфейса COM, которая прекрасно работает для вызова библиотечных функций в Python. Однако, наоборот, мне нужно сделать.
Здесь:
class METHOD(object):
'''COM method.'''
def __init__(self, restype, *args):
self.restype = restype
self.argtypes = args
def get_field(self):
return ctypes.WINFUNCTYPE(self.restype, *self.argtypes)
class STDMETHOD(METHOD):
'''COM method with HRESULT return value.'''
def __init__(self, *args):
super(STDMETHOD, self).__init__(ctypes.HRESULT, *args)
class COMMethodInstance(object):
'''Binds a COM interface method.'''
def __init__(self, name, i, method):
self.name = name
self.i = i
self.method = method
def __get__(self, obj, tp):
if obj is not None:
def _call(*args):
ret = self.method.get_field()(self.i, self.name)(obj, *args)
return ret
return _call
raise AttributeError()
class COMInterface(ctypes.Structure):
'''Dummy struct to serve as the type of all COM pointers.'''
_fields_ = [
('lpVtbl', ctypes.c_void_p),
]
class InterfaceMetaclass(type(ctypes.POINTER(COMInterface))):
'''Creates COM interface pointers.'''
def __new__(cls, name, bases, dct):
methods = []
for base in bases[::-1]:
methods.extend(base.__dict__.get('_methods_', ()))
methods.extend(dct.get('_methods_', ()))
for i, (n, method) in enumerate(methods):
dct[n] = COMMethodInstance(n, i, method)
dct['_type_'] = COMInterface
return super(InterfaceMetaclass, cls).__new__(cls, name, bases, dct)
Interface = InterfaceMetaclass(str('Interface'), (ctypes.POINTER(COMInterface),), {
'__doc__': 'Base COM interface pointer.',
})
class IUnknown(Interface):
_methods_ = [
('QueryInterface', STDMETHOD(REFIID, ctypes.c_void_p)),
('AddRef', METHOD(ctypes.c_int)),
('Release', METHOD(ctypes.c_int))
]
Я пытался сделать что-то подобное, но, похоже, не работает:
def testfunc1(value):
print("testfunc1")
def testfunc2():
print("testfunc2")
# Call back types.
cb_buffer = ctypes.WINFUNCTYPE(None, POINTER(None)) # onBufferEnd,onBufferStart, onLoopEnd
cb_start = ctypes.WINFUNCTYPE(None, UINT32) # onVoiceProcessingPassStart
cb_end = ctypes.WINFUNCTYPE(None) # onStreamEnd, onVoiceProcessingPassEmd
cb_voice_error = ctypes.WINFUNCTYPE(None, POINTER(None), HRESULT)
class IXAudio2VoiceCallback(com.Interface):
_methods_ = [
('OnVoiceProcessingPassStart',
cb_start(testfunc1)),
('OnVoiceProcessingPassEnd',
cb_end(testfunc2)),
('onStreamEnd',
cb_end(testfunc2)),
('onBufferStart',
cb_buffer(testfunc1)),
('OnBufferEnd',
cb_buffer(testfunc1)),
('OnLoopEnd',
cb_buffer(testfunc1))
]
Любые предложения с благодарностью