PPTXファイルを直接書き換えて音声や字幕を付加

プログラミング

PPTXファイルに音声や文字のナレーションを埋め込むWindowsPC用ソフトを作っています。コーディングに時間が取れなかったこともあって、前回の投稿からだいぶ間が空いてしまいました。でも開発は「遅々として進む」という状況で、幾つかの機能を付け加えてきました。この数週間での大きな成果(と自分で思っていること)は「クリックと連動する字幕を表示できるようになった」ことです。

作ったスライドショーの例

このソフトで字幕を付けたPowerpointスライドショーの例を以下に示します。自分で動かしてみてください。表示画面下の矢印ボタンのクリック、スライド画面内にポインタを置いた状態でのマウスクリック,あるいは矢印キーなどでスライドショーが進行します。

字幕をつけたスライドショーの例

実際の講義でも,この例と同様に数式の変形過程などを細かなステップに分け,何回もクリックしてアニメで表示し,口頭で説明を加えています。このため,このPPTスライドショーを音声や字幕が付いていないままweb用に変換しても,どういう意図で細かくステップ分けしているかを分かっていただけない可能性があります

<注を読む>

なお,上のPPTスライドショーは,iSpring社のPPTアドインであるiSpring Suite 10を使ってHTML5ファイルに変換しています。このソフトは有料ですが,同様のことをするフリーのアドインや,PPTファイルを変換してくれるフリーのwebサイトもあります。

<注を閉じる>

このソフトウェアでできること

  • 音声ナレーションと字幕をスライドショーに付加
    PPTXのスライドショーに音声や字幕によるナレーションを自動で(つまりPPTのGUI操作をしないで)付加できる。また「かんたん!Aiトーク5」を制御して、テキストから自動的にオーディオデータを作ることができる。
  • スクリプトにより自動実行
    付加される音声ナレーションや字幕は、スライドの進行やアニメを制御するマウスクリックなどと同期している。この動作は、スライド切替え、マウスクリック、ナレーションの内容をテキストで表して順番に並べた「スクリプト(台本)」に従って作られる。 
  • サポート機能
    スクリプトの作成や、オーディオデータの確認と再編集をサポートする機能などが実装されている。

字幕を付けることの意義

もともとは,私自身の講義の記録(アーカイブ)と,学生さんの自習に使うことを考えてソフトを作り始めました。そのときは,字幕は「おまけ」の機能と考えていたのです。講義でスライドショーを見せる場合は,教員による口頭での説明があるので字幕は不要です。自習の場合も字幕は邪魔かなと考えていました。スライドは視覚により情報を伝えています。字幕という視覚による情報伝達がさらに加わると,「見て読んで理解する」という視覚による認知の負担が増えてしまいます。それで,あまり教育の現場での需要は少ないかも,と考えました。

<注を読む>

字幕のような視覚情報が増えても学習の効率は低下しない,と主張する人もいます(星 友啓:脳を活かすスマホ術 スタンフォード哲学博士が教える知的活用法、朝日新聞出版(2023))。でも、多様な学生を対象とした調査結果を見てからでないと、判断は難しいと思います。

<注を閉じる>

しかし,字幕だけで済むならファイルの容量を減らせます。また,聴覚が弱かったり、口頭での説明の理解に時間がかかったりする学生さんには助けになるでしょう。web上で閲覧できるスライドショーなら学習する人が自分のペースに合わせた進行や繰り返しができます。ですから視覚的負担の増加はそれほど問題にならないと考えられます。

スライドショーに字幕を付けることの効用や弊害,どのような使い方が効果的か,などは今後の研究課題だと思います(既に研究されているかもしれませんが)。簡単に字幕を付けられるソフトがあれば,そのような研究の助けになると思います。

技術的な内容の説明に移るの前に,背景となっている事情について説明します。過去の投稿とダブる部分もあります。興味のある方は目をとおしてください。

<詳しく読む>

技術的には・・・PPTXファイルを直接書き換えている

GUIでもできることをしている

まず,はじめに申し上げておきます。開発しているソフトは,これまで技術的に不可能だったことを可能にするようなものではありません。

アニメを起動するマウスクリックにオーディオの効果を付けたり,テキストボックスで作った字幕を出現させたり消したりすることは,全てPowerpointの標準的なGUIを使ってできてしまいます。しかし実際問題として,少しナレーションの数が増えるとGUIを使った作業は非常に時間がかかるものになります。字幕の作成も,正確な位置にテキストボックスを作成し,文字を入れ,出現と消滅のアニメの効果を付ける,ということを繰り返すので,字幕の数が少なくても大変です。

