1つの試聴から一括処理へ

ShellBeginner
オンラインで実践に進む

はじめに

Fieldnote Audio Studio は、録音を短く試聴できるファイルを必要としています。まず3秒の WAV を作成して再生し、そのコマンドを再利用可能な Bash スクリプトに発展させます。用意された録音は、この教材独自の合成音であり、マイクで録音したものではありません。

Shell for Beginners で扱う Bash の基本的な引数、条件分岐、ループ、配列、関数を理解していることを前提とします。このラボでは、必要なメディアオプションと引用符の使い方を説明します。FFmpeg は準備済みです。Workspace ターミナルと Listening Room ページは1つのコンテナを共有し、各ラボは独立して初期化されます。

試聴ファイルを作成して再生する

このステップでは、一括処理スクリプトを書く前に、正しく作成された試聴ファイルの内容を実際に聴きます。

UTF-8 のファイル名を扱えるよう準備された Workspace から始めます。作業ディレクトリは用意されたスタジオです。相対パスが見つからないと表示されたら、pwd で現在のディレクトリを確認し、次の完全な cd コマンドで戻ってください。このコースの作業はすべてここで行います。

cd /home/labex/project/audio-studio
ls input

Morning bells.wav、海辺.wav、-closing.wav が表示されるはずです。それぞれ8秒のモノラル WAV で、毎秒22050サンプルの16ビット PCM データを格納しています。PCM はサンプル値を直接保存する方式です。この形式を保ち、先頭だけを取り出します。対象は入力ディレクトリ直下にある、隠しファイルではなく、小文字の .wav で終わる通常ファイルのみです。サブディレクトリを再帰的に探索したり、任意のコーデックを扱ったりはしません。

まず、単一ファイルの実験用ディレクトリ single を作ります。mkdir -p は必要ならディレクトリを作成し、すでに存在していても受け入れます。

mkdir -p single

次のコマンドでは、-i が入力を指定し、-t 3 が出力を3秒に制限し、-c:a pcm_s16le が符号付き16ビット PCM 音声を選びます。オプションはその後の入力または出力を説明するため、位置が重要です。-v error は通常の進行状況を隠し、エラーは表示します。-nostdin は FFmpeg が対話コマンドのためにターミナル入力を読むのを防ぎ、ループ内でも安全に使いやすくします。最後のパスが出力ファイル名です。

二重引用符は、空白を含むパスを1つの引数として保ちます。先頭の ./ や絶対ディレクトリの接頭辞は、- で始まるファイル名がオプションと誤認されることも防ぎます。新しい出力ファイルを使ってください。標準入力による対話を無効にすると、FFmpeg は通常、既存ファイルの上書きを拒否します。

ffmpeg -nostdin -v error -i "input/Morning bells.wav" -t 3 -c:a pcm_s16le "single/Morning bells.wav"

このログレベルでは、変換が成功しても何も表示されません。$? は直前のコマンド自身が返した終了ステータスです。0は成功、0以外は失敗を意味します。別のコマンドを実行すると置き換わるので、すぐに読み取ります。

printf 'Conversion exit: %s\n' "$?"

Conversion exit: 0 と表示されるはずです。これは変換が正常に実行されたことを示します。ファイルが使えることは、続いて再生して確認します。

Listening Room タブに切り替えてページを再読み込みします。First preview の Morning bells にある再生コントロールを押してください。変化する音を聴き、シークコントロールを動かしてクリップ内の位置を変え、3秒で終わることを確認します。Download WAV を選ぶと、同じファイルを保存できます。このページが読むのは実際の出力ファイルだけです。音声が欠けていてもライブラリから補完しません。Workspace に戻って続けます。

Listening Room に表示された最初の3秒の試聴ファイル

First preview の下にある再生・シークコントロールと Download WAV リンクに注目してください。

入力パスを打ち間違えたら、ls input を確認して引用符内の名前を修正し、コマンドを再実行します。不要になった実験ファイルが出力名を使用している場合は、再試行前に rm -- "single/Morning bells.wav" でそのファイルだけを削除してください。-- はオプションの終わりであり、「すべて削除する」という意味ではありません。このラボでは library、archive、notes.txt、scratch-other を変更しないでください。

変換処理を再利用可能な関数にする

このステップでは、録音ごとにコマンドを書き換える代わりに、入力、出力、長さを同じ変換処理へ渡します。

Workspace の /home/labex/project/audio-studio から始めます。関数は、複数のコマンドに名前を付けたものです。make_preview の中では、$1、$2、$3 がその関数の引数です。スクリプトは自分の引数を受け取り、順番を保って渡します。"$1" のように引用符で囲むと、空白や Unicode を含むパス全体が保たれます。

次のヒアドキュメントで小さなスクリプトを書き込みます。cat > preview-one.sh はそのスクリプトファイルを置き換えます。<<'BASH' は、BASH だけの行までを文字どおりのテキストとして受け取ります。区切り文字を引用すると、保存時に現在のシェルが $1 を展開しません。先頭行はスクリプトをプログラムとして起動する際に Bash を選びます。ここでは明示的に bash で実行します。

cat > preview-one.sh <<'BASH'
#!/usr/bin/env bash
make_preview() {
  ffmpeg -nostdin -v error -i "$1" -t "$3" -c:a pcm_s16le "$2"
}
make_preview "$1" "$2" "$3"
BASH

