mirror of
				https://github.com/JakubMelka/PDF4QT.git
				synced 2025-06-05 21:59:17 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			180 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			180 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| //    Copyright (C) 2021 Jakub Melka
 | |
| //
 | |
| //    This file is part of PDF4QT.
 | |
| //
 | |
| //    PDF4QT is free software: you can redistribute it and/or modify
 | |
| //    it under the terms of the GNU Lesser General Public License as published by
 | |
| //    the Free Software Foundation, either version 3 of the License, or
 | |
| //    with the written consent of the copyright owner, any later version.
 | |
| //
 | |
| //    PDF4QT 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 Lesser General Public License for more details.
 | |
| //
 | |
| //    You should have received a copy of the GNU Lesser General Public License
 | |
| //    along with PDF4QT.  If not, see <https://www.gnu.org/licenses/>.
 | |
| 
 | |
| #include "audiobookcreator.h"
 | |
| 
 | |
| #ifdef Q_OS_WIN
 | |
| #include <windows.h>
 | |
| #include <sapi.h>
 | |
| #if defined(PDF4QT_USE_PRAGMA_LIB)
 | |
| #pragma comment(lib, "ole32")
 | |
| #endif
 | |
| #endif
 | |
| 
 | |
| namespace pdfplugin
 | |
| {
 | |
| 
 | |
| AudioBookCreator::AudioBookCreator() :
 | |
|     m_initialized(false)
 | |
| {
 | |
| #ifdef Q_OS_WIN
 | |
|     auto comResult = ::CoInitialize(nullptr);
 | |
|     m_initialized = SUCCEEDED(comResult);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| AudioBookCreator::~AudioBookCreator()
 | |
| {
 | |
| #ifdef Q_OS_WIN
 | |
|     if (m_initialized)
 | |
|     {
 | |
|         ::CoUninitialize();
 | |
|     }
 | |
| #endif
 | |
| }
 | |
| 
 | |
| pdf::PDFOperationResult AudioBookCreator::createAudioBook(const Settings& settings, pdf::PDFDocumentTextFlow& flow)
 | |
| {
 | |
| #ifdef Q_OS_WIN
 | |
|     QString audioString;
 | |
|     QTextStream textStream(&audioString);
 | |
| 
 | |
|     for (const pdf::PDFDocumentTextFlow::Item& item : flow.getItems())
 | |
|     {
 | |
|         QString trimmedText = item.text.trimmed();
 | |
|         if (!trimmedText.isEmpty())
 | |
|         {
 | |
|             textStream << trimmedText << Qt::endl;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     auto getVoiceToken = [](const Settings& settings)
 | |
|     {
 | |
|         ISpObjectToken* token = nullptr;
 | |
| 
 | |
|         QStringList voiceSelector;
 | |
|         if (!settings.voiceName.isEmpty())
 | |
|         {
 | |
|             voiceSelector << QString("Name=%1").arg(settings.voiceName);
 | |
|         }
 | |
|         if (!settings.voiceGender.isEmpty())
 | |
|         {
 | |
|             voiceSelector << QString("Gender=%1").arg(settings.voiceGender);
 | |
|         }
 | |
|         if (!settings.voiceAge.isEmpty())
 | |
|         {
 | |
|             voiceSelector << QString("Age=%1").arg(settings.voiceAge);
 | |
|         }
 | |
|         if (!settings.voiceLangCode.isEmpty())
 | |
|         {
 | |
|             voiceSelector << QString("Language=%1").arg(settings.voiceLangCode);
 | |
|         }
 | |
|         QString voiceSelectorString = voiceSelector.join(";");
 | |
|         LPCWSTR requiredAttributes = !voiceSelectorString.isEmpty() ? (LPCWSTR)voiceSelectorString.utf16() : nullptr;
 | |
| 
 | |
|         ISpObjectTokenCategory* category = nullptr;
 | |
|         if (!SUCCEEDED(::CoCreateInstance(CLSID_SpObjectTokenCategory, NULL, CLSCTX_ALL, __uuidof(ISpObjectTokenCategory), (LPVOID*)&category)))
 | |
|         {
 | |
|             return token;
 | |
|         }
 | |
| 
 | |
|         if (!SUCCEEDED(category->SetId(SPCAT_VOICES, FALSE)))
 | |
|         {
 | |
|             category->Release();
 | |
|             return token;
 | |
|         }
 | |
| 
 | |
|         IEnumSpObjectTokens* enumTokensObject = nullptr;
 | |
|         if (SUCCEEDED(category->EnumTokens(requiredAttributes, NULL, &enumTokensObject)))
 | |
|         {
 | |
|             enumTokensObject->Next(1, &token, NULL);
 | |
|         }
 | |
| 
 | |
|         if (enumTokensObject)
 | |
|         {
 | |
|             enumTokensObject->Release();
 | |
|         }
 | |
| 
 | |
|         if (category)
 | |
|         {
 | |
|             category->Release();
 | |
|         }
 | |
| 
 | |
|         return token;
 | |
|     };
 | |
| 
 | |
|     ISpObjectToken* voiceToken = getVoiceToken(settings);
 | |
| 
 | |
|     // Do we have any voice?
 | |
|     if (!voiceToken)
 | |
|     {
 | |
|         return tr("No suitable voice found.");
 | |
|     }
 | |
| 
 | |
|     QString outputFile = settings.audioFileName;
 | |
|     BSTR outputFileName = (BSTR)outputFile.utf16();
 | |
| 
 | |
|     ISpeechFileStream* stream = nullptr;
 | |
|     if (!SUCCEEDED(::CoCreateInstance(CLSID_SpFileStream, NULL, CLSCTX_ALL, __uuidof(ISpeechFileStream), (LPVOID*)&stream)))
 | |
|     {
 | |
|         voiceToken->Release();
 | |
|         return tr("Cannot create output stream '%1'.").arg(outputFile);
 | |
|     }
 | |
| 
 | |
|     ISpVoice* voice = nullptr;
 | |
|     if (!SUCCEEDED(::CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, __uuidof(ISpVoice), (LPVOID*)&voice)))
 | |
|     {
 | |
|         voiceToken->Release();
 | |
|         stream->Release();
 | |
|         return tr("Cannot create voice.");
 | |
|     }
 | |
| 
 | |
|     if (!SUCCEEDED(stream->Open(outputFileName, SSFMCreateForWrite)))
 | |
|     {
 | |
|         voiceToken->Release();
 | |
|         voice->Release();
 | |
|         stream->Release();
 | |
|         return tr("Cannot create output stream '%1'.").arg(outputFile);
 | |
|     }
 | |
| 
 | |
|     if (!SUCCEEDED(voice->SetVoice(voiceToken)))
 | |
|     {
 | |
|         voiceToken->Release();
 | |
|         voice->Release();
 | |
|         stream->Release();
 | |
|         return tr("Failed to set requested voice.");
 | |
|     }
 | |
| 
 | |
|     LPCWSTR stringToSpeak = (LPCWSTR)audioString.utf16();
 | |
| 
 | |
|     voice->SetOutput(stream, FALSE);
 | |
|     voice->SetRate(settings.rate * 10.0);
 | |
|     voice->SetVolume(settings.volume * 100.0);
 | |
|     voice->Speak(stringToSpeak, SPF_PURGEBEFORESPEAK | SPF_PARSE_SAPI, NULL);
 | |
| 
 | |
|     voice->Release();
 | |
|     stream->Release();
 | |
|     voiceToken->Release();
 | |
| 
 | |
|     return true;
 | |
| #else
 | |
|     return tr("Audio book plugin is unsupported on your system.");
 | |
| #endif
 | |
| }
 | |
| 
 | |
| }   // namespace pdfplugin
 |