自動化ソフトでも問題あり

それでは,ソフトウェアを自動で操作するRPAツールやPythonなどで作ったGUI信号生成ソフトを使う方法はどうでしょうか? 開発しているアプリの音声ナレーションを合成したり追加する部分は、AHK(AutoHotKey)スクリプトやPythonコードによるGUI制御により作っていました。しかし動作が遅いのです。オーディオデータが揃った状態で「用意ドン!」すれば,物凄く手の速い人には負けるんじゃないかな,という感じでした。

また,動かすたびにタイミングが微妙にずれるため動作が不安定で,そのための誤動作もありました。Pythonコードにしたことで改善されましたが、それでも時々誤動作します(“時々”というのが厄介です)。一番問題なのは,動かしている間はPCを使った他の作業ができないということです(これはちゃんとしたRPAツールなら解決されている問題かもしれません)。

PPTファイルの直接操作

そこで,PPTのファイルを直接書き直す方法を採用することにしました。Pythonのライブラリにpython-pptxというものがあり,これを使うとPPTXファイルの操作ができそうです。それで大いに期待したのですが,アニメの操作が可能かどうかがよくわかりません(たぶん私の調べ方が下手なせいだと思います)。

PPTXファイルの中身は複数のxml(Extensible Markup Language)ファイルやデータファイルをまとめてzip圧縮した形になっています。したがって,解凍すればpython-pptxを使わなくてもxmlファイルの書き直しやデータの追加が可能になります。

開発中のソフトは,基本的にはzip展開されたPPTXファイルの中身を直接書き換えたりデータファイルを追加することで音声や字幕の追加を実行しています。ほとんどがテキスト操作になります。

もちろん,ファイルの書き換えなので,圧縮してPPTXファイルにしてみたらPPTアプリで開けなくなっているという危険性があります。書き換えた結果がxmlの規則に反している可能性もあります。実際,アプリの動作確認をしている段階では,出来上がったPPTXを開こうとしても「壊れているので修復しますか?」といったメッセージが出され,修復して開いても,意図した動作はしないというケースが多くありました。(ごく最近も思わぬ原因で誤動作することが分かりました。)

そこで,このアプリでは,元のPPTXファイルはそのままにして手を付けないようにしています。zip展開したフォルダ内のファイルだけを操作し,最後に圧縮して別の名前のPPTXファイルを作るようにしています。

<閉じる>

字幕付加の処理の全体の流れ

字幕を付加するソフトは,PPTXファイルをzip展開した中にあるslide1.xml,slide2.xml・・・などのxmlファイルを直接書き換えています。処理の全体を理解するため,まず,次のスライドショーをごらんになってください。

アプリは,大きく分けると以下のことをしています。

  • テキストボックス用のshapeの生成
    字幕はテキストボックスを使って表示します。テキストボックスはPPTスライド上の図形であるshapeの1つです。スライド毎にslide1.xml,slide2.xml,・・・という名前で作られたxmlファイルの中のShape Treeと呼ばれる構造の中に以下のようなxmlコードのブロックを挿入していきます。
  • アニメの記述を<p:timing>要素に追加
    スライド画面上にshapeを出現(英語版のPPTでは“enter”のようです)させたり消したり(“exit”)させるアニメのタイミングや表現方法について<p:timing>~</p:timing>の間で記述します。
  • 必要ならばスライドの寸法を変更する
    字幕用のテキストボックスはスライド画面の一番下に配置しています。元々ある図形や文字が字幕に隠されないように,スライド画面の寸法を下の方向に増やしてそこに字幕を配置するモードも用意しました。この操作はslide.xmlではなく別のxmlファイル(presentation.xml)を書き換えることになります。

字幕用shapeの生成と挿入

