SVX日記

2004|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|07|08|09|10|11|12|
2023|01|

2023-01-22(Sun) dockerのコンテナ内でもっとアレしたい

  年末に「今年のまとめ」としてアレコレ書きたかったのだが、ほかにもヤリたいことだらけで、まとめられないまま1月も下旬になってしまった。まぁ、反省や抱負も大事だが、ヤリたいことがあるうちは、それをヤルことが優先である。反省や抱負は一段落してからにしよう。

  去年から今年にかけてプログラムをする機会が多い。公私ともに。仕事環境は劣化の一途でいっそヤメたろうかとまで思っているが、割と好きなことをできているので、まさにイタしカユしである。

  最近は、コンテナで動かすことを前提にしたプログラムをすることが多く、ちょうど1年前くらいに作ったdockerのコンテナ内でアレしたいのツール群が大活躍中なのであるが、活躍しているからこそ、先に面倒だから実装を省いた「プロセスの起動時間やCPU時間」が見たくなってきてしまった。

  というわけでサクッと実装である。

# docker_ps ldap-
# INTO [ldap-alpha].
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 11:44 -        00:00:00 /sbin/init 
root          26       1  0 11:44 -        00:00:00 /usr/lib/systemd/systemd-journald 
root          36       1  0 11:44 -        00:00:00 /usr/lib/systemd/systemd-homed 
dbus          44       1  0 11:44 -        00:00:00 /usr/bin/dbus-broker-launch --scope system --audit 
dbus          53      44  0 11:44 -        00:00:00 dbus-broker --log 4 --controller 9 --machine-id a6cd51e94fd74eb98afdf11c55965591 --max-bytes 536870912 --max-fds 4096 --max-matches 16384 --audit 
ldap          54       1  0 11:44 -        00:00:00 /usr/sbin/slapd -u ldap -h ldap:/// ldaps:/// ldapi:/// 
root        1287       0  0       -                 
# docker exec ldap-alpha ps -efww
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 11:44 ?        00:00:00 /sbin/init
root          26       1  0 11:44 ?        00:00:00 /usr/lib/systemd/systemd-journald
root          36       1  0 11:44 ?        00:00:00 /usr/lib/systemd/systemd-homed
dbus          44       1  0 11:44 ?        00:00:00 /usr/bin/dbus-broker-launch --scope system --audit
dbus          53      44  0 11:44 ?        00:00:00 dbus-broker --log 4 --controller 9 --machine-id a6cd51e94fd74eb98afdf11c55965591 --max-bytes 536870912 --max-fds 4096 --max-matches 16384 --audit
ldap          54       1  0 11:44 ?        00:00:00 /usr/sbin/slapd -u ldap -h ldap:/// ldaps:/// ldapi:///
root        1356       0  0 12:07 ?        00:00:00 ps -efww

  で、以下がdocker_execのコードである。

#!/usr/bin/env ruby
# coding: utf-8
 
class String
	def to_ipaddr
		r = self.scan(/.{8}/).map {|h8|
			h8.scan(/../).map {|hh|
				hh.to_i(16)
			}.reverse
		}.join('.')
		self.size < 9 ? r : r.gsub(/(0\.){3,}0?/, '::')
	end
end
 
states = [
	'', 'ESTABLISHED', 'SYN_SENT', 'SYN_RECV', 'FIN_WAIT1', 'FIN_WAIT2',
	'TIME_WAIT', 'CLOSE', 'CLOSE_WAIT', 'LAST_ACK', 'LISTEN', 'CLOSING',
]
 
$0 =~ /docker_(.+)/ and subcmd = $1
 
ps = `docker ps`
targets = []; ps.split(/\n/)[1..-1].each {|l|
	(it = l.split(/\s+/).last) =~ /#{ARGV.last}/ and targets << it
}
 
if(targets.size == 0)
	puts('# NOT MATCH...')
 
