diff --git a/libcore/include/atlsubset.h b/libcore/include/atlsubset.h new file mode 100644 index 00000000000..6b5667467f8 --- /dev/null +++ b/libcore/include/atlsubset.h @@ -0,0 +1,180 @@ +/* Copyright (C) 2003-2013 Runtime Revolution Ltd. + +This file is part of LiveCode. + +LiveCode is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License v3 as published by the Free +Software Foundation. + +LiveCode 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 +for more details. + +You should have received a copy of the GNU General Public License +along with LiveCode. If not see . */ + +#ifndef __MINI_ATL_H +#define __MINI_ATL_H +#define __ATLBASE_H__ + +#include + +//////////////////////////////////////////////////////////////////////////////// +// String Conversion + +inline char *_ConvW2A(char *p_ansi, const WCHAR *p_wide, int p_ansi_chars) +{ + int t_conv; + t_conv = WideCharToMultiByte(CP_ACP, 0, p_wide, -1, p_ansi, p_ansi_chars, NULL, NULL); + if (t_conv == 0) + return NULL; + return p_ansi; +} + +inline WCHAR *_ConvA2W(WCHAR *p_wide, const char *p_ansi, int p_wide_chars) +{ + int t_conv; + t_conv = MultiByteToWideChar(CP_ACP, 0, p_ansi, -1, p_wide, p_wide_chars); + if (t_conv == 0) + return NULL; + return p_wide; +} + +#define USES_CONVERSION int _conv_chars; char *_conv_ansi_str; WCHAR *_conv_wide_str; (_conv_chars,_conv_ansi_str,_conv_wide_str); + +#define W2A(w) (w == NULL ? NULL : (_conv_chars = WideCharToMultiByte(CP_ACP, 0, w, -1, NULL, 0, NULL, NULL), _conv_ansi_str = (char*)alloca(_conv_chars), _ConvW2A(_conv_ansi_str, w, _conv_chars))) +#define A2W(a) (a == NULL ? NULL : (_conv_chars = MultiByteToWideChar(CP_ACP, 0, a, -1, NULL, 0), _conv_wide_str = (WCHAR*)alloca(_conv_chars * sizeof(WCHAR)), _ConvA2W(_conv_wide_str, a, _conv_chars))) + +#if defined(_UNICODE) +#define W2T(a) (a) +#define T2W(a) (a) +#else +#define W2T W2A +#define T2W A2W +#endif + +//////////////////////////////////////////////////////////////////////////////// +// CComPtr + +template +class CComPtr +{ +public: + T** operator & (void) + { + return &m_ptr; + } + + bool operator ! (void) + { + return m_ptr == NULL; + } + + T* operator -> (void) + { + return m_ptr; + } + + operator T* (void) + { + return m_ptr; + } + + T* operator = (const CComPtr &p_comptr) + { + if (m_ptr != p_comptr.m_ptr) + { + Release(); + m_ptr = p_comptr.m_ptr; + + if (m_ptr != NULL) + m_ptr->AddRef(); + } + + return m_ptr; + } + + CComPtr() + { + m_ptr = NULL; + } + + //CComPtr(T*); + //CComPtr(const CComPtr&); + + //template + //CComPtr(const CComPtr&); + + CComPtr(int p_null) + { + // initialise to NULL + m_ptr = NULL; + } + + ~CComPtr() + { + Release(); + } + + HRESULT CoCreateInstance(GUID p_id, IUnknown *p_outer = NULL, DWORD p_cls_context = CLSCTX_ALL) + { + HRESULT t_result; + t_result = ::CoCreateInstance(p_id, p_outer, p_cls_context, __uuidof(T), (void**)&m_ptr); + + return t_result; + } + + void Release(void) + { + if (m_ptr != NULL) + m_ptr->Release(); + m_ptr = NULL; + } + + T* Detach(void) + { + T* t_ptr = m_ptr; + m_ptr = NULL; + return t_ptr; + } + +protected: + T *m_ptr; +}; + +template +class CComQIPtr : public CComPtr +{ +public: + T* operator = (IUnknown *p_unknown) + { + Query(p_unknown); + return m_ptr; + } + + CComQIPtr() : CComPtr() + { + } + + //CComQIPtr(T*); + CComQIPtr(IUnknown *p_unknown) + { + Query(p_unknown); + } + +protected: + void Query(IUnknown *p_unknown) + { + IUnknown *t_queried = NULL; + if (p_unknown != NULL) + p_unknown->QueryInterface(*I, (void**)&t_queried); + + Release(); + m_ptr = (T*)t_queried; + } +}; + +//////////////////////////////////////////////////////////////////////////////// + +#endif diff --git a/revspeech/revspeech.vcproj b/revspeech/revspeech.vcproj index 972eef19cc8..249730cf817 100644 --- a/revspeech/revspeech.vcproj +++ b/revspeech/revspeech.vcproj @@ -57,6 +57,7 @@ /> ", m_Npitch); strcpy( pstrTotal, strXMLtag); strcat( pstrTotal, p_string); - szWTextString = new WCHAR[ strlen(pstrTotal) + 20 ]; - wcscpy( szWTextString, A2W(pstrTotal)); + + szWTextString = A2W(pstrTotal); } else - { - int t_length; - t_length = strlen(p_string); - szWTextString = new WCHAR[ t_length + 1 ]; - - wcscpy( szWTextString, A2W(p_string)); - } + szWTextString = A2W(p_string); hr = m_cpVoice->Speak(szWTextString, SPF_ASYNC | SPF_IS_XML, NULL ); - delete[] szWTextString; - if( SUCCEEDED( hr ) ) { isspeaking = true; @@ -235,13 +227,15 @@ bool WindowsSAPI5Narrator::SpeakToFile(const char* p_string, const char* p_file) bool t_success; t_success = true; - // Set the audio format - CSpStreamFormat t_audio_format; - hr = t_audio_format.AssignFormat(SPSF_44kHz16BitStereo); - if (!SUCCEEDED(hr)) - { - t_success = false; - } + // Setup the wave format structure - 44kHz, 16bit, stereo + WAVEFORMATEX t_waveformat; + t_waveformat.wFormatTag = WAVE_FORMAT_PCM; + t_waveformat.nChannels = 2; + t_waveformat.nBlockAlign = 4; + t_waveformat.nSamplesPerSec = 44100; + t_waveformat.wBitsPerSample = 16; + t_waveformat.nAvgBytesPerSec = t_waveformat.nSamplesPerSec * t_waveformat.nBlockAlign; + t_waveformat.cbSize = 0; // unicode string containg file path WCHAR* t_file; @@ -265,10 +259,17 @@ bool WindowsSAPI5Narrator::SpeakToFile(const char* p_string, const char* p_file) wcscpy(t_file, A2W(t_native_path)); // Bind the output to the stream - hr = SPBindToFile(t_file, SPFM_CREATE_ALWAYS, &t_cstream, &t_audio_format.FormatId(), t_audio_format.WaveFormatExPtr(), SPFEI_ALL_EVENTS); - if (!SUCCEEDED(hr)) + hr = CoCreateInstance(CLSID_SpStream, NULL, CLSCTX_ALL, __uuidof(t_cstream), (void**)&t_cstream); + t_success = SUCCEEDED(hr); + if (t_success) { - t_success = false; + hr = t_cstream->BindToFile(t_file, SPFM_CREATE_ALWAYS, &SPDFID_WaveFormatEx, &t_waveformat, SPFEI_ALL_EVENTS); + t_success = SUCCEEDED(hr); + if (!t_success) + { + t_cstream->Release(); + t_cstream = NULL; + } } } } @@ -409,6 +410,156 @@ bool WindowsSAPI5Narrator::GetVolume(int& p_volume) } } +bool _GetCategoryFromId(const WCHAR *p_id, ISpObjectTokenCategory** r_category) +{ + bool t_success = true; + + CComPtr t_token_category = NULL; + t_success = SUCCEEDED(t_token_category.CoCreateInstance(CLSID_SpObjectTokenCategory)); + + if (t_success) + t_success = SUCCEEDED(t_token_category->SetId(p_id, FALSE)); + + if (t_success) + *r_category = t_token_category.Detach(); + + return t_success; +} + +bool _EnumTokens(const WCHAR *p_category_id, const WCHAR *p_req_attribs, const WCHAR *p_opt_attribs, IEnumSpObjectTokens **r_enum) +{ + bool t_success = true; + + CComPtr t_category; + t_success = _GetCategoryFromId(p_category_id, &t_category); + + if (t_success) + t_success = SUCCEEDED(t_category->EnumTokens(p_req_attribs, p_opt_attribs, r_enum)); + + return t_success; +} + +bool _GetDescription(ISpObjectToken *p_token, WCHAR **r_description) +{ + typedef HRESULT (WINAPI *_RegLoadMUIStringWPtr)(HKEY, LPCWSTR, LPWSTR, DWORD, LPDWORD, DWORD, LPCWSTR); + static HMODULE s_advapi32 = NULL; + static _RegLoadMUIStringWPtr s_RegLoadMUIStringW = NULL; + + if (s_advapi32 == NULL) + { + // Attempt to load function RegLoadMUIStringW which should be available if we're running on Vista & later + s_advapi32 = LoadLibraryA("advapi32.dll"); + if(s_advapi32 != NULL) + s_RegLoadMUIStringW = (_RegLoadMUIStringWPtr)GetProcAddress(s_advapi32, "RegLoadMUIStringW"); + } + + bool t_success = true; + + if (r_description == NULL) + { + return false; + } + + WCHAR *t_description = NULL; + + if(s_RegLoadMUIStringW != NULL) + { + WCHAR* t_path = NULL; + WCHAR* t_reg_key = NULL; + HKEY t_hkey = NULL; + + t_success = SUCCEEDED(p_token->GetId(&t_reg_key)); + + if (t_success) + { + // Split the path & base of the registry key - path is invalid if there is no separator + t_path = wcschr(t_reg_key, L'\\'); + t_success = t_path != NULL; + } + if(t_success) + { + *t_path++ = L'\0'; + + if (wcscmp(t_reg_key, L"HKEY_LOCAL_MACHINE") == 0) + t_success = ERROR_SUCCESS == RegOpenKeyExW(HKEY_LOCAL_MACHINE, t_path, 0, KEY_QUERY_VALUE, &t_hkey); + else if (wcscmp(t_reg_key, L"HKEY_CURRENT_USER") == 0) + t_success = ERROR_SUCCESS == RegOpenKeyExW(HKEY_CURRENT_USER, t_path, 0, KEY_QUERY_VALUE, &t_hkey); + else + t_success = false; + + DWORD t_size = 0; + if (t_success) + t_success = ERROR_MORE_DATA == s_RegLoadMUIStringW(t_hkey, L"Description", NULL, 0, &t_size, 0, NULL); + if (t_success) + t_success = NULL != (t_description = (WCHAR*) CoTaskMemAlloc(t_size)); + if (t_success) + t_success = ERROR_SUCCESS == s_RegLoadMUIStringW(t_hkey, L"Description", t_description, t_size, NULL, 0, NULL); + } + + // Cleanup + if(t_hkey) + RegCloseKey(t_hkey); + if(t_reg_key) + CoTaskMemFree(t_reg_key); + + if (!t_success && t_description != NULL) + { + CoTaskMemFree(t_description); + t_description = NULL; + } + } + + // fetch from the registry - if all else fails, fallback to the default attribute + if (s_RegLoadMUIStringW == NULL || !t_success) + { + WCHAR t_lang_id[10]; + + if (_ultow_s(GetUserDefaultUILanguage(), t_lang_id, 9, 16)) + { + t_lang_id[0] = L'0'; + t_lang_id[1] = 0; + } + + HRESULT hr; + hr = p_token->GetStringValue(t_lang_id, &t_description); + if (hr == SPERR_NOT_FOUND) + { + hr = p_token->GetStringValue(NULL, &t_description); + } + t_success = SUCCEEDED(hr); + } + + if (t_success) + *r_description = t_description; + + return t_success; +} + +// Helper function, frees tokens allocated by ListVoices. +void ManageCleanup( WCHAR** ppszTokenIds, CSpDynamicString* ppcDesciptionString, ULONG ulNumTokens) +{ + ULONG ulIndex; + + // Free all allocated token ids + if ( ppszTokenIds ) + { + for ( ulIndex = 0; ulIndex < ulNumTokens; ulIndex++ ) + { + if ( NULL != ppszTokenIds[ulIndex] ) + { + CoTaskMemFree( ppszTokenIds[ulIndex] ); + } + } + + delete [] ppszTokenIds; + } + + if ( ppcDesciptionString ) + { + delete [] ppcDesciptionString; + } +} + // Returns // true if listing voices suceeeded, false otherwise. // Description @@ -418,6 +569,9 @@ bool WindowsSAPI5Narrator::GetVolume(int& p_volume) // to get a token enumerator for the voices or if out of memory when listing voices. bool WindowsSAPI5Narrator::ListVoices(NarratorGender p_gender, NarratorListVoicesCallback p_callback, void* p_context) { + bool t_success = true; + + USES_CONVERSION; if(!bInited) return false; @@ -451,41 +605,40 @@ bool WindowsSAPI5Narrator::ListVoices(NarratorGender p_gender, NarratorListVoice } // Get a token enumerator for tts voices available - HRESULT hr = SpEnumTokens(SPCAT_VOICES, szRequiredAttributes, NULL, &cpEnum); + t_success = _EnumTokens(SPCAT_VOICES, szRequiredAttributes, NULL, &cpEnum); - if (SUCCEEDED(hr)) + if (t_success) { - hr = cpEnum->GetCount( &ulNumTokens ); + t_success = SUCCEEDED(cpEnum->GetCount( &ulNumTokens )); - if ( SUCCEEDED( hr ) && 0 != ulNumTokens ) + if ( t_success && 0 != ulNumTokens ) { ppcDesciptionString = new CSpDynamicString [ulNumTokens]; if ( NULL == ppcDesciptionString ) { - hr = E_OUTOFMEMORY; + /* TODO - CLEANUP */ return false; } ppszTokenIds = new WCHAR* [ulNumTokens]; if ( NULL == ppszTokenIds ) { - hr = E_OUTOFMEMORY; + /* TODO - CLEANUP */ return false; } ZeroMemory( ppszTokenIds, ulNumTokens*sizeof( WCHAR* ) ); // Get the next token in the enumeration // State is maintained in the enumerator - while (cpEnum->Next(1, &pToken, NULL) == S_OK) + while (t_success && cpEnum->Next(1, &pToken, NULL) == S_OK) { // Get a string which describes the token, in our case, the voice name - hr = SpGetDescription( pToken, &ppcDesciptionString[ulIndex] ); - _ASSERTE( SUCCEEDED( hr ) ); + t_success = SUCCEEDED(_GetDescription( pToken, &ppcDesciptionString[ulIndex] )); // Get the token id, for a low overhead way to retrieve the token later // without holding on to the object itself - hr = pToken->GetId( &ppszTokenIds[ulIndex] ); - _ASSERTE( SUCCEEDED( hr ) ); + if (t_success) + t_success = SUCCEEDED(pToken->GetId( &ppszTokenIds[ulIndex] )); ulIndex++; @@ -495,7 +648,7 @@ bool WindowsSAPI5Narrator::ListVoices(NarratorGender p_gender, NarratorListVoice } } // if we've failed to properly initialize, then we should completely shut-down - if ( S_OK != hr ) + if ( !t_success ) { if ( pToken ) { @@ -509,17 +662,14 @@ bool WindowsSAPI5Narrator::ListVoices(NarratorGender p_gender, NarratorListVoice for ( ulIndex = 0; ulIndex < ulNumTokens; ulIndex++ ) { - char* string = ppcDesciptionString[ulIndex].CopyToChar(); + const char* string = W2A(ppcDesciptionString[ulIndex]); p_callback(p_context, p_gender, (const char *)string); } ManageCleanup( ppszTokenIds, ppcDesciptionString, ulNumTokens ); - return true; - } - else - { - return false; } + + return t_success; } // Parameters @@ -533,41 +683,40 @@ bool WindowsSAPI5Narrator::ListVoices(NarratorGender p_gender, NarratorListVoice // the matching token fails then also returns false. bool WindowsSAPI5Narrator::SetVoice(const char* p_voice) { + USES_CONVERSION; if(!bInited) return false; + bool t_success = true; + + const WCHAR *t_wvoice = A2W(p_voice); + CComPtr cpEnum;// Pointer to token enumerator ULONG ulIndex = 0, ulCurTokenIndex = 0, ulNumTokens = 0; // Find out which token corresponds to our voice which is currently in use ISpObjectToken *pToken = NULL; // Token interface pointer - HRESULT hr = SpEnumTokens(SPCAT_VOICES, NULL, NULL, &cpEnum); + t_success = _EnumTokens(SPCAT_VOICES, NULL, NULL, &cpEnum); - if (SUCCEEDED(hr) ) - { - hr = cpEnum->GetCount( &ulNumTokens ); - } - else - { - return false; - } + if (t_success) + t_success = SUCCEEDED(cpEnum->GetCount( &ulNumTokens )); + if (t_success) + t_success = ulNumTokens > 0; - if ( SUCCEEDED(hr) && 0 != ulNumTokens) - { - while (cpEnum->Next(1, &pToken, NULL) == S_OK) - { - CSpDynamicString ppcDesciptionString; - // Get a string which describes the token, in our case, the voice name - hr = SpGetDescription( pToken, &ppcDesciptionString); - _ASSERTE( SUCCEEDED( hr ) ); - - // Release the token itself - pToken->Release(); - pToken = NULL; + while (t_success && cpEnum->Next(1, &pToken, NULL) == S_OK) + { + CSpDynamicString ppcDesciptionString; + // Get a string which describes the token, in our case, the voice name + t_success = _GetDescription( pToken, &ppcDesciptionString); + + // Release the token itself + pToken->Release(); + pToken = NULL; - char* string = ppcDesciptionString.CopyToChar(); - if(strcmp(string, p_voice) == 0) + if (t_success) + { + if(wcscmp(ppcDesciptionString, t_wvoice) == 0) { ulCurTokenIndex = ulIndex; break; @@ -575,27 +724,18 @@ bool WindowsSAPI5Narrator::SetVoice(const char* p_voice) ulIndex++; } } - else - return false; - if ( ulIndex < ulNumTokens ) + if (t_success) + t_success = ulIndex < ulNumTokens; + + if (t_success) { cpEnum->Item( ulCurTokenIndex, &pToken ); - hr = m_cpVoice->SetVoice( pToken ); - if (SUCCEEDED(hr)) - { - pToken->Release(); - return true; - } - else - { - return false; - } - } - else - { - return false; + t_success = SUCCEEDED(m_cpVoice->SetVoice( pToken )); + pToken->Release(); } + + return t_success; } // Parameters @@ -682,28 +822,3 @@ bool WindowsSAPI5Narrator::GetPitch(int& p_pitch) return true; } - -// Helper function, frees tokens allocated by ListVoices. -void WindowsSAPI5Narrator::ManageCleanup( WCHAR** ppszTokenIds, CSpDynamicString* ppcDesciptionString, ULONG ulNumTokens) -{ - ULONG ulIndex; - - // Free all allocated token ids - if ( ppszTokenIds ) - { - for ( ulIndex = 0; ulIndex < ulNumTokens; ulIndex++ ) - { - if ( NULL != ppszTokenIds[ulIndex] ) - { - CoTaskMemFree( ppszTokenIds[ulIndex] ); - } - } - - delete [] ppszTokenIds; - } - - if ( ppcDesciptionString ) - { - delete [] ppcDesciptionString; - } -} \ No newline at end of file diff --git a/revspeech/src/w32sapi5speech.h b/revspeech/src/w32sapi5speech.h index 22e741975e8..8c3ee905a4a 100644 --- a/revspeech/src/w32sapi5speech.h +++ b/revspeech/src/w32sapi5speech.h @@ -21,12 +21,13 @@ along with LiveCode. If not see . */ #include "revspeech.h" #endif -#include #include -#include +#include #include #include +#include "atlsubset.h" + class WindowsSAPI5Narrator: public INarrator { public: @@ -67,10 +68,39 @@ class WindowsSAPI5Narrator: public INarrator // Pointer to our tts voice CComPtr m_cpVoice; - void ManageCleanup( WCHAR** ppszTokenIds, CSpDynamicString* ppcDesciptionString, ULONG ulNumTokens); bool m_bUseDefaultPitch; short m_Npitch; }; +//////////////////////////////////////////////////////////////////////////////// + +class CSpDynamicString +{ +public: + CSpDynamicString() + { + m_ptr = NULL; + } + + ~CSpDynamicString() + { + if (m_ptr != NULL) + CoTaskMemFree(m_ptr); + } + + WCHAR** operator &(void) + { + return &m_ptr; + } + + operator WCHAR* (void) + { + return m_ptr; + } + +protected: + WCHAR *m_ptr; +}; + #endif diff --git a/revvideograbber/revvideograbber.vcproj b/revvideograbber/revvideograbber.vcproj index cc1fc146dcc..34c8b5eea1a 100644 --- a/revvideograbber/revvideograbber.vcproj +++ b/revvideograbber/revvideograbber.vcproj @@ -56,7 +56,7 @@ /> . */ #define DXVIDEO_H #include "videograbber.h" -#include -extern CComModule _Module; -#include - #include #include "qedit.h" #include +#include "atlsubset.h" + #define MAX_VIDEODEVICE 10 #define MAX_AUDIODEVICE 10 #define MAX_CODEC 50 #define MAX_DEVICENAME_LENGTH 256 - // defines for video dialog setting enum E_VideoDialog { diff --git a/stage.sln b/stage.sln index 80148bd8a85..ef9efbb01a5 100644 --- a/stage.sln +++ b/stage.sln @@ -110,9 +110,7 @@ Global {90699A04-E42A-460B-B294-40DDC3E6166B}.Release|Win32.ActiveCfg = Release|Win32 {90699A04-E42A-460B-B294-40DDC3E6166B}.Release|Win32.Build.0 = Release|Win32 {9EF7E3FF-92D7-401E-BB4F-3F5B359DB131}.Debug|Win32.ActiveCfg = Debug|Win32 - {9EF7E3FF-92D7-401E-BB4F-3F5B359DB131}.Debug|Win32.Build.0 = Debug|Win32 {9EF7E3FF-92D7-401E-BB4F-3F5B359DB131}.Release|Win32.ActiveCfg = Release|Win32 - {9EF7E3FF-92D7-401E-BB4F-3F5B359DB131}.Release|Win32.Build.0 = Release|Win32 {4E727414-3B14-45B7-9B89-BFFFE396E1C9}.Debug|Win32.ActiveCfg = Debug|Win32 {4E727414-3B14-45B7-9B89-BFFFE396E1C9}.Debug|Win32.Build.0 = Debug|Win32 {4E727414-3B14-45B7-9B89-BFFFE396E1C9}.Release|Win32.ActiveCfg = Release|Win32