シェルスクリプトで音声ファイルの波形を画像化するやつ

最近、ハイレゾ音源なのにダイナミックレンジを殺したミキシング・マスタリングで海苔波形が云々という文句をアニメ方面からよく聞くので、じゃあ自分が持ってる息の長いバンドの音源を見比べてみようかと思うも、音源を編集ツールに読ませてスクショするのは面倒でダサいし、かといって視覚的に出力するツールを知らないし、ということでそういうツールを自作することにしました…シェルスクリプトで。

# ソース

``` bash:sndvisual
#!/bin/sh -e
# License: CC0

# mktemp(1)はPOSIX外
tmp=$(mktemp -d)
clean() {
	rm -rf $tmp
}
trap clean EXIT

# オプション
# -v,-m 音量の基準線を追加する。
#       線の引く位置を -v はdB、-m はサンプルに対する倍率で指定
# -r    基準線に合わせて出力波形も拡大縮小する
level= reduce=
while getopts m:v:r sw
do
	case $sw in
	v)
		level=$(printf %.3f $(echo "e(($OPTARG/20)*l(10))"|bc -l))
		;;
	m)
		level=$(printf %.3f $OPTARG)
		;;
	r)
		reduce=t
		;;
	*)
		cat <<heredoc >&2
usage:
	${0##*/} [-v vol|-m mag] [-r] [sound]
heredoc
		exit 1
		;;
	esac
done
shift $(($OPTIND - 1))

if [ -n "$reduce" -a -n "$level" ]
then reduce=$level
else reduce=1
fi

# 音声ファイルをモノラル8bit PCM化。要ffmpeg
# 要は音声データをLiner PCMに変換できてストリームの情報を得らればいいので
# soxとか自分の使いやすいソフトで代替するのも良いと思います
[ $# -eq 0 ] && input=- || input="$1"
ffmpeg -i "$input" -ac 1 -f s8 $tmp/samp.bin 2>$tmp/meta
sr=$(sed '/^Output #0, s8,/,/Stream #0:0/!d' $tmp/meta |sed '$!d; s/.* \([0-9]*\) Hz.*/\1/')
len=$(wc -c <$tmp/samp.bin)

# ここからSVG出力
cat <<heredoc
<?xml version='1.0' encoding='utf-8' standalone='yes'?>
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 -144 2048 288">
<style type="text/css"><![CDATA[
polygon {
fill: #44f;
stroke: #00f;
stroke-width: 1;
}
]]></style>
<rect x="0" y="-144" width="2048" height="288" fill="#ddd"/>
<path d="M0,-128 H2048 M0,128 H2048" stroke="#444" stroke-width="1"/>
heredoc

# 1分毎に縦線を引くやつ
awk -v "sr=$sr" -v "len=$len" 'BEGIN{
	print "<path stroke=\"#777\" stroke-width=\"1\" d=\""
	for (m = sr * 60; m < len; m += sr * 60) {
		print "M" m * 2048 / len ",-144v288"
	}
	print "\"/>"
}'

# 区切り毎のサンプル最大・最小値を求める
# ここら辺、シェルスクリプトでもバイナリ処理頑張れるよってのを示したかっただけなので
# 実際使うなら遅いからバイナリを直接読める言語を使ったほうがいいよ……
od -tdC -v -An $tmp/samp.bin |sed 's/  */ /g; s/^ //; s/ $//;' |tr ' ' '
' |awk -v "len=$len" '
BEGIN{
	pos = 1
	posf = int(len / 2048)
	sampmax = 0
	sampmin = 0
	NR--
}
{
	if (NR < posf) {
		v = -$1
		if (sampmax < v) {
			sampmax = v
		}
		else if (sampmin > v) {
			sampmin = v
		}
	}
	else {
		print pos - 1, sampmax, sampmin
		pos++
		posf= int(pos * len / 2048)
		sampmax = 0
		sampmin = 0
	}
}
END {
	print pos - 1, sampmax, sampmin
}
' >$tmp/samp.txt

# 下半分多角形
echo '<polygon'
echo "transform='scale(1,$reduce)'"
echo 'points="0,0'
cut -f1,2 -d ' ' $tmp/samp.txt
echo '2048,0"/>'

# 上半分多角形
echo '<polygon'
echo "transform='scale(1,$reduce)'"
echo 'points="0,0'
cut -f1,3 -d ' ' $tmp/samp.txt
echo '2048,0"/>'

# 基準線追加
if [ -n "$level" ]
then
	levelf=$(echo "$level*128"|bc)
	echo "<path stroke='red' stroke-width='1' d='M0,$levelf H2048 M0,-$levelf H2048' stroke-dasharray='8' />"
fi

echo '</svg>'
```

最終的には画像形式をSVGにし、全体を2048等分して、その区間内での最小値・最大値を求めて折れ線で繋いでいくという処理になりました。これでこんな感じの画像が得られます。