elsif(targets.size == 1)
 
	def get_ps(target)
		uids = {}; progs = {}; sockets = {}
		IO.popen(['docker', 'exec', target, 'ls', '-lR', '/proc'], :err => [:child, :out]) {|io|
			pid = nil; io.each {|line|
				if(!pid and line =~ /^d.*?(\d+)$/)
					uids[$1] = line.split(/\s+/)[2]
				end
				if(line =~ /^\/proc\/(\d+):/)
					progs[pid = $1] ||= []
				end
				if(line =~ / exe -> (.+)/)
					progs[pid] << $1
				end
				if(line =~ / -> socket:\[(\d+)\]/)
					sockets[$1] ||= []
					sockets[$1] << pid
				end
			}
		}
		[uids, progs, sockets]
	end
 
	def get_ps2(target)
		ppids = {}; cmdlines = {}
		IO.popen(['docker', 'exec', target, '/bin/bash', '-c',
			'ls /proc | grep -e "^[1-9]" | sed "s/\(.*\)/cat \/proc\/\\1\/status/" | sh 2>/dev/null'], :err => [:child, :out]) {|io|
			pid = nil; io.each {|line|
				line =~ /^Pid:\s*(\d+)/ and pid = $1
				line =~ /^PPid:\s*(\d+)/ and ppids[pid] = $1
			}
		}
		IO.popen(['docker', 'exec', target, '/bin/bash', '-c',
			'ls /proc | grep -e "^[1-9]" | sed "s/\(.*\)/echo -n \\1:; cat \/proc\/\\1\/cmdline; echo/" | sh 2>/dev/null'], :err => [:child, :out]) {|io|
			io.each {|line|
				line =~ /^(\d+):(.*)/ and cmdlines[$1] = $2.gsub(/\u0000/, ' ')
			}
		}
		[ppids, cmdlines]
	end
 
	def get_ps3(target)
		start_times = {}; cpu_times = {}
		btime = nil
		IO.popen(['/bin/bash', '-c', 'cat /proc/stat'], :err => [:child, :out]) {|io|
			io.each {|line|
				line =~ /^btime\s+(\d+)/ and btime = $1.to_i and break
			}
		}
		now_a = Time.now.to_a
		IO.popen(['docker', 'exec', target, '/bin/bash', '-c',
			'ls /proc | grep -e "^[1-9]" | sed "s/\(.*\)/cat \/proc\/\\1\/stat/" | sh 2>/dev/null'], :err => [:child, :out]) {|io|
			io.each {|line|
				if(line =~ /(\d+)\s+\([^)]+\)\s+(.+)/)
					pid = $1
					stats = $2.split(/\s/)
					cpu_time = (stats[11].to_i + stats[12].to_i) / 100	# 11:utime + 12:stime
					days = cpu_time / 86400
					cpu_times[pid] = (days > 0 ? '%2d-' % days : '') + Time.at(cpu_time).utc.strftime('%H:%M:%S')
					start_time = Time.at(btime + stats[19].to_i / 100)	# 19:start_time
					start_time_a = start_time.to_a
					if(now_a[5] != start_time_a[5])
						start_times[pid] = '%5d' % start_time.year
					elsif(now_a[7] != start_time_a[7])
						start_times[pid] = start_time.strftime('%b%d')
					else
						start_times[pid] = start_time.strftime('%H:%M')
					end
				end
			}
		}
		[start_times, cpu_times]
	end
 
	puts('# INTO [%s].' % targets[0])
	if(subcmd == 'exec')
		# TODO
		system('docker exec -it %s %s /bin/bash' % [ARGV[0..-2].join(' '), targets[0]])
 
	elsif(subcmd == 'ps')
		uids, progs, sockets = get_ps(targets[0])
		ppids, cmdlines = get_ps2(targets[0])
		start_times, cpu_times = get_ps3(targets[0])
		puts('UID          PID    PPID  C STIME TTY          TIME CMD')
		progs.keys.sort {|a, b|
			a.to_i <=> b.to_i
		}.each {|pid|
			puts('%-8s %7d %7d %2d %5s %-8s %8s %s' % [uids[pid], pid, ppids[pid] || 0, 0, start_times[pid], '-', cpu_times[pid], cmdlines[pid]])
		}
 
	elsif(subcmd == 'netstat')
		uids, progs, sockets = get_ps(targets[0])
		puts('Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name')
		['tcp', 'tcp6', 'udp', 'udp6'].each {|prot|
			IO.popen(['docker', 'exec', targets[0], 'cat', '/proc/net/' + prot]) {|io|
				io.each {|line|
					ls = line.split(/[:\s]+/)
					ls[1] =~ /^\d/ or next
					laddr = ls[2].to_ipaddr + ':' + ls[3].to_i(16).to_s
					faddr = ls[4].to_ipaddr + ':' + ls[5].to_i(16).to_s
					prog = '-'; (it = sockets[ls[14]]) and prog = '%s/%s' % [it[0], progs[it[0]][0].split('/').last]
					puts('%-5s %6d %6d %-23s %-23s %-11s %-s' % [prot, ls[8].to_i(16), ls[7].to_i(16), laddr, faddr, states[ls[6].to_i(16)], prog])
				}
			}
			(it = $?.exitstatus and it == 0) or raise 'failed.'
		}
 
	else
		system('docker %s %s %s' % [subcmd, ARGV[0..-2].join(' '), targets[0]])
	end
 
