PDFスライドに音声をつけて公開するためのWEBアプリを作りました
みなさん、こんにちは。情報システム学専攻の酒井です。2020年8月にこの文章を書いてます。大学教員は皆そうだと思うんですけど、この春は対面の授業ができなくてオンライン教材の作成に追われたんじゃないかと思います。紆余曲折を経て、PDFスライドに音声をつけた教材により講義を提供することに落ち着いたので、その顛末とその際に作ったプログラムの情報を、玉手箱に残すことにしました。
オンライン講義の形式
まず、多様な環境で受講可能なことを、最重要事項に設定しました。このことから、高速・大容量のネット環境がないと辛いライブ配信や講義ビデオは除外となり、音声つきのスライドが候補になります。次に、音声つきのスライドのうち、検討したけれど採用しなかったのは以下の2つの候補です。
- パワーポイントは候補から除外。音声は組み込めるけど、ユーザが特定のソフトを使わないといけないから。それに、パワーポイント使ってスライドを作りたいとも思わないですし。
- ググっていたら、PDFにも音声がつけられるとの情報あり。でもAcrobat readerでないと再生できないと判明。
なにぶん時間的な余裕がなかったので、いままで授業に使っていたLaTeXで作ったPDFスライドをできるだけ再利用して、それに音声をつけて公開する、という方針で走り始めました。この4月に講義を開始してから、以下のようにスライドの形式を変えて行きました。いずれも、スライドのページ毎にmp3形式の音声を準備しておいて、それらのファイルをWEBサーバに置く方式です。自分で研究室のWEBサーバを管理していたので、この辺は楽ちんでした。
- 形式1:PDFの決まった位置に音声ファイルへの外部URLを組み込んでおく形式。各ページのスライドの内容にかかわらず、音声リンクを定位置にストレスなく作成するために、少しだけLaTeXマクロを書いた。
- 形式2:スライド形式のWEBコンテンツを作成。スライドはマークダウン形式で作成。WEBコンテンツへの変換には Marp を使った。ただし、Marpにも音声リンクの機能はないので、音声リンクを定位置に設置するためのHTML記述を手間なく組み込むためにプリプロセッサを用意した。
- 形式3:PDFスライドには細工せずに、PDFスライドを音声コントロールを持つWEBコンテンツとして表示するためのアプリをJavaScriptで書いた。PDF.js (https://mozilla.github.io/pdf.js/) ライブラリを使用した。
形式1とその問題点
スライドのPDFに、図1の右上にあるような音声リンク追加しました。(技術的な説明は付録を見てね。) 音声ファイルはWEB上にあるので、スライドを一旦ダウンロードしてから一般のPDFビューワを使ってもいいし、スライドをブラウザで直接開いてもいい。けれど以下の欠点が判明。知らせてくれた学生さんありがと。
- ある環境でブラウザで直接開いた場合に、スピーカボタンを押すと音声再生のために別の画面に移ってしまう。スライドの該当ページを見るために、ブラウザの「戻るボタン」でスライドに戻ると、スライドの最初のページが表示されてしまい、該当ページに移動するのが大変。
このような事態に直面する学生さんは稀だと思うんだけど、当事者にとってはかぎりなく悲惨です。そもそもブラウザに組み込みのPDF表示ツールは規格の統一があまりされていなくて、また、音声リンクをクリックした時の挙動はスライド作成者側では制御しようがない。そこで形式1はすぐにあきらめ、こちらで殆どの挙動を制御できるようにWEBコンテンツを作ってしまえということで、形式2を試みることにしました。
形式2とその問題点
スライドをWEBに掲載する方法について調べていたら、マークダウンでスライドを書いてそれをWEBコンテンツ(html)形式に変換してくれるソフトMarpがあることが分かりました。そのデモスライドを見てみると、ページ移動などはどのブラウザでも同じように動くし、少しならLaTeXの数式も書ける。なので、これに音声再生機能を追加してやれば良い感じでした。図2は、実際に自分でマークダウンで書いたスライドで、それに頑張って右上に音声コントロールをつけたものです。(技術的な説明は付録を見てね。) どのブラウザでもほぼ同じように見えるし動作する、というわけで初回の講義開始早々だったこともあり、それまでのスライドを形式2に書き換え、結局第3回の講義まではこの形式で突き進みました。
第3回の資料作成中に面倒な問題に気づきました(というか、Marpを使い始めた段階で気づいていたんだけど、講義の準備は進めなきゃいけないし、形式3の準備にどうしても時間がかかったという訳)。それは、「図の回り込み」と「項目化やブロック」が同時には使えず、図3のようなページすらめっちゃ書きにくいという事実です。図3では、htmlの<div>を駆使して無理やり縦割りにして書いていますが、面倒なばかりかこの程度の仕上がりです。図が自由に置けないのは致命的。結局、オーディオコントロールも右寄せにできませんでしたし、また、htmlで直接スライドを書く元気や時間もない。。。
形式3
結局、普通に作られたPDF形式のスライドと必要なページの解説音声ファイルから、WEBページを構成するアプリを作ることにしました。調べたところ PDF.js っていうJavascriptライブラリがあって、Chromeブラウザの内部でもPDFを見せるための仕組みに使われているらしいことが分かり、それを使って作ることにしました。作成したアプリのスライド提供側としての使い方はもう少し下の節をご覧ください。
ユーザの使いやすさのため、以下の点に注意しました。
- Chromeなどのブラウザ組み込みのPDF表示機能で形式1のスライドを利用したときの不満を解消すること。具体的には、以下の機能を提供しました。
- ウィンドウのリサイズに合わせて自動拡大する仕組み。
- ページ移動にクリック以外にスワイプや矢印キーなどが使えること。
- ブラウザのリロードボタンを押したり、他のページから戻るボタンにより戻って来た際に、最後に表示していたページをちゃんと表示してくれること。
- 音声のあるページのみに音声コントロールを表示すること。
- スマホでも受講可能なこと。
- (あたりまえだけど)無駄なデータ転送をしないこと。
- 外部リンクを別タブで開くなど、当然必要な機能を持つこと。(ただし、PDFのスライド内リンクへの対応は8月になってから実装した。)
スマホやタブレットでの受講者のためにスワイプでページ移動ができるようにしたのですが、当初、二本指での拡大縮小は禁止したつもりでした。Androidでは動作確認してあったんですけど、どうやらiPhoneなどでは拡大縮小の禁止がうまく働いていなかったようです。それだけなら良いのですが拡大縮小の際に隣のページに移動してしまうらしいのです。情報を寄せてくださった学生さん、先生方ありがとう。1.0.1版からタッチパネルでの拡大縮小にも対応してあります。
なお、以下は現時点で未実装です。
- PDFスライド内の文章のコピーのための選択機能。(PDF.jsライブラリには対応する仕組みはあるので、実装するだけといえばそれまでだけど、意外と面倒)
- アニメーションへの対応。これは問題点の整理すらしていない状況。
- PDFに埋め込まれたリンクに、対応していない形式がまだ残っていそう。
- 形式1のPDFファイルも使いやすく表示すること。
最後の項目ですが、本WEBアプリを使って形式1のPDFスライドを表示させた場合には、スピーカーマーク(音声ボタン)は動作しません。これを単にブラウザで開くようにすることもできるはずですがそうせずに、このスピーカーマークにカーソルを合わせるたときに音声コントロールが現れるようにすると、使いやすいだけでなく一枚のスライドに複数の音声が貼り付けられることになるので便利そうです。
音声ファイルの作成について
音声の録音と、そのmp3形式への変換には、Audacityを使いました。よくできたソフトですし、Ubuntuでもパッケージからインストール可能です。モノラルトラックで収録して、mp3の圧縮を目一杯かけると、だいたい一分間あたりのファイルサイズはせいぜい250KBと十分小さくなります。
最初の頃は時間がなくて一発録音で作ったウダウダの説明をそのまま使ったため、受講者の皆さんにはきっと迷惑をかけたのではないかと思います。時間的な余裕の増加に合わせてだんだんと、編集により結構まともな解説音声を作ることができました。ただ納得がいくものを作るには、1ページあたり30分ぐらいかかりますけどね。最近、気合を入れて作った音声を聞き直してみたんですが、分かりやすくてよくできていました(自画自賛ですが)。気になる箇所を何度でも聞き返せるし、普通に講義するよりわかりやすいかも。
普通のマイクだと、雨音とかの環境音が入ることが分かりましたが、途中からゲーム用のヘッドセット(AsusのROG STRIX GO 2.4)を手に入れて解決しました。GPGPU向きグラフィックカードなどもそうなんですが、ゲーム用はコストパフォーマンス最高ですね。近くで掃除機かけていても分からないぐらい。ただ、微妙に喋り出しが切れるのが難点かな、気をつけて聞かないとわからないレベルですが。あとはマイクの位置は重要で、口の位置かそれより上でないと音がこもってしまうことが分かりました。また、息からくる雑音を防ぐために、手近にあった台所スポンジでマイクフードを作りました。色が変だけど見せるわけじゃないから無問題。
解説音声の作成で一番気をつけたのは、スライドの該当ページ中のどの場所をみながら話を聞いたらよいか分かるようにすること。ポインタで指すことは原理的に不可能なので、わかりづらそうな場所にはスライドの必要な場所に色を付けたり、番号をつけたりして、それを見るように音声で誘導するようにしました。特にこれまでの授業で板書しながら説明していた部分は(もちろんスライドに追加しましたが)、ちょっと苦労しました。板書とスライドを見比べながら説明していた箇所とかですね。そんなポイントは往々にして理解が難しい箇所ですし。
音声は原稿なしで録音後、編集する方法に落ち着きました。基本的に録音中におかしいと思ったら何度でも言い直しておいて、あとで不要な部分をカットする方法で作成。どうしても必要な場合にはあとから追加録音して部分的に置き換えました。うまくつながるように録音し直すためには、考えなきゃいけないことが一杯あって相当疲れます。また、やってみて分かったことは、人の声って喋り始めてから声質が安定するまで結構時間がかかるってことです。声の高さは合わせられるけど声質が違って微妙に繋がりません。こんなことやっているからスライド一枚30分もかかるんですね。理解が難しい箇所でスライドをさらに工夫し始めるとスライド数枚分にたいしても、すぐ半日かかりました。でも原稿をつくってから収録するよりは分かりやすい内容になるし、結局このほうが速いと思います。
一コマ(90分)だけ部分的に担当する講義のために、別の講義用に作った教材を再利用する機会がありました。その際に、自分で収録した説明音声から文字起こしをしてからの再録音を試してみました。文字起こしにはVrewという動画の音声に字幕をつけるソフトを利用。Ubuntuでも動くし、mp3音声を入力しても文字起こしができてtxt形式でダウンロードできる。正しく認識されていない箇所を修正してから、さらに原稿を納得がいくまで推敲して録音しました。これならわかりやすい喋り方に集中できるので疲れないし、ほぼ音声編集なしで仕上がりました。来年の授業では、このやり方でより良い教材を作成しようと心に誓うのでした。(実際はどうなるかわからないけど)
スライド提供側の使い方
スライド提供側としては、pdf-slide-viewer.php(以下ではPHPファイルと呼びます)、および、表示したいPDF形式のスライド、ページごとの音声ファイルを準備して同一のWEBサーバに置くことが必要です。ページ毎の音声ファイルはファイル名にいれるページ番号で区別されます。PHPファイルはソースサイトからダウンロードするか、あるいは、pdf-slide-viewer.php を右クリックでファイル保存して下さい。PHPファイルはWEBサーバに1つあれば十分ですが、別のディレクトリに複数あっても構いません。
一つの講義に対して、WEBサイトに一つのディレクトリを用意して、PHPファイルと各回の講義スライドPDFと音声ファイルを全部置いて、インデックスページから各スライドにアクセスするような使い方が便利と思います。
具体的な使い方は最初に示したデモ https://www.trs.css.i.nagoya-u.ac.jp/js/show-pdf-slides/pdf-slide-viewer.php?pdf=demo.pdf&audioBaseFile=audio/sound-&audioSuffix=mp3を見ていただければ分かると思います。
- デモの場合には、PHPファイルと同じディレクトリにスライドファイルのdemo.pdfが置いてあるが、同一のWEBサーバであれば問題ない。pdfファイルは、WEB上での絶対パスか、PHPファイルの置き場所からの相対パス付きで指定できる。
- デモの3ページ目の音楽ファイルはPHPファイルの置き場所から相対的にaudio/sound-3.mp3というパスと名前で置いてある。またmp3がデフォルトなので、&audioSuffix=map3は省略可能。
- 99ページより後のページにはそのままではオーディオコントロールが表示されないが、例えば &pageMax=200 とすることで200ページまでのスライドに対応するようにできる。
- &docTitle=DEMO のようにすることで、ブラウザのタブのところに表示される文字を設定できる(さっき実装したばかりなので、1.0.2版以降で使える)
これらのパラメタ名を間違えてもHTMLの仕様上、一見何も文句を言われません。しかし、例えばChromeブラウザならCtrl-Shift-i でデバッグモードに入って、その console にメッセージが表示されます。
PHPに非対応のWEBサーバの場合には、pdf-slide-viewer.phpの代わりにpdf-slide-viewer.htmlが使えます。ただし、音声ファイルがないページにもオーディオコントロールが現れるようになります(ブラウザによって動作は異なるかもしれません)。
プログラム作成上の雑感など
どのようなプログラミング言語も基本概念に大差ないとはいうものの、実のところJavascriptどころかクライアントサイドのWEB系プログラムは全く初めてだったので、「Javascript特有の常識」みたいなのが分かるまでは苦労しました。このことに加えて、PDF.js ライブラリにはほとんどドキュメントがなく、それなりに苦労しました。結局、PDF.js のインタフェースに近い部分(api.js)を中心にプログラムを読むことになりました。すこしだけ印象の深かった出来事をいくつか。
- ご存知のように複数のファイルを使ってのプログラミング開発は普通のこと。ただIEなどの古めのブラウザだとimportとかでネストして読み込めないんで、単純にはすべての必要なファイルをhtmlから読み込むことになる。それでは大変なので、必要なライブラリをrequireで書いて、browserifyで一つのファイルにまとめたりします。必要なライブラリを動的に読み込むことができたりして賢いのだけれど、最初、この仕組みで作られたことを知らずに、そんな出力のPDF.jsを読んでた!! 人が読み書きできるようなものではないなと感じてはいたんだけどね。
- Chromeブラウザに(他のブラウザでも似たようなものらしいが)組み込みのデバッグ環境がとても良くできていて、大変お世話になった。デバッグしたいwebページを表示して、Ctrl-Shift-i を押すとこのモードに入る。console.log(変数)と書くと変数の中身が複雑な構造をしていても賢く表示してくれるとか、普通にブレークポイントを設定して、ステップ実行するとか。なお、PDF.jsではpromiseという機能を使ってロード待ちの間に他の実行をしたりしているのだけれど、これも問題なくデバッグできたのは助かった。あと地味だけど、スマホのタッチ機能のエミュレート機能とかも。
- ページ毎の音声ファイルはファイル名にいれる番号で区別されるが、WEBの仕様上、クライアントサイドではそのページにファイルがあるかどうかはダウンロードをしてエラーになるかをチェックするしかない。(サーバのファイル一覧がそんなに簡単に手に入ったらセキュリティがボロボロと言えるかも。) しかたなく、音声ファイルがあるかどうかだけは、馴染みのあるPHPを使ってサーバサイドで実現した。さらに、音声のないページにも(動作しない)音声コントロールが表示されることを許せば、サーバサイドプログラムなしでも動かせるように、該当部分を除外したものも用意した。まあ大した事をしているわけではないのでPHPで書かれた部分を他の方法(perl, python, rubyなど)に書き換えるのも難しくないはず。
GitLabサーバについて
最近は講義資料もGitというバージョン管理をして、それをGitLabに置いてそのリモート環境での管理を容易にしてます。実際に研究室でもGitLabサーバを立ち上げて使っていますが、研究室のWEBサーバとは直接連携させていません。時間があったらやりたいこととして、GitLab Pagesをちゃんと動くようにして、教材の公開も含めた管理を容易にしたいなぁと考えたりします。
付録(形式1,2の技術的詳細)
形式1で音声リンクを決まった位置に置くためのLaTeXマクロは以下の通り。absoluteとoverlayパッケージが良く出来てると思った。
%%% 絶対位置指定 \usepackage[absolute,overlay]{textpos} %%% 音声リンク \newcommand{\audio}[1]{ \begin{textblock}{256px}(320pt, 0pt) \href{#1}{\includegraphics[width=40pt]{figs/speaker.png}} \end{textblock}}
なお、スピーカーアイコンはフリー素材のサイトから持ってきた。
形式2でのスライド作成は、環境づくりに敷居が高いかも。Marpはマークダウン形式をhtml形式に変換するコンパイラで、環境さえ構築してしまえば、シンプルなスライドであれば簡単に作成できる。環境づくりにはいくつかのサイト(例えばここ)のお世話になった。
肝心の音声ファイルの組み込みについては、Marpでは入力のマークダウンに含まれるhtml形式が基本的にそのまま出力されるという性質を持っているため、これを利用すればオーティオコントロールを組み込める、というわけ。
<audio controls src="audio/non-3-2.mp3"> Your browser does not support the <code>audio</code> element. </audio>
上の文を各ページにストレスなく置くために、C言語のプリプロセッサにも使われているらしいm4マクロプロセッサを利用した。初めて使ったんですが相当マニアック。なので説明はすみませんが割愛。また、オーディオコントロールが右上の定位置に来るように、ccsを次のように設定した。
audio { position: absolute; top: 1px; right: 1px; }