Have an amazing solution built in RAD Studio? Let us know. Looking for discounts? Visit our Special Offers page!
C++DelphiRAD Studio事例

Delphi / C++Builder を使って『ずんだもん』におしゃべりさせるのだ!

zundamonblogheader

『ずんだもん』をご存知でしょうか?
その名前に聞き覚えがなくても、
こちらで声やその姿を見ていただければ YouTube 動画のナレーションとして見覚えがある方も多いでしょう。
動画のナレーションとしては
AquesTalk を利用した『ゆっくり実況』がおなじみですが、『ずんだもん (VOICEVOX) 』もその活躍の場を広げています。
動画視聴者にとって『ずんだもん』が支持される理由は、特徴的で親しみやすく、動画向けとも言える聞き取りやすい声にあります。

同時に『ずんだもん』は多くの動画クリエイターからも支持を得ているようです。その大きな理由は、個人の動画クリエイターでも気軽に今すぐ始められる点にあります。
このため『ずんだもん』は動画制作の強い味方という印象が強いかもしれませんが、実はアプリケーション開発でも活用しやすい特徴を備えています。

『ずんだもん』の音声生成は、専用アプリケーション(VOICEVOX)の GUI 操作だけでなく、REST API 経由でも行うことができます。
この点は、REST API の利用を得意とする Delphi / C++Builder と非常に相性が良い部分です。
さらに、『ずんだもん』の音声を生成するために用意されている REST API は構成がシンプルなため、REST API を初めて扱う際の学習教材としても適しています。

今回はこの人気キャラクター『ずんだもん』にDelphi / C++Builderで作成したアプリケーションの「なかの人」として、音声ガイダンスを担ってもらう方法をご紹介します。

※ 本記事における「ずんだもん」という表記は、特に断りのない限り音声(音源)の利用を指すものとします。なお、実際にアプリケーションへ組み込み、運用する際には、必ず公式サイトの利用規約をご確認ください。

動作イメージ

今回は Delphi / C++Builder を使用し、Windows VCLアプリケーションを作成します。

フォームに配置した EditBox の入力文字列と「こんにちはなのだ」を結合した文章を『ずんだもん』に発話をさせます。
図の例では EditBox に 「デルファイ」と入力されていますので、実行ボタンをクリックすると「デルファイさん こんにちはなのだ」と発話します。

zundamonapp

準備するもの

準備するものはたったこれだけです:

  • VOICEVOX (テキスト読み上げアプリケーション)
  • RAD Studio (Delphi/C++Builder)

追加で必要なランタイムやライブラリのインストールはありません。

REST API サーバーの準備

REST API サーバーは VOICEVOX アプリケーションそのものを使用します。
VOICEVOX は、指定したテキストを元に読み上げを行うデスクトップ アプリケーションですが、同時に REST API のリクエストを受信するサーバとしても機能します。
VOICEVOX は公式サイトより入手し、RAD Studio と同じPCにインストール、インストールを完了させたら VOICEVOX を起動します。

次に Webブラウザを起動し、 http://localhost:50021/docsにアクセスしてみましょう。図のように API 一覧が表示されます。

voicevox_docs-2

RAD Studio からのテスト

RAD Studio からも VOICEVOX にアクセスしてみましょう。
[ツール > RESTデバッガ] を起動し、「要求」タブに以下の通り入力します:

voicevox_restdebugger

応答結果として「本体」タブに JSON 形式のデータを表示していることが確認できます。

VCL アプリケーションからの実行

次に VCL アプリケーションでの使用例を見ていきましょう。今回は VCL アプリケーションから『ずんだもん』の音声を再生することを目的とします。
例えば VCL アプリケーションから「こんにちは」といった固定メッセージを再生するだけであれば、VOICEVOX 単体で音声データを作成し、それを利用することができます。
一方で、「◯◯さん、こんにちは」のように再生内容を動的に変更したい場合には、その内容に応じて音声データを生成する仕組みが必要になります。
今回ご紹介する例は、EditBox に入力された文字列を元に音声を生成し、再生するシンプルなアプリケーションです。

プログラムの流れ

大まかな処理フローは次のとおりです:

  1. 「実行」ボタンクリックでイベントを実行します。
  2. EditBox に入力された内容を元に発話する文章を作成し、それをVOICEVOX の API audio_query のパラメータとして設定、 POST します→ レスポンスとして JSON 形式のデータが取得できます。
  3.  audio_query の実行結果(JSON)を VOICEVOX の API synthesis のパラメータとして設定し POST します → レスポンスとしてバイナリ形式の音声データ(WAV ファイル) がダウンロードできるのでローカルドライブに保存します。
  4. 音声データを再生します。