else
	puts('# MULTIPLE MATCH...')
	puts(targets.join("\n"))
end
 
__END__

  結局、去年書いたコードに特段のバグは見つからず、ほぼ純粋に処理を追加しただけで実装できた。こういうのが、なんとも気分がいいんだよなぁ。


2022-12-31(Sat) 走り納め

  寒いし、タイヤに不安はあるが、どうしてもこの辺りで走っておきたい気分で、なんとなく走りに行った。そう遠くはないが、走ったことのない道。矢作川の対岸、旭高原元気村。大晦日は空いている。走り納めには絶好だな。

  画像の説明

  雪が路肩にチョロチョロあったりもして、予定通りの道を走らなかったが、ラリージャパンの最終スペシャルステージを逆走した。意外なほどに急勾配、道幅の広さだった。

  しかし、クルマは暖まってこないとダメだな。全てが滑らかになる。エンジンの回転、シフトの入り方、暖房が効いてこなければ運転操作も硬いし、靴底も暖まらないとヒールアンドトゥも上手くいかない。


2022-12-30(Fri) ロードスターのバッテリを交換

  先日のアクセラのバッテリ交換に引き続いて、ロードスターのバッテリを交換した。

  2度目の車検を終えて5年経過。12.36Vとまだまだ大丈夫そうだし、別に通勤に使っているわけでもないので、突然死したとて問題はないのだが、ちょっとしたチューニングを兼ねているので、ちょっと早いが交換するのである。

  交換するバッテリはパナソニックの40B19Lという、ごく普通のヤツ。だが、標準サイズの46B24Lではない。幅が2割くらい短く、11kg→8kgと3割くらい軽いもの。ロードスター研究所というサイトのマネッコである。

  標準のものよりも性能が低く、そのぶん劣化も早いかもしれないが、4千円と安い。3kgも軽ければ、体感までしないまでも、走行性能や燃費に良い影響を与える可能性も考えられる。

  画像の説明

  例によって、先のメモリバックアップ用電源装置(ラベルプリンタ型)を適用してみたが、ロードスターの場合、時計とオーディオの設定くらいで、ほとんど失う情報がない。なので、マイナスのワニ口をいい加減に噛ませておいたら外れてしまって保持に失敗してしまったw。

  それより失敗だったのはバッテリの交換作業時。マイナス極を先に外したり、端子に軍手を被せたり、慎重に作業をしたつもりだったが、取り付けの際、プラス極のナットをラチェットで締めている時に、ラチェットの柄の部分をマイナス極に軽くヒットしてしまい、マイナス極がわずかに溶けてしまった。あわわわ……こういうのって、結局は経験なのかなぁ、反省。

  画像の説明

  ちょっと失敗だったが、あっという間に作業が終わってしまったので、ついでに室内のエアフィルタを交換することした。結構、大掛かりに室内の内装を外す必要があるのだが、先の記憶以上に手が入らず、フィルタカバーが外せない。コネクタの外し方も忘れている。

  画像の説明

  コネクタは正面から見える爪を寄せるようにして外すんだな。どうにかこうにか外して、真っ黒になっているフィルタを外して捨て、新しいものに張り替えた。

  最近、遠出していないな。イヤと言うほど走りたい。スローパンクチャが気になっているのも大きい。早くタイヤを修理しなければ。


