diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cd7664d83..cc3dc1ac8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -376,3 +376,45 @@ jobs: tag: ${{ github.ref }} # ==> github.event.release.tag_name file: release/*.zip file_glob: true + + build-win-cpp-example: + runs-on: windows-latest + needs: [build-cpp-shared] + + env: + # Path to the solution file relative to the root of the project. + SOLUTION_FILE_PATH: example\cpp\windows\windows_example.sln + + # Configuration type to build. + # You can convert this to a build matrix if you need coverage of multiple configuration types. + # https://docs.github.com/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix + BUILD_CONFIGURATION: Release + + steps: + - uses: actions/checkout@v3 + + - name: Add MSBuild to PATH + uses: microsoft/setup-msbuild@v1.0.2 + + - name: Restore NuGet packages + working-directory: ${{env.GITHUB_WORKSPACE}} + run: nuget restore ${{env.SOLUTION_FILE_PATH}} + + - name: Download and extract artifact + uses: actions/download-artifact@v2 + id: download + with: + name: windows-x64-cpu-cpp-shared + path: artifacts\ + + - name: Copy core.lib + working-directory: ${{env.GITHUB_WORKSPACE}} + run: | + mkdir example\cpp\windows\simple_tts\lib\x64 + copy ${{steps.download.outputs.download-path}}\core.lib example\cpp\windows\simple_tts\lib\x64 + + - name: Build + working-directory: ${{env.GITHUB_WORKSPACE}} + # Add additional options to the MSBuild command line here (like platform or verbosity level). + # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference + run: msbuild /m /p:Configuration=${{env.BUILD_CONFIGURATION}} ${{env.SOLUTION_FILE_PATH}} diff --git a/.gitignore b/.gitignore index 3a6334f00..d15e18d32 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ directml*/ # Build artifacts build/ lib/ +bin/ core/_core.cpp # Output @@ -32,3 +33,7 @@ CMakeCache.txt # Voicevox release release/ + +# Visual Studio +.vs/ +packages/ diff --git a/example/cpp/windows/README.md b/example/cpp/windows/README.md new file mode 100644 index 000000000..d30a3818c --- /dev/null +++ b/example/cpp/windows/README.md @@ -0,0 +1,61 @@ +# Windows C++ のサンプルプロジェクト + +ここには、voicevox_coreライブラリをC++から使用するサンプルプロジェクトが含まれています。 +プロジェクトを開くには、Microsoft Visual Studio Community 2022(無料)が必要です。また、「C++によるデスクトップ開発」のワークロードが必要です。 +Visual Studio Installerを使用しインストールしてください。 + +## simple_tts + +単純な音声合成を行うコンシールアプリケーションです。 + +### 環境構築・ビルド方法 + +ビルドして実行するには、「core.dll」「core.lib」「Open JTalk辞書フォルダ」が必要です。 +以下はDebug x64でビルドする場合です。他の構成・プラットフォーム向けにビルドする場合は、適宜読み替えてください。 + +Releasesから「windows-x64-cpu-cpp-shared.zip」をダウンロードします。 +zipファイルを展開し、展開されたフォルダに含まれているdllファイルを「core.dll」にリネームします。 +出力フォルダを作成するために、一度ビルドします。「windows_example.sln」をVisual Studioで開き、メニューの「ビルド」→「ソリューションのビルド」を押します。 +この段階では、ビルドは失敗します。「bin」フォルダと「lib」フォルダが生成されていればOKです。 + +「core.lib」を「simple_tts\lib\x64」に配置します。 +「core.dll」を「simple_tts\bin\x64\Debug」に配置します。 + +もう一度ビルドします。今度は成功するはずです。失敗した場合は、「core.lib」の場所を確認してください。 + +続いて、「Open JTalk辞書フォルダ」を配置します。 +http://open-jtalk.sourceforge.net/ を開き、Dictionary for Open JTalk 欄の Binary Package (UTF-8)をクリックして「open_jtalk_dic_utf_8-1.11.tar.gz」をダウンロードします。 + +展開してできた「open_jtalk_dic_utf_8-1.11」フォルダをフォルダごと「simple_tts\bin\x64\Debug」に配置します。 + +最終的には以下のようなフォルダ構成になっているはずです。 +``` +simple_tts +│ packages.config +│ simple_tts.cpp +│ simple_tts.h +│ simple_tts.vcxproj +│ simple_tts.vcxproj.filters +│ simple_tts.vcxproj.user +│ +├─bin +│ └─x64 +│ └─Debug +│ │ core.dll +│ │ onnxruntime.dll +│ │ onnxruntime_providers_shared.dll +│ │ simple_tts.exe +│ │ simple_tts.pdb +│ │ +│ └─open_jtalk_dic_utf_8-1.11 +│ +└─lib + └─x64 + │ core.lib + │ + └─Debug +``` + +### 実行 +Visual Studioのツールバーにある「ローカル Windows デバッガー」と書いてある三角のつているボタンを押すと実行できます。出力フォルダにある「simple_tts.exe」を直接実行することもできます。 +表示されたコンソール画面に、生成したい音声の文字列を入力しエンターキーを押します。そうすると音声合成が開始し、合成された音声が再生されます。 diff --git a/example/cpp/windows/simple_tts/packages.config b/example/cpp/windows/simple_tts/packages.config new file mode 100644 index 000000000..07a94bc06 --- /dev/null +++ b/example/cpp/windows/simple_tts/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/example/cpp/windows/simple_tts/simple_tts.cpp b/example/cpp/windows/simple_tts/simple_tts.cpp new file mode 100644 index 000000000..3fd6f2fc4 --- /dev/null +++ b/example/cpp/windows/simple_tts/simple_tts.cpp @@ -0,0 +1,141 @@ +// simple_tts.cpp : このファイルには 'main' 関数が含まれています。プログラム実行の開始と終了がそこで行われます。 +// + +#include "simple_tts.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "..\..\..\core\src\core.h" + +#define OPENJTALK_DICT_NAME L"open_jtalk_dic_utf_8-1.11" + +int main() { + std::wcout.imbue(std::locale("")); + std::wcin.imbue(std::locale("")); + + std::wcout << L"生成する音声の文字列を入力" << std::endl; + std::wcout << L">"; + std::wstring speak_words; + std::wcin >> speak_words; + + std::wcout << L"coreの初期化中" << std::endl; + initialize(false); + + VoicevoxResultCode result = VoicevoxResultCode::VOICEVOX_RESULT_SUCCEED; + + std::wcout << L"openjtalk辞書の読み込み" << std::endl; + result = voicevox_load_openjtalk_dict(GetOpenJTalkDict().c_str()); + if (result != VoicevoxResultCode::VOICEVOX_RESULT_SUCCEED) { + std::cout << voicevox_error_result_to_message(result) << std::endl; + return 0; + } + + std::wcout << L"音声生成中" << std::endl; + int64_t speaker_id = 0; + int output_binary_size = 0; + uint8_t* output_wav = nullptr; + result = voicevox_tts(wide_to_utf8_cppapi(speak_words).c_str(), speaker_id, &output_binary_size, &output_wav); + if (result != VoicevoxResultCode::VOICEVOX_RESULT_SUCCEED) { + std::cout << voicevox_error_result_to_message(result) << std::endl; + return 0; + } + + { + //音声ファイルの保存 + std::ofstream out_stream(GetWaveFileName().c_str(), std::ios::binary); + out_stream.write(reinterpret_cast(output_wav), output_binary_size); + std::wcout << GetWaveFileName() << L" に保存しました。" << std::endl; + } //ここでファイルが閉じられる + + std::wcout << L"音声再生中" << std::endl; + PlaySound((LPCTSTR)output_wav, nullptr, SND_MEMORY); + + std::wcout << L"音声データの開放" << std::endl; + voicevox_wav_free(output_wav); + +} + +/// +/// OpenJTalk辞書のパスを取得します。 +/// +/// OpenJTalk辞書のパス +std::string GetOpenJTalkDict() { + wchar_t buff[MAX_PATH] = {0}; + PathCchCombine(buff, MAX_PATH, GetExeDirectory().c_str(), OPENJTALK_DICT_NAME); + std::string retVal = wide_to_multi_capi(buff); + return retVal; +} + +/// +/// 音声ファイル名を取得します。 +/// +/// 音声ファイルのフルパス +std::wstring GetWaveFileName() { + wchar_t buff[MAX_PATH] = {0}; + PathCchCombine(buff, MAX_PATH, GetExeDirectory().c_str(), L"speech.wav"); + return std::wstring(buff); +} + +/// +/// 自分自身のあるパスを取得する +/// +/// 自分のexeのフルパス +std::wstring GetExePath() { + wchar_t buff[MAX_PATH] = {0}; + GetModuleFileName(nullptr, buff, MAX_PATH); + return std::wstring(buff); +} + +/// +/// 自分自身のあるディレクトリを取得する +/// +/// 自分のexeのあるディレクトリ +std::wstring GetExeDirectory() { + wchar_t buff[MAX_PATH] = {0}; + wcscpy_s(buff, MAX_PATH, GetExePath().c_str()); + //フルパスからファイル名の削除 + PathRemoveFileSpec(buff); + return std::wstring(buff); +} + +/// +/// ワイド文字列をShift_JISに変換します。 +/// +/// ワイド文字列 +/// Shift_JIS文字列 +/// +/// https://nekko1119.hatenablog.com/entry/2017/01/02/054629 から引用 +/// +std::string wide_to_multi_capi(std::wstring const& src) { + std::size_t converted{}; + std::vector dest(src.size() * sizeof(wchar_t) + 1, '\0'); + if (::_wcstombs_s_l(&converted, dest.data(), dest.size(), src.data(), _TRUNCATE, ::_create_locale(LC_ALL, "jpn")) != + 0) { + throw std::system_error{errno, std::system_category()}; + } + dest.resize(std::char_traits::length(dest.data())); + dest.shrink_to_fit(); + return std::string(dest.begin(), dest.end()); +} + +/// +/// ワイド文字列をUTF8に変換します。 +/// +/// ワイド文字列 +/// UTF8文字列 +/// +/// https://nekko1119.hatenablog.com/entry/2017/01/02/054629 から引用 +/// +std::string wide_to_utf8_cppapi(std::wstring const& src) { + std::wstring_convert> converter; + return converter.to_bytes(src); +} diff --git a/example/cpp/windows/simple_tts/simple_tts.h b/example/cpp/windows/simple_tts/simple_tts.h new file mode 100644 index 000000000..14e2316bb --- /dev/null +++ b/example/cpp/windows/simple_tts/simple_tts.h @@ -0,0 +1,9 @@ +#pragma once +#include + +std::string GetOpenJTalkDict(); +std::wstring GetWaveFileName(); +std::wstring GetExePath(); +std::wstring GetExeDirectory(); +std::string wide_to_multi_capi(std::wstring const& src); +std::string wide_to_utf8_cppapi(std::wstring const& src); \ No newline at end of file diff --git a/example/cpp/windows/simple_tts/simple_tts.vcxproj b/example/cpp/windows/simple_tts/simple_tts.vcxproj new file mode 100644 index 000000000..1a48276fa --- /dev/null +++ b/example/cpp/windows/simple_tts/simple_tts.vcxproj @@ -0,0 +1,182 @@ + + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {7f68db71-f477-4359-947f-4e35ad8b604e} + simpletts + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)lib\$(Platform)\$(Configuration)\ + + + false + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)lib\$(Platform)\$(Configuration)\ + + + true + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)lib\$(Platform)\$(Configuration)\ + + + false + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)lib\$(Platform)\$(Configuration)\ + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\..\..\core\src;%(AdditionalIncludeDirectories) + + + Console + true + Shlwapi.lib;Pathcch.lib;Winmm.lib;core.lib;%(AdditionalDependencies) + $(ProjectDir)lib\$(Platform)\ + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\..\..\core\src;%(AdditionalIncludeDirectories) + + + Console + true + true + true + Shlwapi.lib;Pathcch.lib;Winmm.lib;core.lib;%(AdditionalDependencies) + $(ProjectDir)lib\$(Platform)\ + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\..\..\core\src;%(AdditionalIncludeDirectories) + + + Console + true + Shlwapi.lib;Pathcch.lib;Winmm.lib;core.lib;%(AdditionalDependencies) + $(ProjectDir)lib\$(Platform)\ + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\..\..\core\src;%(AdditionalIncludeDirectories) + + + Console + true + true + true + Shlwapi.lib;Pathcch.lib;Winmm.lib;core.lib;%(AdditionalDependencies) + $(ProjectDir)lib\$(Platform)\ + + + + + + + + + + + + + + + + + + このプロジェクトは、このコンピューター上にない NuGet パッケージを参照しています。それらのパッケージをダウンロードするには、[NuGet パッケージの復元] を使用します。詳細については、http://go.microsoft.com/fwlink/?LinkID=322105 を参照してください。見つからないファイルは {0} です。 + + + + + \ No newline at end of file diff --git a/example/cpp/windows/simple_tts/simple_tts.vcxproj.filters b/example/cpp/windows/simple_tts/simple_tts.vcxproj.filters new file mode 100644 index 000000000..a96485cbb --- /dev/null +++ b/example/cpp/windows/simple_tts/simple_tts.vcxproj.filters @@ -0,0 +1,32 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + ソース ファイル + + + + + ヘッダー ファイル + + + + + + + + \ No newline at end of file diff --git a/example/cpp/windows/simple_tts/simple_tts.vcxproj.user b/example/cpp/windows/simple_tts/simple_tts.vcxproj.user new file mode 100644 index 000000000..88a550947 --- /dev/null +++ b/example/cpp/windows/simple_tts/simple_tts.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/example/cpp/windows/windows_example.sln b/example/cpp/windows/windows_example.sln new file mode 100644 index 000000000..995b2479b --- /dev/null +++ b/example/cpp/windows/windows_example.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32228.430 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simple_tts", "simple_tts\simple_tts.vcxproj", "{7F68DB71-F477-4359-947F-4E35AD8B604E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7F68DB71-F477-4359-947F-4E35AD8B604E}.Debug|x64.ActiveCfg = Debug|x64 + {7F68DB71-F477-4359-947F-4E35AD8B604E}.Debug|x64.Build.0 = Debug|x64 + {7F68DB71-F477-4359-947F-4E35AD8B604E}.Debug|x86.ActiveCfg = Debug|Win32 + {7F68DB71-F477-4359-947F-4E35AD8B604E}.Debug|x86.Build.0 = Debug|Win32 + {7F68DB71-F477-4359-947F-4E35AD8B604E}.Release|x64.ActiveCfg = Release|x64 + {7F68DB71-F477-4359-947F-4E35AD8B604E}.Release|x64.Build.0 = Release|x64 + {7F68DB71-F477-4359-947F-4E35AD8B604E}.Release|x86.ActiveCfg = Release|Win32 + {7F68DB71-F477-4359-947F-4E35AD8B604E}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {001C3FD2-0646-4227-97B4-86453599A519} + EndGlobalSection +EndGlobal