ここでのポイントは VOICEVOX の REST API に2回リクエストを送信している点です。

  • 1度目のリクエストでは、発話のためのテキストを送信し、そのレスポンスとして WAV 作成のための「設計図」とも言える JSON 形式のデータを受信します。
  • 2度目のリクエストでは、その「設計図」を送信し、WAV 形式の音声ファイルを受信します。

サンプルアプリケーションのフォームデザインとコード

フォームデザイン例

※ TRESTClient、TRESTRequest、TRESTResponse については、前章「RAD Studio からのテスト」のテストを行った後、「コンポーネントのコピー」をクリックし、フォームデザイン画面でペーストすることで配置できます。
「コンポーネントのコピー」を使用しない場合は、パレットよりコンポーネントを選択し、対象のフォームにドラッグ・アンド・ドロップして配置します。

zundamondesinger

コーディング例

Delphi コード(implementation 部のみ抜粋)

// 実行ボタンクリック時 Edit1の内容をもとにメッセージ再生
procedure TForm1.Button1Click(Sender: TObject);
const
  WAV_FILE = 'output.wav';   // 音声ファイル名
begin
  // WAVファイル作成 Exeと同じ場所に作成される
  GoZunda(Edit1.Text + 'さんこんにちはなのだ', WAV_FILE);

  // WAVファイル再生
  PlaySoundW(WAV_FILE, 0, SND_FILENAME or SND_ASYNC);
end;

// テキストを元に音声データ作成
procedure TForm1.GoZunda(const AText: string; const AOutputWavFile: string);
var
  LJsonObj : TJSONObject;
  LWavData : TBytes;
  FS      : TFileStream;
begin
  // 初期化(デザイナで設定済みであれば省略可能)
  RestClient1.BaseURL := 'http://127.0.0.1:50021';
  RestRequest1.Method := rmPOST;
  RESTRequest1.Client := RESTClient1;
  RESTRequest1.Response := RESTResponse1;

  // 1. audio_query 呼び出し
  with RestRequest1 do
  begin
    Resource := 'audio_query';
    Params.Clear;
    AddParameter('text', AText, pkQUERY); // 発話内容の文字列
    AddParameter('speaker', '1', pkQUERY);   // SpeakerId 1はずんだもん
    Body.ClearBody;
    Execute;
  end;

  // RestResponse1.StatusCode のエラー判定
  if RESTResponse1.StatusCode <> 200 then
    raise Exception.Create('Error RESTResponse1.StatusCode = ' 
                                       + RESTResponse1.StatusCode.ToString);

  // 2. synthesis 呼び出しと JSON のパース
  LJsonObj := TJSONObject.ParseJSONValue(RestResponse1.Content) as TJSONObject;
  try
    with RestRequest1 do
    begin
      Resource := 'synthesis';
      Params.Clear;
      Body.ClearBody;
      AddParameter('speaker', '1', pkGETorPOST);
      AddBody(LJsonObj.ToJSON, ctAPPLICATION_JSON);
      Execute;
    end;

  // RestResponse1.StatusCode のエラー判定
  if RESTResponse1.StatusCode <> 200 then
    raise Exception.Create('Error RESTResponse1.StatusCode = ' 
                                    + RESTResponse1.StatusCode.ToString);

    // Wavファイルを保存
    LWavData := RestResponse1.RawBytes;
    FS := TFileStream.Create(AOutputWavFile, fmCreate);
    try
      if Length(LWavData) > 0 then
        FS.WriteBuffer(LWavData[0], Length(LWavData));
    finally
      FS.Free;
    end;
  finally
    LJsonObj.Free;
  end;
end;

C++Builder コード (ヘッダファイル省略)