2022-12-26(Mon) フェアリイ・冬

  リアルに「フェアリイ・冬」みたいな事態が起きた。

  よくわからない上の意向で、部員にすごい評価が付いた。そして、当人からその評価について問いただされた。経緯は知っていたから、一切を包み隠さず当人に伝えた。伝えるかどうか、迷った挙げ句に伝えないことを選択していたのだが、問いただされれば、伝えない理由はないし、その義務もあるはず。

  当人は評価を喜ぶどころか、涙声で悔しがっていた。正しい。そして悲しい。当人の努力は認めるが、適切な評価じゃない。それを悔しがる部員は本当に真摯な人間だ。若いのに尊敬に値する。そういうマトモな人間に対しては、過当な評価が逆効果になると思わなかったのか。マトモじゃない人間には想像できなかったのだろう。そんなクソな評価を付けた上にも、結果それを跳ね返せなかった自分にも、本当に腹が立つ。

  勲章をもらってもおかしくはない。しかし、適切な章じゃない。苦労を認めた上での受章とはいえない。勲章は返せない、捨てることもできない、壊すこともできない。この歳になって、ブッカー少佐の気持ちを味わおうとはな。

  動かない限り、もう春は来ないように思える。


2022-11-20(Sun) アクセラのバッテリを交換

  現状、特段の問題は出ていないが、これまでの経験からも、バッテリが弱ってエンジンが始動できない現象は突然にやってくるので、本格的な冬を目の前に交換を検討し始めていた。

  アクセラは中古で買ってもうすぐ4年になるが、2016年の秋に登録のクルマなので、もう6年以上も使っている計算になる。まだバッテリの電圧は12Vチョイあるので、もう少し粘れそうではあるが、10年に対して折返し点は過ぎているし、予防交換しても悪くはないタイミングだろう。

  最近の車は電装品が多く、バッテリを外してしまうと、いろいろな設定が飛んでしまう。時刻やラジオ局くらいならいいのだが、ステアリングの切れ角やパワーウィンドウの停止位置、挙げ句にはバッテリの状態みたいなものも記憶しているらしく、それらを飛ばしてしまうと再設定が面倒らしい。

  というわけで、だいぶ以前にこんなものを製作していたのであった。メモリバックアップ用電源装置(ラベルプリンタ型)である。

  これ系の製品は多くの種類が市販されているが、バッテリターミナル、OBDコネクタ、アクセサリソケットなどに接続するようになっている。しかし、バッテリターミナルだと作業の邪魔だし、OBDコネクタだと対応コネクタがいるし、アクセサリソケットだとナビが動くから電源容量が不足するはずだ。ヒューズ電源から供給するのが一番だと思うがそういう製品は見当たらない。というわけで自作したのであった。ラベルプリンタを使ったのは、単に12Vの電池ボックスとして格好だったから、である。

  画像の説明

  アクセラの場合、室内ヒューズボックスの5番が常時供給なので、そこに12Vを供給するヒューズを挿し、マイナス側のミノムシを適当な金属部分に噛ませる。これでメモリバックアップ状態のハズである。

  あとはバッテリを交換する。用意したバッテリはTuflong STANDARDという製品の75D23L。本来はアイドリングストップ車なのでQ-85指定なのであるが、i-stopはうっとおしいので、長らく走り出すとすぐにボタンでオフにしてしまっており、遂には先日よく知られているウラ技によって強制オフ状態にしてしまったので、めでたく充電制御車対応のバッテリにダウングレードしてしまうのである。

  画像の説明

  バッテリの端子が外れにくかったり、外した端子に軍手を被せて短絡の保護をしたり、重くて取り出しに手間取ったり、通り一遍の苦労をしつつ、無事に交換完了。エンジンを始動し、設定が残っているっぽい様子を確認。メモリバックアップにも成功したようだ。ちょうどそこにあるラベルプリンタで交換日を印刷して貼っておくかw。

  このアクセラ、特段の不具合を起こすこともなく、MT車であることもあって普通に気分良く乗れている。これからも長らく活躍していただきたい。次は、ぼちぼち5年経つ、ロードスターのバッテリも交換だなぁ。


