No description
Find a file
2026-03-17 05:10:17 +09:00
audio 最初のコミット 2026-03-17 05:10:17 +09:00
broadcast 最初のコミット 2026-03-17 05:10:17 +09:00
chats 最初のコミット 2026-03-17 05:10:17 +09:00
app.js 最初のコミット 2026-03-17 05:10:17 +09:00
audioEngine.js 最初のコミット 2026-03-17 05:10:17 +09:00
broadcastConfig.js 最初のコミット 2026-03-17 05:10:17 +09:00
index.html 最初のコミット 2026-03-17 05:10:17 +09:00
jidouhousou.code-workspace 最初のコミット 2026-03-17 05:10:17 +09:00
README.md 最初のコミット 2026-03-17 05:10:17 +09:00
styles.css 最初のコミット 2026-03-17 05:10:17 +09:00

架空鉄道 車内自動放送タブレットWeb Audio API

使い方

  • index.html をブラウザで開きます
  • 本プロジェクトは ES Modules を使っているため、ローカルサーバ経由で開くのを推奨します
    • 例: python -m http.server 8000http://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 8000http://localhost:8000/
  • 画面上部の「音声を有効化」を押します(ブラウザの仕様で、ユーザー操作後にしか音が出ません)
  • 「駅名放送」を押すたびに、サービス設定の timeline.items の順番で進みます
  • 左側の矢印(←/→で、放送フェーズを「1手ずつ」戻す/進めることができます(例:次はまもなく次は
    • は履歴の先が無い場合、無音で1手進めます状態だけ進める
  • dep/arr/terminal を使って「次は / まもなく / 終点」を表現します(例: dep を書かずに arr だけ書けば soon-only になります)
  • 放送中に止めたい場合は「停止」を押します

ルートフェーズ一覧を1ファイルで管理するサービス設定

列車種別・行先などで「停車駅」や「フェーズ一覧(進行順)」が変わり得る前提として、サービス設定を 1つのJSONファイルで完結 させます。

  • サービス設定(例): broadcast/service_aoba_up_local.json
    • timeline.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.json
    • services[] に、サービス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>.json
    • announcement(配列)に「上から順に実行する放送ブロック」を書けます
  • パターン定義: 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/*.jsonsequence や、broadcast/phases/*.jsonannouncement[].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>.jsonticker を追加すると、その文字列を表示します(例: "ticker": "{nextStationName}、{nextStationName}です。"

使える変数:

  • {currentStationName}
  • {nextStationName}

無音時の待ち時間を変えたいとき

  • broadcast/phases/<phaseId>.jsondurationSec を追加すると、無音でもその秒数待ってから次へ進みます

出口方向(左右どちらの扉が開くか)

フェーズ定義に doorSide を書くと、そのフェーズのデフォルトの出口方向として扱われます(未指定なら none)。

値:

  • none(指定なし)
  • left(左)
  • right(右)
  • both_left(両側・左から)
  • both_right(両側・右から)

UIの「出口」で デフォルト(フェーズ) 以外を選ぶと、フェーズの doorSide よりUI指定が優先されます。

出口案内を鳴らしたい場合は、フェーズ定義の announcement{"kind":"doorSide"}鳴らしたい位置 に挿入します(自動付与はしません)。

ファイル

  • index.html : 画面
  • styles.css : タブレット風の見た目
  • app.js : 停車駅ロジックと Web Audio APIオシレータ/ノイズ)