何謂 60 秒的戲劇?
就是用(大約)60 秒的時間來講述故事。建立基本的情境,引入一個沖突,非常快速地描繪景物,最後以某種方式解決沖突,結局完美。您可以使用錄制的聲音,但是在本例中,我要使用利用 TTS 引擎 Festival 產生的合成聲音。說出來的話會是平淡且不帶感情的,並且 — 有時候 — 難以理解,因為合成的聲音不是很完美。
下載 完整的樣例劇本,即一個音頻 .ogg 文件,然後繼續往下閱讀本文,了解劇本是如何生成的。在這裡,劇本不重要,重要的是如何編程實現劇本。我完全利用開源的免費軟件生成劇本,該軟件使用的是 Festival TTS 引擎和它提供的一些有特色的聲音。
播放劇本
播放帶有兩個文件的劇本:一個 XML 數據文件(包含劇本本身)和一個用 PHP 編寫的制作器腳本。XML 數據包含演員表、字幕和劇終感謝、一系列用作效果的文件,以及每個人物的台詞(對白)。根據 XML 數據文件中的指示(XML 文件使得創建不同的劇本或編輯當前劇本並用同一個制作器播放該劇本很容易),制作器將劇本呈現到音頻設備。
劇本的基本結構
一個劇本有很多幕,每一幕又分成幾場,每一場是一系列諸如噪音、音樂、對白和旁白之類的事件。在實際的劇場中,可以看到幕布在每一幕開始時被拉開,在結束時被合上,讓置景工可以為劇本的下一幕改變背景。幕有助於將劇本分成幾個部分,通常表示時間已過去或者到了另外一個地方。
在音頻劇本中,沒有幕布拉起時的那種生動的視覺效果。戲劇效果必須來自聲音或者旁白。
您可以提供一種聲音來表示幕布拉起和拉下,以提供一種可以聽到的標志。此外,您還可以宣讀劇本名稱和劇作者。在每一幕的開始,一個人會說: “第一幕。在古羅馬城鎮廣場的台階上。” 在劇本的末尾,這個人會滾動劇中感謝並提供聽眾需要的解釋(“Joe Blow 被判 70 年的牢獄……”)。有時,在劇本中間需要注解:比如說您聽到一聲耳光,並且此時只有兩個演員在場,那麼您有必要知道是誰打了誰。
劇中人物:演員表
演員表不要太長,否則每個人物在一分鐘內說不上什麼話。Festival 提供 9 種不同的聲音 — 一些男聲、一些女聲、一些年老的聲音、一些年輕的聲音 — 所以我們將演員限制為 9 個。也許劇本中有一個角色叫 Fred,您想用 Festival 聲音 voice_don_diphone 來代表 Fred。可以將 Fred 聲明為:
<role voice="voice_rab_diphone">Fred</role>
這裡,角色是 Fred,聲音是 XML 元素 role 的一個屬性。每次在劇本中出現 Fred,制作器都知道使用什麼聲音來講 Fred 的台詞。如果您決定為 Fred 使用另外一種聲音,只需要在一個地方更改屬性即可。
分配旁白員
旁白員是一個特殊而重要的人物。此聲音宣讀劇本和作者的名稱,注入解說詞,以及列出劇終感謝。所以此聲音將劇本緊緊綁定在一起。可以在演員表中將旁白員聲明為:
<role voice="voice_don_diphone">Narrator</role>
現在,每次旁白員說話時,制作器都使用一種不同於 Fred 的聲音。
音效和對白事件
劇本是一系列事件。隨著每個事件的出現,環境要麼變得更加清晰,要麼變得更加復雜。這裡的例子是兩個連續事件 —— 一個人物的對白後面緊接著一種音效:
<event type="effect" player="mplayer">gunshot.wav</event>
<event type="dialogue" player="Bozo">Freeze, turkey</event>
第一個事件具有屬性 effect,這表示它是一種聲音,不用 TTS 引擎呈現。第二個屬性表明您想要 Mplayer 來扮演此聲音,即文件 gunshot.wav。
第二個事件是人物 Bozo 說的對白,他說:“別動,沒用的東西。” 此環境中只有一個 TTS 引擎,所以不需要用屬性來指定使用哪個引擎。制作器總是使用同一個引擎。
在此結構中,事件作為 XML 元素只出現在場中;但是,您也可以在幕和場結構外面具有聲音和言語,比如旁白員的開場白、劇終感謝和歡呼。
幕和場
由於劇本包括很多幕,每一幕又分為幾場,所以基本的流結構應該類似於 清單 1。
清單 1. 幕、場和相關的事件
<act>
<curtainUp>KDE_Window_Shade_Up.ogg</curtainUp>
<scene>
<event type="dialogue" player="five">...</event>
<event type="dialogue" player="nine">...</event>
</scene>
<scene>
<event type="dialogue" player="five">...</event>
<event type="dialogue" player="two">...</event>
</scene>
<curtainDown>KDE_Window_Shade_Down.ogg</curtainDown>
</act>
這裡,只有一幕,分為兩場,每一場有兩個人的台詞,涉及到人物五、九和二 — 每個人物都有一種定義在演員表中的獨特聲音,前面已有解釋。在幕的開始和結尾播放的文件指示了幕布的拉起和放下。在我的例子中,我借用了一些來自 K Desktop Environment (KDE) 的系統聲音。
動態分配聲音
讓人物通過擴音器或耳麥說話的工作由制作器負責。制作器 PHP 代碼包含 清單 2 中所示的函數。
清單 2. 動態地調用聲音
function deliver($phrase,$voice) {
exec('festival -b \'(begin ('.$voice.')
(SayText "'.$phrase.'"))\' >/dev/null',$out);
}
在此函數中,參數是短語(要說的內容)和聲音(將用於呈現內容的 Festival 聲音)。exec 函數以批模式調用 Festival 來做兩件事:設置聲音和使用指定的聲音說出短語。begin 指示向 Festival 指出,這裡有幾件事情要做。
完整的劇本
清單 3 展示了一個完整的 XML 格式的簡單劇本數據文件。
清單 3. XML 格式的完整劇本數據文件
<?XML version="1.0" encoding='UTF-8'?>
<play>
<dramatisp>
<role voice="voice_don_diphone">muchi</role>
<role voice="voice_kal_diphone">dad</role>
<role voice="voice_rab_diphone">narra</role>
<role voice="voice_nitech_us_awb_arctic_hts">mscot</role>
<role voice="voice_nitech_us_bdl_arctic_hts">spare</role>
<role voice="voice_nitech_us_clb_arctic_hts">matron</role>
<role voice="voice_nitech_us_jmk_arctic_hts">fuzzy</role>
<role voice="voice_nitech_us_rms_arctic_hts">uncle</role>
<role voice="voice_nitech_us_slt_arctic_hts">filly</role>
</dramatisp>
<intro>
<music>chimes.ogg</music>
<theatre>Sixty second theatre with XML and Festival</theatre>
<title>Todays play - The demonstration effect</title>
</intro>
<act>
<curtainUp>KDE_Window_Shade_Up.ogg</curtainUp>
<scene>
<!-- event type="effect" player="mplayer">tmp.wav</event -->
<event type="dialogue" player="dad">The doctor is taking a long time</event>
<event type="dialogue" player="matron">Yes but it is worth the wait</event>
<event type="dialogue" player="dad">Looks like you broke your arm</event>
<event type="dialogue" player="dad">Did you have a bad fall</event>
<event type="dialogue" player="matron">Yes one of those silly falls</event>
<event type="dialogue" player="matron">Icy steps</event>
<event type="dialogue" player="dad">Could happen to anybody</event>
</scene>
<curtainDown>KDE_Window_Shade_Down.ogg</curtainDown>
</act>
<act>
<curtainUp>KDE_Window_Shade_Up.ogg</curtainUp>
<scene>
<!-- event type="effect" player="mplayer">tmp.wav</event -->
<event type="dialogue" player="dad">It is really cold out there</event>
<event type="dialogue" player="uncle">Yes the cold gives me chill blains</event>
<event type="dialogue" player="dad">Hands or feet</event>
<event type="dialogue" player="uncle">Both</event>
<event type="dialogue" player="dad">That is bad luck</event>
</scene>
<curtainDown>KDE_Window_Shade_Down.ogg</curtainDown>
</act>
<act>
<curtainUp>KDE_Window_Shade_Up.ogg</curtainUp>
<scene>
<!--event type="effect" player="mplayer">tmp.wav</event -->
<event type="dialogue" player="dad">Thats a bad cough</event>
<event type="dialogue" player="filly">Yes it hurts when I breathe</event>
<event type="dialogue" player="dad">I am sorry to hear that</event>
<event type="dialogue" player="filly">What is your ailment</event>
<event type="dialogue" player="dad">Oh I am not actually sick</event>
<event type="dialogue" player="dad">But I do not feel well unless I surround
myself with people who are a lot worse off</event>
</scene>
<curtainDown>KDE_Window_Shade_Down.ogg</curtainDown>
</act>
<end>
<musicEnd></musicEnd>
<credits>Thanks to Festival, PHP, Audacity and XML</credits>
</end>
<Applause></Applause>
</play>
在這個 XML 數據文件中,根元素是 <play>。除了數據文件中間的幕和場之外,劇本開頭的元素 <dramatisp> 中聲明了角色及其聲音;在簡介部分,旁白員或報幕員報出了劇本的名稱;在結尾部分,有音樂、劇終感謝,也許還有歡呼。
制作器
除了一些次要的方面之外,現在已經具備了播放劇本所需的所有部件。制作器腳本在簡介、幕、場和劇終之間迭代,在需要時,使用 Mplayer 或 Festival 按順序播放事件。清單 4 展示了整個制作器腳本,它被編寫為從命令行運行。
清單 4. 用 PHP 編寫的制作器腳本
<?PHP
// sixty second theatre player
echo "60 second theater player\n";
if ($argc < 2) die("No play specifIEd\n");
$playXML = $argv[1];
$xml = simplexml_load_file($playXML);
// load players' voices
$roles = $XML->dramatisp->role;
foreach ($roles as $rolevoice) {
$rolev["$rolevoice"] = $rolevoice['voice'];
}
$announcer = $rolev["narra"];
$timestart = time();
//
// now the introduction
//
play_effect($XML->intro->music,"mplayer");
deliver((string) $XML->intro->theatre,$announcer);
deliver((string) $XML->intro->title,$announcer);
//
// now the acts
//
$anum = 0;
$snum = 0;
foreach ($XML->act as $A) {
$anum++;
deliver("Act $anum",$announcer);
play_effect($A->curtainUp,"mplayer");
foreach ($A->scene as $s) {
$snum++;
//deliver("Scene $snum",$announcer);
play_effect("chimes1.ogg","mplayer");
$events = $s->event;
foreach ($events as $e) {
switch ($e['type']) {
case "effect":
$engine = $e['player'];
play_effect($e,$engine);
break;
case "dialogue":
$plyr = $e['player'];
// echo "Trying $e with $plyr\n";
deliver($e,$rolev["$plyr"]);
break;
default:
dIE("Invalid type");
break;
}
}
}
play_effect($A->curtainDown,"mplayer");
$snum = 0;
}
//
// end of the play
//
deliver($XML->end->credits,$announcer);
$timeend = time();
$length = $timeend - $timestart;
echo("Total length is $length seconds.\n");
//
// functions
//
function play_effect($effect,$engine) {
exec("$engine $effect",$out);
}
function deliver($phrase,$voice) {
// echo "$phrase with $voice\n";
exec('festival -b \'(begin ('.$voice.')
(SayText "'.$phrase.'"))\' >/dev/null',$out);
}
?>
在這個制作器文件中,您首先將劇本(即 XML 數據文件 myplay.xml)加載到內存,並將它聲明為一個 XML 對象。接下來,尋找演員表,並將它們加載到一個帶有各自聲音的數組中。然後,選擇將用於旁白員或報幕員的聲音,並記錄開始時間,以便在劇本運行完時可以知道它運行了多長時間。在劇本名稱宣布之後,馬上進入幕的循環,在每一幕中,又是場的循環,場中包含事件指示。
第一次排演
要向揚聲器播放劇本,啟動制作器並提供數據文件:
$ php producer.PHP myplay.XML
通過讓制作器將輸出輸送到一個錄制的文件,您也可以記錄劇本:
$ php producer.PHP myplay.XML | arecord (options) myplay.wav
然後您可以在一個諸如 Audacity®的音頻編輯器中編輯此文件,或者用一個諸如 sox 的音頻實用工具操縱它。
總結
盡管可以聽錄音帶了,但是該過程的結果還是可以改善的。例如:
讓一些對話淡入和淡出或者重疊聲音和對話,可能比較有幫助。盡管不能在劇本制作期間直接用制作器達到此目的,但是可以在後期利用諸如 Audacity 之類的工具做到。不過,越這樣改善劇本,劇本就變得越復雜。
對於緩慢的計算機來說,啟動和停止音頻引擎要花一兩秒時間,因而會在輸出的事件之間留下不必要的長長的靜音。這些長長的靜音在任何後期編輯中都是很有幫助的,因為它們清楚地顯示了事件之間的分隔,但是對於最終的版本,應該縮短它們,以改善最終作品的間隔。這可以利用 Audacity 的 Truncate silence 效果做到。注意,使用這種不偏心的靜音截去方法會刪除您插入的任何電影劇本節拍(故意的靜音),所以這樣的節拍最好在截去靜音之後再插入。
使用您喜歡的任何音效和各種不同的聲音,但是不要在如此短的時間內把事情搞得太復雜。使用諸如救護車汽笛或禮炮之類著名的音效是有幫助的,因為這些聲音本身蘊含了環境信息。
結束語
您當然可以使用關系數據庫而不是 XML 方法來存儲劇本及其事件。但是在本例中,XML 對於讀者和編輯來說更為清楚明了,因為它是平板文件形式。如果您不喜歡某句說出的對話台詞,可以進行快速的編輯,重新運行制作器,得到新的最終作品。
帶有平板而無感情的合成聲音的 60 秒戲劇是一種最基本的藝術形式。沒有訓練有素的演員的表演,聽眾體會到的是一種別樣的感覺。聽眾不得不自己去想象,補充一些細節,比如語氣和語調。但是也完全可以使用 XML 和 Festival 或其他 TTS 引擎產生富有感情的作品。
本文示例源代碼或素材下載