![result.png](https://qiita-image-store.s3.amazonaws.com/0/91658/a42e0457-be5d-0a06-46ae-ca9e0efd3509.png)
(これはスプラトゥーンの「バトル 勝ちジングル」より)

元々はPCMサンプル毎にピクセルを出力していって出来た長大な画像を縮小するとか、SVGでサンプル毎に線を引くとかしてたけど、どちらもべらぼうに時間がかかる上にたいして綺麗じゃなかったのでやめました。44100 Hzのサンプルが5分続くと1000千万超えますしね…。

あとこれで作ったSVGをImageMagickでconvert(1)すると90秒ぐらい掛かるんだけど遅すぎない? batikのrasterizerだと2〜3秒しか掛からないのに。

# 画像一覧

まあそんなわけでめでたく波形の視覚化に成功したので、T‐スクェアの曲からいくつか挙げてみましょう。(これがやりたかっただけ)

※ 赤い線はノーマライズのためにEBU R128に基づくラウドネス測定の後-18 LUFSになるまで下げられる音量です。間隔が狭いほど元々の音が大きく感じます。

## T‐スクェア

A FEEL DEEP INSIDE / LUCKY SUMMER LADY (1978、1987再販)
![lucky summer lady@1.png](https://qiita-image-store.s3.amazonaws.com/0/91658/743f5fa2-fda2-ef31-02de-2927d55ff88e.png)

LICKIN’ IT / MIDNIGHT LOVER (1978、1990再販)
![out.png](https://qiita-image-store.s3.amazonaws.com/0/91658/b533c6f1-b99a-9024-1ab0-f7ac525027e9.png)

TEXAS KID / MAKE ME A STAR (1979、1987再販)
![make me a star@7.png](https://qiita-image-store.s3.amazonaws.com/0/91658/b1d56294-75cf-9ec8-51eb-fb0c34f632d6.png)

TOMORROW’S AFFAIR / ROCKOON (1980、1990再販)
![rockoon@3.png](https://qiita-image-store.s3.amazonaws.com/0/91658/3b9be078-e3f9-5269-21fa-8289da3dda96.png)

CHOU CHOW / MAGIC (1981、1987再販)
![out.png](https://qiita-image-store.s3.amazonaws.com/0/91658/bf3664db-5e0d-80ef-489b-c4f42784e3fc.png)

LOVE’S STILL BURNIN’ / 脚線美の誘惑 (1982、1990再販)
![out.png](https://qiita-image-store.s3.amazonaws.com/0/91658/b0a1d817-ab36-aa6b-d7bf-11ba5ff7d878.png)

君はハリケーン / うち水にRAINBOW (1983、1990再販)
![out.png](https://qiita-image-store.s3.amazonaws.com/0/91658/ceda6306-d2ca-9821-5c1a-d1764d46de11.png)

ADVENTURES (EPILOGUE) / ADVENTURES (1984)
![adventures@9.png](https://qiita-image-store.s3.amazonaws.com/0/91658/d1424226-c651-4c83-0cdd-5e6343693b75.png)

いとしのうなじ / STARS AND THE MOON (1984、2001リマスター)
![stars and the moon@1.png](https://qiita-image-store.s3.amazonaws.com/0/91658/f5fd6200-50db-d3d8-28d5-ea52ee9d7964.png)

PRIME / R·E·S·O·R·T (1985)
![resort@8.png](https://qiita-image-store.s3.amazonaws.com/0/91658/59749ae6-6747-afa3-b449-c00c965ece4f.png)

LEAVE ME ALONE / S·P·O·R·T·S (1986)
![out.png](https://qiita-image-store.s3.amazonaws.com/0/91658/e6da9e13-35ce-17a2-c4cb-401c052a940a.png)

TRUTH / TRUTH (1987)
![truth@5.png](https://qiita-image-store.s3.amazonaws.com/0/91658/900bdca9-9755-c7cf-ec77-5edf486cd3cd.png)

GO FOR IT / YES, NO. (1988)
![yes no@2.png](https://qiita-image-store.s3.amazonaws.com/0/91658/ea5741cd-5879-785a-15ab-6f8cb2c963d6.png)

MORNING STAR / WAVE 上: (1989) 下: (1989、2001リマスター)
![wave@1.png](https://qiita-image-store.s3.amazonaws.com/0/91658/6f77ac60-f028-f07d-e65b-da95961296f3.png)
![wave vrcl-2114@1.png](https://qiita-image-store.s3.amazonaws.com/0/91658/c51a1db1-9d22-44de-b4a9-1ab7666ee7f5.png)


WIND SONG / NATURAL (1990)
![natural--1@3.png](https://qiita-image-store.s3.amazonaws.com/0/91658/753bf0c5-ec02-abe9-b6a4-e55d2bb0f075.png)

ここから本田雅人
TRAVELERS / REFRESHEST (1991) 
![refreshest@9.png](https://qiita-image-store.s3.amazonaws.com/0/91658/331688c9-d202-d538-590e-1d242741dcc0.png)

MEGALITH / NEW‐S (1991)
![out.png](https://qiita-image-store.s3.amazonaws.com/0/91658/d4e297f2-3e97-40d9-a377-3003b3645c03.png)

FACES / IMPRESSIVE (1992)
![impressive@1.png](https://qiita-image-store.s3.amazonaws.com/0/91658/5ac66c9f-c359-c992-0d04-832b971aeea3.png)

明日への扉 / HUMAN (1993)
![human@1.png](https://qiita-image-store.s3.amazonaws.com/0/91658/45f08174-5ac4-c920-1271-d6873fe693d9.png)

夏の蜃気楼 / 夏の惑星 (1994)
![out.png](https://qiita-image-store.s3.amazonaws.com/0/91658/e5f49d36-7371-d2e0-596a-30836e3e35f9.png)

NO END RUN / SOLITUDE (1994)
![solitude@3.png](https://qiita-image-store.s3.amazonaws.com/0/91658/33b353fb-b34f-016a-1613-39bfe3820d95.png)

CROWN AND ROSES / WELCOME TO THE ROSE GARDEN (1995)
![out.png](https://qiita-image-store.s3.amazonaws.com/0/91658/d8e0c48d-f7d7-4c91-ab23-d01dbbefc18b.png)

WHO’S LICKIN’ IT / MISS YOU IN NEW YORK (1995)
![miss you in new york@5.png](https://qiita-image-store.s3.amazonaws.com/0/91658/cf1def38-afdf-f8fa-a85a-6d70d31f5674.png)

SUNSHINE SHOWER / B.C. A.D. (1996)
![b.c. a.d.@8.png](https://qiita-image-store.s3.amazonaws.com/0/91658/0f1bdb04-d75d-d53c-fa68-ba82125145a2.png)

SAMURAI METROPOLIS / BLUE IN RED (1997、2002リマスター)
![out.png](https://qiita-image-store.s3.amazonaws.com/0/91658/6a135f52-9e59-2ff8-9453-52347e53ff51.png)

ここから宮崎隆睦
DOWN TO EARTH / GRAVITY (1998) 
![gravity--1@6.png](https://qiita-image-store.s3.amazonaws.com/0/91658/af34be7f-b5a1-2549-6ec1-3e65d732c534.png)

JAPANESE SOUL BROTHERS / GRAVITY (1998)
![gravity--2@1.png](https://qiita-image-store.s3.amazonaws.com/0/91658/4b3266b0-a14d-9b33-008f-ddd6db987d90.png)

SCRAMBLING / SWEET & GENTLE (1999)
![sweet and gentle@5.png](https://qiita-image-store.s3.amazonaws.com/0/91658/34e81ca6-1fe2-9b57-4065-7c1e196f7940.png)

MAN ON THE MOON / T‐SQUARE (2000)
![out.png](https://qiita-image-store.s3.amazonaws.com/0/91658/e6f6b62b-4180-3b64-4caf-e9b27ac2aee3.png)

ここから2人ユニット
SAFARI / FRIENDSHIP (2000) 
![friendship@2.png](https://qiita-image-store.s3.amazonaws.com/0/91658/f0f4b4e6-2a04-c090-6b85-da557f28fba6.png)

TOYS / BRASIL (2001)
![out.png](https://qiita-image-store.s3.amazonaws.com/0/91658/01a51326-51ab-d716-7d17-16700dac522f.png)

COME AND GET IT / NEW ROAD, OLD WAY (2002)
![new road old way@7.png](https://qiita-image-store.s3.amazonaws.com/0/91658/cca73dd5-8053-f535-d132-042717f6c1d7.png)

EUROSTAR 〜RUN INTO THE LIGHT〜 / SPIRITS (2003)
![spirits--1@3.png](https://qiita-image-store.s3.amazonaws.com/0/91658/f9f3be7c-3ecc-b95c-3e7a-0b6ccd05dee9.png)

DREAM WEAVER / GROOVE GLOBE (2004)
![out.png](https://qiita-image-store.s3.amazonaws.com/0/91658/7a003aeb-46b5-2762-27b9-1e22588af789.png)

ここからバンド制復活
MORE THAN LEMONADE / PASSION FLOWER (2005) 
![out.png](https://qiita-image-store.s3.amazonaws.com/0/91658/76f16bd8-4a5b-688b-e426-e18b4bc628f5.png)

REVENGE / BLOOD MUSIC (2006)
![out.png](https://qiita-image-store.s3.amazonaws.com/0/91658/e576dcb8-14a4-1c5f-3761-7f0b6179be3a.png)

RONDO / 33 (2007)
![33--1@1.png](https://qiita-image-store.s3.amazonaws.com/0/91658/8e8feed2-a817-ec8e-80b8-82aa41643e6a.png)

ANTHEM / WONDERFUL DAYS (2008)
![out.png](https://qiita-image-store.s3.amazonaws.com/0/91658/52408099-163b-146f-3c3f-2ae32a4cc864.png)

SURVIVOR / DISCOVERIES (2009)
![discoveries@2.png](https://qiita-image-store.s3.amazonaws.com/0/91658/a486700b-4e7d-a7b3-7930-a1fa31507305.png)

FANTASTIC STORY 〜時間旅行〜 / 時間旅行 (2010)
![jikan ryoko@1.png](https://qiita-image-store.s3.amazonaws.com/0/91658/65258859-32c4-f1c4-6149-cec23b3db3c0.png)

はやぶさ 〜THE GREAT JOURNEY: 奇跡の帰還〜 / NINE STORIES (2011)
![nine stories@2.png](https://qiita-image-store.s3.amazonaws.com/0/91658/9ec0a9b0-e87c-46d4-a288-3ae89aba0c88.png)

SUNSHOWER / WINGS (2012)
![wings@4.png](https://qiita-image-store.s3.amazonaws.com/0/91658/7766335c-8662-2f13-8280-1c1427b11dcf.png)

TEMPS, 10 P.M. / SMILE (2013)
![smile@5.png](https://qiita-image-store.s3.amazonaws.com/0/91658/98253561-6f6a-87a6-a6d8-b05bdf96652b.png)

YOU’RE THE ONE / NEXT (2014)
![next@1.png](https://qiita-image-store.s3.amazonaws.com/0/91658/9fcdbe07-daf4-315a-2610-8b29e745470d.png)

KNOCK ME OUT / PARADISE (2015)
![paradise@7.png](https://qiita-image-store.s3.amazonaws.com/0/91658/988ba5af-e89a-c98e-72a4-1251a6b24f0a.png)

7‐6‐5 / TREASURE HUNTER (2016)
![treasure hunter@5.png](https://qiita-image-store.s3.amazonaws.com/0/91658/0cc85fa6-01b7-2434-3522-dbcea0ed535b.png)

T‐スクェアはまぁポップやロックとはジャンルが違うし最近の曲は大体Hybrid SACDでリリースされているだけあってそこまでどぎつく海苔海苔しくなっているわけではありませんね。

## T‐スクェア以外で気になったやつ

塩谷哲 - SETEMBRO (BRAZILIAN WEDDING SONG) / WISHING WELL (1998)
![se.png](https://qiita-image-store.s3.amazonaws.com/0/91658/3947ce64-07bb-7434-410c-6a0e8259f885.png)
原曲はIvan Lins、Quincy Jonesによるカバーが有名な曲です。これは塩谷哲によるピアノのカバーなんですが、バラードらしくしっとりと始まり、終盤の盛り上がるところで音がドーンと力強く迫ってくる感じが本当に良い。ジャズ録音斯くあるべしといった感じ。

本田雅人 - SEVEN / SAXES STREET (2015)
上はハイレゾ配信版、下はCD版
![out.png](https://qiita-image-store.s3.amazonaws.com/0/91658/a0ea0ce7-5cbe-58ba-c2df-ef5ab78ab9c6.png)
![out.png](https://qiita-image-store.s3.amazonaws.com/0/91658/f59be198-542a-609c-bd2e-b7ca0528bc90.png)
最初CDを買ったところとても気に入ったので、音質の向上を期待しつつ、あとお布施のつもりでハイレゾ版も買ってみたのですが、サンプリングレートとビット深度が増えただけで内容が一緒、その容れ物に見合ったミキシング・マスタリングがなされていないことにとてもショックを受けてしまいました。フュージョンは今やジャズとは距離のあるジャンルとはいえ流通上はジャズと一緒に扱われるのだからもう少し気を遣えないものなんでしょうか? もうハイレゾ買う気がしない…。

Skrillex - Rock n’ Roll (Will Take You to the Mountain) / Scary Monsters and Nice Sprites
![out.png](https://qiita-image-store.s3.amazonaws.com/0/91658/87f7634d-a265-c2c0-be16-e74f9bd582e0.png)
自分のライブラリに入っているやつ(vorbis限定)で一番うるさかったやつで、ReplayGainで-12.99 dBも下げられる。海苔。
これをReplayGainを有効にしつつ再生した時の波形は以下のようになります。
![out.png](https://qiita-image-store.s3.amazonaws.com/0/91658/02b34d9b-7b5a-65c3-55b3-8e89609bd849.png)

# まとめ
T‐スクェア40周年おめでとうございます。