図形(shape)の形や位置の情報は,<p:spTree>要素の中にある<p:sp>~</p:sp>の間で記述される<p:sp>要素で表されます。実際に挿入される字幕用テキストボックスの<p:sp>要素の例は,以下のようになります。

      <p:sp>
        <p:nvSpPr>
          <p:cNvPr id="9" name="Caption 1">
            <a:extLst>
              <a:ext uri="{FF2B5EF4-FFF2-40B4-BE49-F238E27FC236}">
                <a16:creationId xmlns:a16="http://schemas.microsoft.com/office/drawing/2014/main" id="{DF488853-4B8B-58A9-2718-B1BC0D4C3A87}" />
              </a:ext>
            </a:extLst>
          </p:cNvPr>
          <p:cNvSpPr txBox="1" />
          <p:nvPr />
        </p:nvSpPr>
        <p:spPr>
          <a:xfrm>
            <a:off x="0" y="6172200" />
            <a:ext cx="9144000" cy="685800" />
          </a:xfrm>
          <a:prstGeom prst="rect">
            <a:avLst />
          </a:prstGeom>
          <a:solidFill>
            <a:srgbClr val="FFFFCC" />
          </a:solidFill>
          <a:ln>
            <a:solidFill>
              <a:schemeClr val="tx1">
                <a:lumMod val="50000" />
                <a:lumOff val="50000" />
              </a:schemeClr>
            </a:solidFill>
          </a:ln>
        </p:spPr>
        <p:txBody>
          <a:bodyPr wrap="square" rtlCol="0">
            <a:spAutoFit />
          </a:bodyPr>
          <a:lstStyle />
          <a:p>
            <a:r>
              <a:rPr lang="ja-JP" altLang="en-US" sz="1600" b="1" dirty="0" />
              <a:t>クリック1です。</a:t>
            </a:r>
          </a:p>
        </p:txBody>
      </p:sp>

Shape Treeに挿入されたコードの例

字幕用shapeの生成

朱書きされた部分をアプリが書き換えてshapeを生成します。以下,上から順に説明していきます。

  • i<p:cNvPr id=”9
    <p:cNvPr・・・からnon-visual properties要素の記述になります。“id =”の次の数字がshapeの識別番号になります。異なるshapeには異なる数字が割り当てられます。この数字はPPTの編集でshapeを作るたびに1ずつ増加して割り当てているようです。
  • name=”Caption 1
    shapeの名前です。日本語版のPPTではテキストボックスには“テキストボックス1”のような全角文字列で名前が割り当てられています。開発中のアプリでは“Caption”+“数字”という半角文字列で名前を割り当てています。数字のない“Caption”だけにしても問題なく動いていますが,後で手動で調整する際,字幕ごとに名前が異なっていた方が良いかもしれないので,異なる番号を割り当てるようにしています。
  • <a:off x=”0” y=”6172200” />
    字幕の位置です。なお,位置や寸法の単位はEMU(English Metric Unit)と呼ばれるものになっています。1インチ=914400EMU,1cm=360000EMU,文字の大きさの単位の1pt=12700EMUと,全て整数のEMU値になります。
  • <a:ext cx=”9144000” cy=”685800” />
    shapeのx方向,y方向の拡がり(extension)つまり寸法です。この例では,横幅がPPTの4×3スクリーンでの10インチ幅(9144000EMU)と同じになっています。
  • <a:srgbClr val=”FFFFCC” />
    テキストボックスの色です。24bitのRGBで指定します。この例では薄い黄色になります。その他,テキストボックスの枠線の色や幅も変更可能ですが,アプリのコードでは固定しています。
  • <a:t>クリック1です。</a:t>
    朱書きの文字が字幕として表示する文字列になります。その前の行の,
    <a:rPr lang=”ja-JP” altLang=”en-US” sz=”1600″ b=”1″ dirty=”0″ />
    で全角/半角の切り替えをしています。

Shape Treeへの挿入

必要な書き換えを行った<p:sp>要素をShape Treeに追加していきます。方法は簡単です。Shape Treeの末尾である</p:spTree>を見つけ,その直前に<p:sp>要素を挿入します。shapeの識別番号,Shape IDは,Shape Tree内を走査して見つかったshape ID値の最大値を求め,それに1を加えた値を新しいshapeに割り当てています。

<p:timing>要素の書き換えと挿入

作った字幕は,そのままではスライド下部の字幕領域に重なった状態で表示されています。これにクリックに同期して出現したり消えたりするアニメ効果を与える必要があります。このための<p:timing>要素の書き換えは,shapeの追加よりはちょっと面倒臭い操作になります。

<p:timing>要素の書き換え