#include <vcl.hpp>
#include <System.SysUtils.hpp>
#include <System.JSON.hpp>
#include <System.Classes.hpp>
#include <Winapi.MMSystem.hpp>
#include <System.IOUtils.hpp>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1* Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) {}
//---------------------------------------------------------------------------
// 実行ボタンクリック時 Edit1の内容をもとにメッセージ再生
void __fastcall TForm1::Button1Click(TObject* Sender)
{
    const String WAV_FILE = L"output.wav"; // 音声ファイル名

    // WAVファイル作成(Exeと同じ場所に作成される)
    GoZunda(Edit1->Text + L"さんこんにちはなのだ", WAV_FILE);

    // WAVファイル再生
    PlaySoundW(WAV_FILE.c_str(), 0, SND_FILENAME | SND_ASYNC);
}
//---------------------------------------------------------------------------
// テキストを元に音声データ作成
void __fastcall TForm1::GoZunda(const String AText, const String AOutputWavFile)
{
    TJSONObject* LJsonObj = nullptr;
    TBytes LWavData;
    TFileStream* FS = nullptr;

    // 初期化(デザイナで設定済みなら省略可)
    RESTClient1->BaseURL = L"http://127.0.0.1:50021";
    RESTRequest1->Method = rmPOST;
    RESTRequest1->Client = RESTClient1;
    RESTRequest1->Response = RESTResponse1;

    // 1. audio_query 呼び出し
    RESTRequest1->Resource = L"audio_query";
    RESTRequest1->Params->Clear();
    RESTRequest1->AddParameter(L"text", AText, pkQUERY);
    RESTRequest1->AddParameter(L"speaker", L"1", pkQUERY); // SpeakerId 1 はずんだもん
    RESTRequest1->Body->ClearBody();
    RESTRequest1->Execute();

    if (RESTResponse1->StatusCode != 200)
        throw Exception(L"Error RESTResponse1.StatusCode = " +
                        IntToStr(RESTResponse1->StatusCode));

    // 2. synthesis 呼び出し(JSONパース)
    LJsonObj = dynamic_cast<TJSONObject*>(
        TJSONObject::ParseJSONValue(RESTResponse1->Content));
    if (!LJsonObj)
        throw Exception(L"Error: audio_query response is not JSON object.");

    try {
        RESTRequest1->Resource = L"synthesis";
        RESTRequest1->Params->Clear();
        RESTRequest1->Body->ClearBody();
        RESTRequest1->AddParameter(L"speaker", L"1", pkGETorPOST);
        RESTRequest1->AddBody(LJsonObj->ToJSON(), ctAPPLICATION_JSON);
        RESTRequest1->Execute();

        if (RESTResponse1->StatusCode != 200)
            throw Exception(L"Error RESTResponse1.StatusCode = " +
                            IntToStr(RESTResponse1->StatusCode));

        // WAVファイル保存
        String wavFile = AOutputWavFile;
		LWavData = RESTResponse1->RawBytes;
        FS = new TFileStream(wavFile, fmCreate);
        try {
            if (RESTResponse1->RawBytes.Length > 0)
                FS->WriteBuffer(&LWavData[0], RESTResponse1->RawBytes.Length);
        } __finally
        {
            delete FS;
        }
    } __finally
    {
        delete LJsonObj;
    }
}

実行結果

コードを書き終わったら、ビルドを行います。
VOICEVOX アプリケーションが起動されていることを確認し、早速実行してみましょう! 『ずんだもん』があなたにご挨拶をしたはずです。

voicevoxappsample

さらなる活用に向けて

今回は、Delphi / C++Builder を使って『ずんだもん』におしゃべりをさせてみました。

VOICEVOX が提供する REST API を利用した処理は、本来ネットワーク処理やデータの受け渡しなど、煩雑になりがちな部分を含みます。しかし Delphi / C++Builder では、REST 関連コンポーネントをフォームに配置と僅かなコーディングで構築することができました。

こうした手軽さは、Delphi / C++Builder の特徴をよく表していると言えるでしょう。

今回は非常にシンプルな活用例をご紹介しましたが、これを応用することで、アプリケーションの音声ガイダンスを実際の業務シーンに組み込めることも想像していただけるのではないでしょうか。
例えば次のようなシステムが挙げられます:

  •  店舗やオフィスの無人来客受付システム (例:「〇〇様ほか 3 名様の受付が完了しました。係員が参りますこのままお待ちください。」)
  •  設備管理システムの警報 (例:「設備番号 10 番を点検してください」)

これまで単純なアラーム音やテキストで行っていた通知を音声ガイダンスを主役にすることで、通知の意味をより明確に伝えることが可能になります。
一方で、「店舗はまだしも、施設管理など緊迫した場面の警報に『ずんだもん』の声は適さないのでは?」と感じる方もいるかもしれません。
そのような場合には、
VOICEVOX Nemoが選択肢となります。VOICEVOX Nemo では、さまざまな利用シーンを想定した汎用性の高い音声を選択することができます。
さらに、VCL デスクトップアプリケーションにとどまらず、FireMonkey を用いることで、iPad や Android タブレットで使用できるモバイルアプリケーションでも表現の幅を広げることができます。

参考

 

 

 

Kai 1.1 Now Available! Kai 1.1 Now Available! What's Coming in RAD Studio 13.2 Florence

Reduce development time and get to market faster with RAD Studio, Delphi, or C++Builder.
Design. Code. Compile. Deploy.

Start Free Trial   Upgrade Today

   Free Delphi Community Edition   Free C++Builder Community Edition

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

IN THE ARTICLES