2022-11-12(Sat) ラリージャパン、サファリドライブ

  何度か観に行っているF1に対して、これまでほとんど興味のなかったラリーだが、近所で開催されるとなればちょっと話が違ってくる。

  さすがにチケットを買って観戦するつもりまではないものの、ちょっと調べたら、なんとあの旧伊勢神トンネルがコースに含まれている。高専時代に心霊スポットとして知り、何度か通ったこともあるが、あそこをブッ放すってマジか。

  そもそも、コースを調べたら、だいぶガチで「酷道」レベルのコースを走るようだ。「酷道」好きで、豊田の多くを踏破しているオイラでも、まだ攻めていない道が含まれている。ラリーって、もう少し広い道を走る印象だったので、これにはだいぶ驚いた。

  ラリーに対する知識のあまり多くないオイラだが、全力でタイムアタックするSS区間と、一般車に混じって規定時刻ピッタリの到着を目指すリエゾン区間があることくらいは知っている。じゃ、ドライブがてら、時間に合わせてリエゾンを逆走すれば、猛獣見物を兼ねられて、一石二鳥ではないか。

  ラリージャパンの公式ではリエゾンは非公開とあったが、トヨタ系のサイトには、ほぼ完璧に読み取れるコースマップがあったので、タイムテーブルと突き合わせてドライブコースを組み立てる。いつもとちょっと違ったドライブ計画。ある意味、オレラリーの開催である。

  で、いつもより少し早い、昼前に出発。豊田の街中を抜け、イベントの中心地である豊田スタジアムにさしかかる。結構な人出だが、渋滞と言うほど混んではいない。そのまま301号に向かうと、後ろからラリーカーが。きたきたッ!しかし、左折車線を直進したり、ゼブラを使って追い越したり、ちょっとお行儀の悪い運転である。まぁ、リエゾンとはいえ、完璧に日本の交通事情に沿うのは難しいわな。

  画像の説明

  ちょうどヒョンデのマシンが後ろからきたので、前に入れて見物する。ヒョンデのラリー2のマシンだ。そこそこウルサく、排気も少しニオう。なるほど。

  そのまま301号を進み、473号に入り、SS8/11 Nukata Forestのスタート地点付近から、リエゾン区間の逆走を始め、向かってくるラリーカーを迎撃する。おー、大漁大漁。次々にすれ違う。記念撮影はドラレコにお任せである。なんちゃってラリーカーも走っているようだが、プライベータも含まれているので、ナンバーが付いていることで、それを識別することはできない。まさかと思ったコペンは競技車であった。中にはカニでキジでマツダのディスリで有名な国沢氏のクルマも含まれていた。

  画像の説明

  少しコンビニで小休止して岡崎方面へ。少し早いので、ワザと遠回りしてから、再びリエゾン区間の逆走を始め、SS13/14 Okazaki Cityに向かうラリーカーを迎撃する。それでもあまりラリーカーが来ないので、退避車線を見つけて小休止すると近づいてくる爆音が。2枚だけX100Sで撮影。1枚目はブレたが、2枚目は割とちゃんと撮れた。と、なんとそれはトップクラスであるカーナンバー1のオジエ選手だった。ちょっとラッキー。その後には、カーナンバー18の勝田選手ともすれ違っていた。まぁ、そこそこ計画したとはいえ、十分すぎる豊作であった。

  画像の説明 画像の説明

  その後、再び三河湖の南を301号で進みSS9/12 Lake Mikawakoのスタート地点付近を抜け、県道35号へ。趣味である通行止めどん詰りチェックをこなしてから、420号で帰路につく……が、紅葉の香嵐渓がオンシーズンであることを忘れていた。渋滞に巻き込まれ、少し回避して帰宅。

  画像の説明

  実は、10月末に左リアがスローパンクチャを起こしていることが判明しており。その原因はおそらく、10月中旬に発見し抜去した金属片なのだが、継続的に空気圧をチェックすることで、スローパンクチャの進行速度は確認済みなので、それを知りつつも、ちょっとドキドキを隠せない200km超ドライブなのであった。帰宅しても190/200kPaは確保されていたが、早く直さなきゃな。


