| audio | ||
| broadcast | ||
| chats | ||
| app.js | ||
| audioEngine.js | ||
| broadcastConfig.js | ||
| index.html | ||
| jidouhousou.code-workspace | ||
| README.md | ||
| styles.css | ||
架空鉄道 車内自動放送タブレット(Web Audio API)
使い方
index.htmlをブラウザで開きます- 本プロジェクトは ES Modules を使っているため、ローカルサーバ経由で開くのを推奨します
- 例:
python -m http.server 8000→http://localhost:8000/ - Windowsで
pythonが使えない場合:py -m http.server 8000 - Pythonが無い場合(Node.js):
npx --yes http-server . -p 8000 -c-1
- 例:
- 音源ファイル(WAV)を使う場合は、ローカルサーバ経由で開くのを推奨します(
file://直開きだとfetchが失敗するブラウザがあります)- 例:
python -m http.server 8000→http://localhost:8000/
- 例:
- 画面上部の「音声を有効化」を押します(ブラウザの仕様で、ユーザー操作後にしか音が出ません)
- 「駅名放送」を押すたびに、サービス設定の
timeline.itemsの順番で進みます - 左側の矢印(←/→)で、放送フェーズを「1手ずつ」戻す/進めることができます(例:
次は←まもなく←次は)→は履歴の先が無い場合、無音で1手進めます(状態だけ進める)
dep/arr/terminalを使って「次は / まもなく / 終点」を表現します(例:depを書かずにarrだけ書けば soon-only になります)- 放送中に止めたい場合は「停止」を押します
ルート+フェーズ一覧を1ファイルで管理する(サービス設定)
列車種別・行先などで「停車駅」や「フェーズ一覧(進行順)」が変わり得る前提として、サービス設定を 1つのJSONファイルで完結 させます。
- サービス設定(例):
broadcast/service_aoba_up_local.jsontimeline.items: 左の「放送フェーズ」一覧と、「駅名放送」ボタンで進む順番- 形式(推奨):
phaseId: フェーズID(数値/文字列どちらでも可。broadcast/phases/<phaseId>.jsonを読みに行きます)text: UI用の表示テキスト(例:宇名町発車後)station: 案内する駅名(nextStationNameへ渡す)stationPhase:dep|arr|terminal(読み上げ種別とUI強調用)doorSide(任意):none|left|right|both_left|both_right(このフェーズのデフォルト出口方向)
dep: 「次は、station。」arr: 「まもなく、stationです。」terminal: 「終点です。」
- 形式(推奨):
stationsは持ちません(タイムラインだけで進行します)routeName/carInfo/serviceInfoは画面上部の表示に反映されます
列車種別・行先で構成を切り替える(サービス)
列車種別・行先などで「駅リスト」や「フェーズ一覧(進行順)」が変わり得る前提として、サービス定義ファイルを用意できます。
- サービス一覧:
broadcast/services.jsonservices[]に、サービスIDと紐づくconfigFile(サービス設定ファイル)を列挙しますdefaultServiceIdは未指定時に選ばれるサービスです
ブラウザのURLで ?service=<id> を付けると、そのサービスの構成で起動します。
例:
http://localhost:8000/?service=aoba_up_local
※ broadcast/services.json が無い場合は、デフォルトとして broadcast/service_aoba_up_local.json を読みます。
音声が欠けたときの動作
- 音源は「候補のパーツ列」を上から順に試します(例:
次は、+駅名です(まとめ)→次は、+駅名+です。) - 候補内で一部パーツが見つからない/デコードできない場合、その候補は失敗として次の候補を試します
- すべて失敗した場合は、無音で進行します(テロップ表示のみ)
将来的に音源(WAV)を流す場合
- 音源ファイルを置くと自動でそちらを優先して再生します(失敗時は無音で進行)。
- 音源は「パーツ」単位で組み立て可能です(例:「次は、|霞大橋です。」や「霞大橋|霞大橋です」)。
- 命名ルールは audio/README.md を参照してください。
フェーズごとに「組み立て」をファイルで固定する
駅名放送(次は / まもなく / 終点)は、フェーズごとに「どのパーツ列で組み立てるか」を JSON ファイルに記録し、実行時に読み出して再生できます。
仕組み
- フェーズ定義:
broadcast/phases/<phaseId>.jsonannouncement(配列)に「上から順に実行する放送ブロック」を書けます
- パターン定義:
broadcast/patterns/<patternId>.json- 中身は
sequence(鳴らすパーツの順番)を持ちます
- 中身は
フェーズ定義/パターン/音源のどれかが欠けて再生できない場合でも、無音で次に進みます。
追加の放送(啓発放送など)を後ろに繋げたいとき
announcement ブロック内で patternIds(配列)を使うと、複数パターンを順番に連結して再生できます。
例: A01 の後に NOTICE01 を鳴らす
{
"version": 1,
"announcement": [
{ "patternIds": ["A01", "NOTICE01"] }
],
"ticker": "{nextStationName}です。"
}
フェーズ内に「直書き」でパーツ列を書きたいとき
たまにしか使わない音声パーツのために broadcast/patterns/*.json を増やしたくない場合、フェーズ定義に直書きできます。
例: パターンの後に、このフェーズ限定で 1パーツだけ足す
{
"version": 1,
"announcement": [
{ "patternId": "A01" },
{ "sequence": ["audio/parts/notice_01"] }
],
"ticker": "{nextStationName}です。"
}
パーツ列の途中に「無音」を挿入したいとき
broadcast/patterns/*.json の sequence や、broadcast/phases/*.json の announcement[].sequence の中で、次のキーワードが使えます。
silence:<秒>(例:silence:0.35)wait:<秒>(同義)
例(「次は、(0.4秒無音) 駅名 です。」)
{
"version": 1,
"sequence": [
["audio/parts/next_is", "audio/parts/next"],
"silence:0.4",
"audio/stations/{nextStationName}",
"audio/parts/desu"
]
}
例: そもそもパターンを使わず、このフェーズだけ直書きする
announcement ブロック(推奨)
フェーズ定義の announcement(配列)に、複数ブロックを並べて「上から順に」実行できます。
各ブロックは patternId / patternIds / sequence を好きに混在できます。
例: パターン→出口案内→直書き啓発放送 の順で鳴らす
{
"version": 1,
"doorSide": "left",
"announcement": [
{"patternId": "B01"},
{"kind": "doorSide"},
{"sequence": ["audio/parts/notice_01"]}
]
}
※ フェーズ定義は announcement(配列)必須です。
phaseId
timeline.items[].phaseId は、そのまま broadcast/phases/<phaseId>.json の <phaseId> として使われます。
例:phaseId: 102 の場合、broadcast/phases/102.json を読みます。
パターン例(「翠嵐大学、翠嵐大学です。」)
- フェーズ:
broadcast/phases/102.jsonを{"announcement":[{"patternId":"B01"}]}にする - パターン
B01:audio/stations/{nextStationName}→audio/stations_desu/{nextStationName}(です)の順に再生
文言(テロップ)を変えたいとき
テロップは通常 app.js が作る文言を表示しますが、フェーズ定義側で上書きできます。
broadcast/phases/<phaseId>.jsonにtickerを追加すると、その文字列を表示します(例:"ticker": "{nextStationName}、{nextStationName}です。")
使える変数:
{currentStationName}{nextStationName}
無音時の待ち時間を変えたいとき
broadcast/phases/<phaseId>.jsonにdurationSecを追加すると、無音でもその秒数待ってから次へ進みます
出口方向(左右どちらの扉が開くか)
フェーズ定義に doorSide を書くと、そのフェーズのデフォルトの出口方向として扱われます(未指定なら none)。
値:
none(指定なし)left(左)right(右)both_left(両側・左から)both_right(両側・右から)
UIの「出口」で デフォルト(フェーズ) 以外を選ぶと、フェーズの doorSide よりUI指定が優先されます。
出口案内を鳴らしたい場合は、フェーズ定義の announcement に {"kind":"doorSide"} を 鳴らしたい位置 に挿入します(自動付与はしません)。
ファイル
index.html: 画面styles.css: タブレット風の見た目app.js: 停車駅ロジックと Web Audio API(オシレータ/ノイズ)