関数を使って、別の2秒の試聴ファイルを作ります。2 は長さの引数で、どちらのファイル名の一部でもありません。

bash preview-one.sh "input/海辺.wav" "single/海辺.wav" 2
printf 'Conversion exit: %s\n' "$?"

再び0になるはずです。Listening Room を再読み込みし、First preview の 海辺 を再生して、今回は2秒で終わることを確認します。Workspace に戻ります。同じ関数で別のパスや長さを扱えるようになりました。次は入力の集合を追加します。

すべての録音を処理する

このステップでは、各入力の試聴ファイルを格納した新しいディレクトリを公開します。

Workspace から始めます。スクリプトが受け取る引数は、入力ディレクトリ、新しい出力ディレクトリ、1〜5秒の整数の長さ、の3つです。最初の一括処理では、入力ディレクトリと出力の親ディレクトリは存在し、互いに分かれているものを指定し、出力自体は入力の外に置きます。成功済みの成果を次の実行で上書きしないよう、既存の出力は拒否する必要があります。同じ出力パスに2つの書き込み処理を同時に実行しないでください。これらは通常のエラー処理であり、並行書き込みの安全性を保証するものではありません。

書き込む前に新しい部分を理解しましょう。realpath は既存の入力ディレクトリを絶対パスにし、各入力に安全なディレクトリ接頭辞を付けます。shopt -s nullglob は、一致するものがない *.wav をアスタリスク入りの文字列ではなく0個の要素に展開します。files=(...) は一致したパスを配列に格納します。ディレクトリの引用符は空白を保ち、引用していない *.wav は引き続きファイル名と一致します。${#files[@]} は要素数を数え、"${files[@]}" は各要素を別々に渡します。

$(command) はコマンドの出力を取り込みます。コマンドが失敗すると || exit 2 で停止します。テスト同士の || は論理和です。[ -e "$2" ] || [ -L "$2" ] は、いずれかの種類の既存対象を拒否理由とします。

case は分岐を選び、1|2|3|4|5 はそのいずれかの値を許可します。[ -e path ] は対象の存在を調べ、-L はリンク先が存在しないシンボリックリンクも検出します。${source##*/} は / で終わる最長の接頭辞を取り除き、ファイル名だけを残します。if ! command はコマンドが0以外を返すと失敗側へ進みます。>&2 は説明をエラーストリームへ出力し、exit 1 は失敗として終了します。

用意された不完全な batch.sh を、次の最初の動作版で置き換えます。これはこのラボの正常な入力用です。変換に失敗すると0以外を返しますが、不完全な新しいディレクトリが残ることがあります。次のガイド付きラボでは、そのような公開を防ぐ方法を学びます。

cat > batch.sh <<'BASH'
#!/usr/bin/env bash
if [ "$#" -ne 3 ]; then
  printf 'Usage: batch.sh INPUT OUTPUT SECONDS\n' >&2
  exit 2
fi
case "$3" in 1|2|3|4|5) ;; *) exit 2 ;; esac
if [ -e "$2" ] || [ -L "$2" ]; then
  printf 'Output already exists\n' >&2
  exit 2
fi
input=$(realpath -- "$1") || exit 2
shopt -s nullglob
files=("$input"/*.wav)
if [ "${#files[@]}" -eq 0 ]; then
  printf 'No WAV inputs\n' >&2
  exit 1
fi
mkdir -- "$2" || exit 1
make_preview() {
  ffmpeg -nostdin -v error -i "$1" -t "$3" -c:a pcm_s16le "$2"
}
for source in "${files[@]}"; do
  name=${source##*/}
  if ! make_preview "$source" "$2/$name" "$3"; then
    printf 'Could not convert: %s\n' "$name" >&2
    exit 1
  fi
done
printf 'Published %s previews\n' "${#files[@]}"
BASH

新しい previews ディレクトリに3秒の一括出力を作ります。公開されたフォルダーには、入力ごとに対応する WAV が元のファイル名で1つだけ含まれ、余計なファイルがないことが必要です。

bash batch.sh input previews 3

Published 3 previews と表示されるはずです。Listening Room を再読み込みします。Your listening pack で3つすべてを再生し、位置を変えて聴き、そのうち1つをダウンロードします。それぞれ3秒で、**-closing** も含め、自分に対応する録音の先頭を含みます。単一ファイルの実験結果は別に残ります。

Workspace に戻り、意図的に同じコマンドを繰り返します。これは想定どおりの拒否であり、環境の故障ではありません。

bash batch.sh input previews 3
printf 'Repeat exit: %s\n' "$?"

Output already exists と Repeat exit: 2 が表示されるはずです。成功した出力は残し、今後の実験では別の新しい出力名を選んでください。空入力にも、役立つパックを作成したふりをせず明確に失敗できます。再利用可能な一括処理ができました。次は録音が1つ壊れているときの動作を改善します。

まとめ

再生可能な音声を作り、引用符でファイル名全体を保ち、関数と配列ループを組み合わせて再利用可能な Bash 一括処理にしました。コマンド自身の終了ステータスを読み、既存の試聴パックの上書きを拒否しました。次のラボではステージングディレクトリを加え、変換失敗で不完全な新しいパックを公開しないようにします。