2022-10-26(Wed) 腕をブン回すボス

  画像の説明


2022-09-29(Thu) ウォッチをウオッチ

  このブログでも何度取り上げているが、長らくナビホークというシチズンの航空腕時計を愛用している。前回は日記エントリにしなかったが、2020年9月に5度目の電池交換をしたところだ。購入日は1999年6月26日とあるので……え、23年も使ってんのか。

  しかし、最近は出勤しないので腕に巻く機会が激減している。カラオケの練習の時に3時間を計測したり、ジョギングしている時間を計測したり、どっちもストップウォッチとしての利用だな。

  が、ここ数ヶ月、右上のボタン、ストップウォッチのスタート、ストップに使うボタンが徐々に渋くなり、とうとう戻ってこなくなってしまった。えー、電池交換までまだ何年もあるし、それだけのためにメンテに出すのもなぁ。結構、お金が掛かるし……。

  ストップウォッチはあったほうがいいのだが、ストップウォッチもなぁ……というワケで、ちょっと見栄えのする安い腕時計を探すことにした。アリエクでいいかな。

  ところが、ちょっと見栄えのするのはアリエクでも2000円くらいする。信頼性の低いものにそんなに出すなら、カシオのがいいわ。というわけで、カシオで探す。G-SHOCKにコダわりはないが、アナログが好きなので、ストップウォッチ付きとなると、ほぼアナデジコンビタイプに絞られる。

  ゴチャっとしているのもイヤじゃないが、針の視認性が低いのはイヤである。黒地に白は悪くないが、液晶部分が灰色だと浮いている感じで違和感。ナビホークは全体に灰色地なのでよかったのだが……と、最近は液晶部分が反転しているものもある。液晶表示は見やすいとはいえないが、デザインとしては悪くない。

  画像の説明 画像の説明

  と、結局、散々に迷った挙げ句、AEQ-110W-1BJHという製品にした。4230円。地味だが、パッと見G-SHOCKで、それほど安っぽくは見えない、と思う。

  最後まで迷った製品は、似たような感じでソーラー稼働だったのだが、ストップウォッチが1時間までという制約と、ソーラーも充電池の交換が必要な場合があると聞いたことがあるので外した。

  画像の説明

  で、26日に届き、フムン、実物も悪くないな、と思っていたところで、偶然にG-SHOCKの新作に関するニュースを目にした。値段は5倍くらいするが、なんだか、デザインコンセプトがすごく似ている気がするな。


2022-09-12(Mon) 結局、効果音生成ツールを改良する

  一応、効果音生成ツールは一応の完成を見たのだが、やりだすと、そう簡単には止まらないのである。

  ゲームの効果音にノイズは欠かせない。そのため、ホワイトノイズ、ブラウンノイズ、ピンクノイズ、パープルノイズが使えるようになっているが、それだけでは足りなすぎるのである。バリエーションが欲しい。

  で、ノイズに音程があるかというと、微妙である。ノイズを音程に分解すると、その分布によって上記の「色」が決まるのだが、それなりに広く分布しているので、ノイズには音程がないと言える。

  んが、一方で、3,8,5というノイズを、3,3,8,8,5,5と加工すれば、低音側に遷移することに疑いはないのである。まぁ、厳密な定義はともかく、指定の方法はトーンに合わせて、440.0を標準とし、詰めたり、間引いたりすることで、ノイズに音程変化が起こせるようにしてみた。

  なんだかんだコードを書いているうち、不具合があることに気付き、割と大きな追加/修正になってしまったが、表現力が大きく上がった気がする。

 @length = 1.0
 
 mod1 = it = {}
 it[:device] = 'generate'
 it[:length] = @length
 it[:type] = 'square'
 it[:freq] = [8]
 it[:amp] = <<AMP
 #--------------25--------------50--------------75-------------100
 ................................................................/
 AMP
 
 noise1 = it = {}
 it[:device] = 'fm'
 it[:length] = @length
 it[:type] = 'pink_noise'
 it[:freq] = [20]
 it[:amp] = <<AMP
 #--------------25--------------50--------------75-------------100
 ................................................/
 ................................................................/
 /
 AMP
 it[:index] = 1.5
 
 @connection = [ mod1, noise1 ]

  こんな記述で、ロケット噴射音っぽいの<聴いてみる>を作ることができた。これは使えるな、そろそろシューティングの製作に戻ろう。


