mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Get listing all available voices working
This commit is contained in:
parent
b1d7a72494
commit
d1b1fa7209
@ -189,8 +189,8 @@
|
|||||||
"only": "windows",
|
"only": "windows",
|
||||||
"headers": "calibre/utils/cpp_binding.h calibre/utils/windows/common.h",
|
"headers": "calibre/utils/cpp_binding.h calibre/utils/windows/common.h",
|
||||||
"sources": "calibre/utils/windows/winspeech.cpp",
|
"sources": "calibre/utils/windows/winspeech.cpp",
|
||||||
"libraries": "shlwapi runtimeobject",
|
"libraries": "WindowsApp",
|
||||||
"cflags": "/X"
|
"cflags": "/X /std:c++17 /ZW /bigobj /await /permissive- /WX /Zc:twoPhase-"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "wpd",
|
"name": "wpd",
|
||||||
|
@ -24,8 +24,42 @@ set_error_from_hresult(PyObject *exc_type, const char *file, const int line, con
|
|||||||
}
|
}
|
||||||
#define error_from_hresult(hr, ...) set_error_from_hresult(PyExc_OSError, __FILE__, __LINE__, hr, __VA_ARGS__)
|
#define error_from_hresult(hr, ...) set_error_from_hresult(PyExc_OSError, __FILE__, __LINE__, hr, __VA_ARGS__)
|
||||||
|
|
||||||
|
class scoped_com_initializer { // {{{
|
||||||
|
public:
|
||||||
|
scoped_com_initializer() : m_succeded(false), hr(0) {
|
||||||
|
hr = CoInitialize(NULL);
|
||||||
|
if (SUCCEEDED(hr)) m_succeded = true;
|
||||||
|
}
|
||||||
|
~scoped_com_initializer() { if (succeeded()) CoUninitialize(); }
|
||||||
|
|
||||||
|
explicit operator bool() const noexcept { return m_succeded; }
|
||||||
|
|
||||||
|
bool succeeded() const noexcept { return m_succeded; }
|
||||||
|
|
||||||
|
PyObject* set_python_error() const noexcept {
|
||||||
|
if (hr == RPC_E_CHANGED_MODE) {
|
||||||
|
PyErr_SetString(PyExc_OSError, "COM initialization failed as it was already initialized in multi-threaded mode");
|
||||||
|
} else {
|
||||||
|
_com_error err(hr);
|
||||||
|
PyObject *pmsg = PyUnicode_FromWideChar(err.ErrorMessage(), -1);
|
||||||
|
PyErr_Format(PyExc_OSError, "COM initialization failed: %V", pmsg, "Out of memory");
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void detach() noexcept { m_succeded = false; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_succeded;
|
||||||
|
HRESULT hr;
|
||||||
|
scoped_com_initializer( const scoped_com_initializer & ) ;
|
||||||
|
scoped_com_initializer & operator=( const scoped_com_initializer & ) ;
|
||||||
|
}; // }}}
|
||||||
|
|
||||||
|
#define INITIALIZE_COM_IN_FUNCTION scoped_com_initializer com; if (!com) return com.set_python_error();
|
||||||
|
|
||||||
static inline void co_task_mem_free(void* m) { CoTaskMemFree(m); }
|
static inline void co_task_mem_free(void* m) { CoTaskMemFree(m); }
|
||||||
typedef generic_raii<wchar_t*, co_task_mem_free, NULL> com_wchar_raii;
|
typedef generic_raii<wchar_t*, co_task_mem_free, static_cast<wchar_t*>(NULL)> com_wchar_raii;
|
||||||
static inline void handle_destructor(HANDLE p) { CloseHandle(p); }
|
static inline void handle_destructor(HANDLE p) { CloseHandle(p); }
|
||||||
typedef generic_raii<HANDLE, handle_destructor, INVALID_HANDLE_VALUE> handle_raii;
|
typedef generic_raii<HANDLE, handle_destructor, INVALID_HANDLE_VALUE> handle_raii;
|
||||||
|
|
||||||
|
@ -4,11 +4,94 @@
|
|||||||
*
|
*
|
||||||
* Distributed under terms of the GPL3 license.
|
* Distributed under terms of the GPL3 license.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
#include <collection.h>
|
||||||
|
#include <winrt/base.h>
|
||||||
|
#include <winrt/Windows.Foundation.Collections.h>
|
||||||
|
#include <windows.foundation.h>
|
||||||
|
#include <windows.media.speechsynthesis.h>
|
||||||
|
#include <windows.storage.streams.h>
|
||||||
|
|
||||||
|
using namespace Windows::Foundation;
|
||||||
|
using namespace Windows::Foundation::Collections;
|
||||||
|
using namespace Windows::Media::SpeechSynthesis;
|
||||||
|
using namespace Windows::Storage::Streams;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PyObject_HEAD
|
||||||
|
SpeechSynthesizer ^synth;
|
||||||
|
} Synthesizer;
|
||||||
|
|
||||||
|
|
||||||
|
static PyTypeObject SynthesizerType = {
|
||||||
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
Synthesizer_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { INITIALIZE_COM_IN_FUNCTION
|
||||||
|
Synthesizer *self = (Synthesizer *) type->tp_alloc(type, 0);
|
||||||
|
if (self) {
|
||||||
|
self->synth = ref new SpeechSynthesizer();
|
||||||
|
}
|
||||||
|
if (self && !PyErr_Occurred()) com.detach();
|
||||||
|
return (PyObject*)self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
Synthesizer_dealloc(Synthesizer *self) {
|
||||||
|
self->synth = nullptr;
|
||||||
|
CoUninitialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
voice_as_dict(VoiceInformation ^voice) {
|
||||||
|
const char *gender = "";
|
||||||
|
switch (voice->Gender) {
|
||||||
|
case VoiceGender::Male: gender = "male"; break;
|
||||||
|
case VoiceGender::Female: gender = "female"; break;
|
||||||
|
}
|
||||||
|
return Py_BuildValue("{su su su su ss}",
|
||||||
|
"display_name", voice->DisplayName? voice->DisplayName->Data() : NULL,
|
||||||
|
"description", voice->Description ? voice->Description->Data() : NULL,
|
||||||
|
"id", voice->Id ? voice->Id->Data(): NULL,
|
||||||
|
"language", voice->Language ? voice->Language->Data() : NULL,
|
||||||
|
"gender", gender
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
all_voices(PyObject* /*self*/, PyObject* /*args*/) { INITIALIZE_COM_IN_FUNCTION
|
||||||
|
IVectorView<VoiceInformation^>^ voices = SpeechSynthesizer::AllVoices;
|
||||||
|
pyobject_raii ans(PyTuple_New(voices->Size));
|
||||||
|
if (!ans) return NULL;
|
||||||
|
Py_ssize_t i = 0;
|
||||||
|
for(auto voice : voices) {
|
||||||
|
PyObject *v = voice_as_dict(voice);
|
||||||
|
if (v) {
|
||||||
|
PyTuple_SET_ITEM(ans.ptr(), i++, v);
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ans.detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
default_voice(PyObject* /*self*/, PyObject* /*args*/) { INITIALIZE_COM_IN_FUNCTION
|
||||||
|
return voice_as_dict(SpeechSynthesizer::DefaultVoice);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define M(name, args) { #name, (PyCFunction)Synthesizer_##name, args, ""}
|
||||||
|
static PyMethodDef Synthesizer_methods[] = {
|
||||||
|
{NULL, NULL, 0, NULL}
|
||||||
|
};
|
||||||
|
#undef M
|
||||||
|
|
||||||
#define M(name, args) { #name, name, args, ""}
|
#define M(name, args) { #name, name, args, ""}
|
||||||
static PyMethodDef methods[] = {
|
static PyMethodDef methods[] = {
|
||||||
|
M(all_voices, METH_NOARGS),
|
||||||
|
M(default_voice, METH_NOARGS),
|
||||||
{NULL, NULL, 0, NULL}
|
{NULL, NULL, 0, NULL}
|
||||||
};
|
};
|
||||||
#undef M
|
#undef M
|
||||||
@ -16,6 +99,22 @@ static PyMethodDef methods[] = {
|
|||||||
|
|
||||||
static int
|
static int
|
||||||
exec_module(PyObject *m) {
|
exec_module(PyObject *m) {
|
||||||
|
SynthesizerType.tp_name = "winspeech.Synthesizer";
|
||||||
|
SynthesizerType.tp_doc = "Wrapper for SpeechSynthesizer";
|
||||||
|
SynthesizerType.tp_basicsize = sizeof(Synthesizer);
|
||||||
|
SynthesizerType.tp_itemsize = 0;
|
||||||
|
SynthesizerType.tp_flags = Py_TPFLAGS_DEFAULT;
|
||||||
|
SynthesizerType.tp_new = Synthesizer_new;
|
||||||
|
SynthesizerType.tp_methods = Synthesizer_methods;
|
||||||
|
SynthesizerType.tp_dealloc = (destructor)Synthesizer_dealloc;
|
||||||
|
if (PyType_Ready(&SynthesizerType) < 0) return -1;
|
||||||
|
|
||||||
|
Py_INCREF(&SynthesizerType);
|
||||||
|
if (PyModule_AddObject(m, "Synthesizer", (PyObject *) &SynthesizerType) < 0) {
|
||||||
|
Py_DECREF(&SynthesizerType);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,17 +273,6 @@ class DeleteFileProgressSink : public IFileOperationProgressSink { // {{{
|
|||||||
ULONG m_cRef;
|
ULONG m_cRef;
|
||||||
}; // }}}
|
}; // }}}
|
||||||
|
|
||||||
class scoped_com_initializer { // {{{
|
|
||||||
public:
|
|
||||||
scoped_com_initializer() : m_succeded(false) { if (SUCCEEDED(CoInitialize(NULL))) m_succeded = true; }
|
|
||||||
~scoped_com_initializer() { CoUninitialize(); }
|
|
||||||
bool succeeded() { return m_succeded; }
|
|
||||||
private:
|
|
||||||
bool m_succeded;
|
|
||||||
scoped_com_initializer( const scoped_com_initializer & ) ;
|
|
||||||
scoped_com_initializer & operator=( const scoped_com_initializer & ) ;
|
|
||||||
}; // }}}
|
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
get_computer_name(PyObject *self, PyObject *args) {
|
get_computer_name(PyObject *self, PyObject *args) {
|
||||||
COMPUTER_NAME_FORMAT fmt = ComputerNameDnsFullyQualified;
|
COMPUTER_NAME_FORMAT fmt = ComputerNameDnsFullyQualified;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user