以下は,字幕を表示させるために<p:timing>要素に追加されたコードブロックの例です。

                              <p:par>
                                <p:cTn id="5" presetID="1" presetClass="entr" presetSubtype="0" fill="hold" grpId="0" nodeType="withEffect">
                                  <p:stCondLst>
                                    <p:cond delay="0" />
                                  </p:stCondLst>
                                  <p:childTnLst>
                                    <p:set>
                                      <p:cBhvr>
                                        <p:cTn id="6" dur="1" fill="hold">
                                          <p:stCondLst>
                                            <p:cond delay="0" />
                                          </p:stCondLst>
                                        </p:cTn>
                                        <p:tgtEl>
                                          <p:spTgt spid="8" />
                                        </p:tgtEl>
                                        <p:attrNameLst>
                                          <p:attrName>style.visibility</p:attrName>
                                        </p:attrNameLst>
                                      </p:cBhvr>
                                      <p:to>
                                        <p:strVal val="visible" />
                                      </p:to>
                                    </p:set>
                                  </p:childTnLst>
                                </p:cTn>
                              </p:par>
                            </p:childTnLst>
                          </p:cTn>
                        </p:par>

<p:timing>要素に挿入されたコードの例

朱文字はアプリが書き換える文字列,緑文字は注目してほしい箇所です。<p:par>~</p:par>で1つの単位となります。

  • <p:cTn id=”5
    挿入されるTime nodeの内容をここから記述していきます。項目ごとに識別番号idが割り当てられます。この番号は,<p:timing>要素の一番上から1で始まり,順番に1つずつ増えていきます。
  • presetClass=”entr
    図形を出現(entry,だと思います)させる効果を指定しています。PPTアプリ(日本語版)では“開始”というアニメーションのクラスに属していることを示します。“開始”クラスのアニメの効果にはフェードインなど様々なパターンがありますが,図形全体が一度に出現するシンプルな“表示”にしています。字幕を消すときは,この文字列”entr“を”exit“にします。
  • nodeType=”withEffect
    直前のクリックと同時に字幕が表示されるようにしています。
  • <p:cTn id=”6
    挿入するコードブロックの中には2つの<p:cTn>要素があるため,idを指定する箇所も2つあります。
  • <p:spTgt spid=”8” />
    コードブロックで記述している効果を与えるshapeを,その識別番号(Shape ID,spid)で指定しています。この例ではspidが”8″であるshapeを対象としています。

コードブロックの<p:timing>要素への挿入

パラメータを書き換えたコードブロックを<p:timing>要素に挿入します。手順は以下のようにしています。

ⅰ)<p:timing>~</p:timing>内でアニメを起動するクリックを見つける
 文字列“clickEffect”を見つけ,スクリプトに載っている番号のものを選ぶ。
ⅱ)“clickEffect”の後で最初に見つけた</p:par>の直後に字幕表示開始のブロックを挿入
 </p:par>がこのクリックで起動されるアニメ効果の記述の終わりになるので,その直後に字幕開始のコードブロックを挿入する。
ⅲ)次のクリックを見つけ,その後ろに字幕終了のブロックを挿入
 これで,次のクリックにより字幕の表示が終わります。このクリックが新しいアニメを起動するものになっていれば,同時に次の字幕が現れます。
ⅳ)最後に<p:timing>要素内のcTn idを割り当て直す
 Shape Treeとは異なり,<p:timing>要素内のcTn idは昇順にする必要があるので,スライド内の全ての字幕のコードの挿入が終わったところで,cTn idの再割り当てをする。

なお,ⅲ)の字幕終了ブロックの挿入については,次のクリックが見つからない場合はスライドの最後に達したことになります。ここでPPTアプリの操作者がマウスをクリックすると,次のスライドに移ります。同時に字幕も消えますので,スライドの一番最後の字幕に関しては消すためのコードブロックは入れないようにしています。クリックで最後の字幕を消し,次のクリックでスライドが切り替わるようにしても良いのですが,ただでさえ多いクリック回数を増やさないようにしました。

とりあえずここまでにしておきます。ソースコードについても忘れないうちに投稿したいです(そもそもこのwebサイト自体,私の備忘録として作ったものです)。

でも,コードがどんどんスパゲッティ化していて,とてもじゃないけど人様の目に耐えられないものになってきています。メンテナンス性も悪く,久しぶりにコーディングに取り掛かると,頭が復帰するのにすごく時間がかかってしまいます。小さなコードから紹介をしていくことになると思います。

コメント