2022-09-10(Sat) 結局、効果音生成ツールを自作する

  先日、ノイズを生成できるようになってから、効果音生成プログラムに組み込んでアレコレしていたのだが、アレコレしているうち、効果音とは、基本となる波形に様々な変化を繰り返し重ねていくことによって生成するものだということがわかってきた。

  主には、正弦波、矩形波などの生成、加算、変調、音量変化などである。しかし、その方法を汎化して記述するにはどうすればいいのか。そもそも変調とは何なのか。

  考えれば考えるほどわからなくなり、コードを書いては試し、何度も書き直した。コードの追加ではなく、こんなに何度も、構造ごと変えては、頭から書き直したのは初めてだ。変調を追求していくうちに、FM音源に行き当たり、周波数変調、位相変調、YM2151(OPM)の仕組みにまで踏み込んでしまい、オペレータのコネクションを再現するために、RPNっぽい手法まで実装することになった。

  結局、ほぼ、シンセサイザを自作した、というレベルではないだろうか。ヤクの毛刈りどころか、3頭はバーベキューにして食い尽くした気分だ。

  最終的には、以下のような記述で音を作る。以下は、soxのサンプルにあるパイプオルガンによるAm7を再現するもの。typeで正弦波(sine)を指定、freqで周波数(A3, C4, E4, G4)を指定、envでエンベロープ(音量変化)を指定。最後にそれらをRPN記法で重ねていく。

 @length = 1
 
 car1 = it = {}
 it[:device] = 'generate'
 it[:length] = @length
 it[:type] = 'sine'
 it[:freq] = <<FREQ
 #+BC+D+EF+G+A+BC+D+EF+G+A+BC+D+EF+G+A+BC+D+EF+G+A+BC+D+EF+G+A+BC+D+EF+G+A+BC+D+EF+G+A+BC+D+EF+G+A
 #--1----(55)1--2---(110)2--3---(220)3--4---(440)4--5---(880)5--6--(1760)6--7--(3520)7--8--(7040)8
 ..................................../
 FREQ
 it[:amp] = <<AMP
 #--------------25--------------50--------------75-------------100
 ................/
 AMP
 
 car2 = it = {}
 <略、C4 の正弦波>
 
 car3 = it = {}
 <略、E4 の正弦波>
 
 car4 = it = {}
 <略、G4 の正弦波>
 
 add = it = {}
 it[:device] = 'add'
 
 env1 = it = {}
 it[:device] = 'env'
 it[:length] = @length
 it[:amp] = <<AMP
 #--------------25--------------50--------------75-------------100
 /
 ................................................................/
 ................................................................/
 ................................................................/
 ................................................................/
 ................................................................/
 /
 AMP
 
 @connection = [ car1, car2, add, car3, add, car4, add, env1 ]

  これでwavファイルが生成される<聴いてみる>。

  もちろん、加算はもっとも基本的な演算で、真骨頂は変調である。インベーダのUFOの音っぽいの<聴いてみる>とか、パックマンのモンスタの音っぽいの<聴いてみる>とか、R-TYPEのショット音っぽいの<聴いてみる>とか、ダライアスのレーザ音っぽいの<聴いてみる>とか、割と直感的な記述により生成することができる。

  今回、FM音源のことを調べていて、ものすごく少ないパラメータ(バイト数)で音色が作れることが、当時のPCの性能事情に即していたこと。反面、音色作りがものすごく難しかったことを、改めて知った。実際、自分もX1のFM音源をアレコレしていたが、ちっともイメージしたような音色が作れなかった。

  とはいえ、アレよりはだいぶマシだと思うが、このツールでもイメージ通りの音に近づけていくのは難しい。近づけようとしても、意外な音ができて、その音が気に入ってしまったりする。ま、それはそれでいいんだが。