BEEP
Table of Contents
beep
前回は、ioctlを調べた方がいいよって結論だった。ならば、ガチャして、も とえ、次のようにして、炙り出 せばよい。
sakae@fb:~ $ find /usr/src/usr.bin -name '*.c' | xargs grep ioctl -l /usr/src/usr.bin/backlight/backlight.c /usr/src/usr.bin/write/write.c /usr/src/usr.bin/morse/morse.c /usr/src/usr.bin/beep/beep.c :
色々出てきた中からbeepを選んでみた。だって、単純そうだから。morse.sなんてのも魅力的だけどね。 手元によりどりみどりのサンプルが有るって、贅沢このうえないぞ。
sakae@slfb:~ $ beep -h Usage: beep [parameters] -F <frequency in HZ, default 440 Hz> -D <duration in ms, from 50 ms to 2000 ms, default 150 ms> -r <sample rate in HZ, from 8000 Hz to 48000 Hz, default 48000 Hz> -d <OSS device (default /dev/dsp)> -g <gain from 0 to 100, default 75> -B Run in background -h Show usage
beep音を発生させるだけのアプリ。高級にサンプリング音源を使うっぽい。簡単に使い方を確認。
sakae@slfb:~ $ beep -D 500 -g 85
440Hzの音を500ms発生しなさい。ボリューム(音量)は85/100の位置でお願いします。 このゲインだけど、ウェーバー・ヘヒナの人体反応を取り込んでいないな。 ウェーバー・ヘヒナってのは、刺激は対数になるって奴だ。
sound dev
FreeBSDで音はどうやって設定するの? そんなのハンドブックを参照するのさ。 第7章 マルチメディア
sakae@slfb:~ $ dmesg | grep pcm pcm0: <Realtek ALC269 (Analog 2.0+HP/2.0)> at nid 20,21 and 18 on hdaa0 pcm1: <Realtek ALC269 (Rear Analog Mic)> at nid 24 on hdaa0 pcm2: <Intel Cantiga (HDMI 8ch)> at nid 3 on hdaa1
どんな音装置を認識してるかは、上記で調査できる。
sakae@slfb:~ $ cat /dev/sndstat Installed devices: pcm0: <Realtek ALC269 (Analog 2.0+HP/2.0)> (play/rec) default pcm1: <Realtek ALC269 (Rear Analog Mic)> (rec) pcm2: <Intel Cantiga (HDMI 8ch)> (play) No devices installed from userspace. sakae@slfb:~ $ ls -l /dev/dsp* crw-rw-rw- 1 root wheel 0x51 Sep 13 06:01 /dev/dsp0 crw-rw-rw- 1 root wheel 0x53 Sep 13 06:01 /dev/dsp1 crw-rw-rw- 1 root wheel 0x55 Sep 13 06:01 /dev/dsp2
このうちの何れが採用されてるかは、sndstatしてデフォになってるのがそれ だ。デフォは、dspってデバイス名になるんだな。 動作チェックは下記の様にして簡単にできます。
cat file > /dev/dsp
fileは何でもいいのですが、rubyのtar玉の中になるpi生成スクリプトで発生 したデータをfileにした奴がお勧めです。
ob$ ruby pi.rb 31415926535897932384626433832795028841971693993751058209 ....
こやつはバッハのフーガとか北島のサブちゃんの祭とか、世界のあらゆる楽 曲が含まれた偉大なアーカイブですからね。
尚、上記は、10年前にWindows7が付属してた、ThinPad SL510 にFreeBSDを入 れたリアルマシンです。
同じマシンでも、OpenBSDだと、
azalia0 at pci0 dev 27 function 0 "Intel 82801I HD Audio" rev 0x03: msi azalia0: codecs: Realtek ALC269, Intel/0x2802, using Realtek ALC269 audio0 at azalia0
そして、デバイスの扱かいも独自だ。 OpenBSD FAQ - Multimedia
on qemu
qemuでは、どうする? まとめは、こちら。 QEMU audio
これって、外部から輸入してくるんだな。
sakae@fb:~ $ qemu-system-i386 -audiodev help Available audio drivers: none dbus oss sdl wav
qemu-system-i386 -m 256 -nographic -no-fd-bootchk -s \ -net nic -net user,hostfwd=tcp::2022-:22 \ -audiodev oss,id=snd0 -device AC97,audiodev=snd0 \ -hda disk.img
ハンドブックにossなんて語句があったので、それを輸入指示。そしてそれを qemu内でどう扱うか指示するんだな。 Audio Codec 97 とか、その後継である、 High Definition Audio それから、おまけで、 I/O コントローラー・ハブ (ICHn) とか。Intel入っている風だ。
root@qemu:~ # dmesg | grep pcm pcm0: <Intel ICH (82801AA)> port 0xc000-0xc3ff,0xc400-0xc4ff irq 11 at device 4.0 on pci0 pcm0: <SigmaTel STAC9700/83/84 AC97 Codec> pcm0: measured ac97 link rate at 2134 Hz root@qemu:~ # cat /dev/sndstat Installed devices: pcm0: <Intel ICH (82801AA)> (play/rec) default No devices installed from userspace.
OpenBSDだと、qemuのパラメータを次のように変更。
-audiodev sndio,id=snd0 -device AC97,audiodev=snd0 \
こんな風に、認識してくれた。
auich0 at pci0 dev 4 function 0 "Intel 82801AA AC97" rev 0x01: apic 0 int 11, H ac97: codec id 0x83847600 (SigmaTel STAC9700) audio0 at auich0
gdb on qemu
ゲストOS側でも、ひょっとしてgdbが必要になるかと思って入れてみた。色々 な付属品がやってくるんでウザイ。
FreeBSD repository update completed. 33086 packages processed. All repositories are up to date. The following 17 package(s) will be affected (of 0 checked): New packages to be INSTALLED: boost-libs: 1.84.0 expat: 2.6.2 gdb: 14.1_2 gettext-runtime: 0.22.5 gmp: 6.3.0 icu: 74.2_1,1 indexinfo: 0.3.1 libffi: 3.4.6 libiconv: 1.17_1 liblz4: 1.9.4_1,1 mpdecimal: 4.0.0 mpfr: 4.2.1,1 python311: 3.11.9 readline: 8.2.10 source-highlight: 3.1.9_9 xxhash: 0.8.2_1 zstd: 1.5.6
入れる前後で、容量チェック。固定資産であるpkgのDBはどれぐらいなんだろ う。7Mのtar玉をダウンロードしてsqlしてたけど。。
sakae@fb14:~ $ df Filesystem 1K-blocks Used Avail Capacity Mounted on /dev/ada0s1a 3842364 1429996 2104980 40% / devfs 1 0 1 0% /dev sakae@fb14:~ $ df Filesystem 1K-blocks Used Avail Capacity Mounted on /dev/ada0s1a 3842364 2082472 1452504 59% / devfs 1 0 1 0% /dev
652Mも喰うんかい。そのうちの92Mは固定資産ってか、pkgの管理に使用されて いた。
which check
ここからが本題。ioctlが発生した時の挙動をチェックしたい。 何処を見ればいいの? まずはアプリ側。
userland side
beep.cの主要部分。
ob$ grep ioctl beep.c if (ioctl(f, SOUND_PCM_WRITE_CHANNELS, &c) != 0) errx(1, "ioctl SOUND_PCM_WRITE_CHANNELS(1) failed"); if (ioctl(f, SNDCTL_DSP_SETFMT, &c) != 0) errx(1, "ioctl SNDCTL_DSP_SETFMT(AFMT_S32_NE) failed"); if (ioctl(f, SNDCTL_DSP_SPEED, &sample_rate) != 0) errx(1, "ioctl SNDCTL_DSP_SPEED(%d) failed", sample_rate); if (ioctl(f, SNDCTL_DSP_SETFRAGMENT, &c)) errx(1, "ioctl SNDCTL_DSP_SETFRAGMENT(0x%x) failed", c); if (ioctl(f, SNDCTL_DSP_GETODELAY, &c) != 0) errx(1, "ioctl SNDCTL_DSP_GETODELAY failed"); while (ioctl(f, SNDCTL_DSP_GETODELAY, &c) == 0) {
この他で大事なのは、
f = open(oss_dev, O_WRONLY);
oss_dev
は、/dev/dsp の事だ。デバイスが使用する音データは、グレイコー
ドとやらを計算して用意してる。グレイコードと実数
sakae@fb:/tmp $ cc -g -O0 beep.c -lm sakae@fb:/tmp $ gdb -q a.out Reading symbols from a.out... (gdb) b beep.c:208 Breakpoint 1 at 0x401f82: file beep.c, line 208. (gdb) r Starting program: /tmp/a.out Breakpoint 1, main (argc=1, argv=0xffbfebb8) at beep.c:208 208 if (ioctl(f, SOUND_PCM_WRITE_CHANNELS, &c) != 0) (gdb) p c $1 = 1 (gdb) p f $2 = 3
FreeBSDではdebug用のバイナリーが用意されてるけど、オプチマイズされてて、 変数の細部を確認できない事がある。しょうがないので、自前でコンパイルし たよ。この例だと、モノラル動作を要求してるのだな。
212 if (ioctl(f, SNDCTL_DSP_SETFMT, &c) != 0) (gdb) p c $3 = 4096 (gdb) n 215 if (ioctl(f, SNDCTL_DSP_SPEED, &sample_rate) != 0) (gdb) p sample_rate $4 = 48000 221 if (ioctl(f, SNDCTL_DSP_SETFRAGMENT, &c)) (gdb) p c $5 = 131084 224 if (ioctl(f, SNDCTL_DSP_GETODELAY, &c) != 0) (gdb) p c $6 = 131084
次々と設定を施していく。
Breakpoint 2, main (argc=1, argv=0xffbfebb8) at beep.c:257 257 if (write(f, buffer, size * sizeof(buffer[0])) != (gdb) p buffer $10 = (int32_t *) 0x20654380 (gdb) p size $11 = 7200 264 while (ioctl(f, SNDCTL_DSP_GETODELAY, &c) == 0) { (gdb) p c $12 = 0
そして、データを計算して、そいつをdspに転送。これで演奏?が始まる。後 は演奏の進行に合わせてデータを供給。全てが終了したら、少し余韻を取って から(usleep(50000);)閉幕。
kernel side
次はカーネル側。デバドラなんで、だまっていてもチェックすべき場所は限定 される。カーネルのソースと言っても、全体の9割は、devの中にあるからね。
sakae@fb:/sys/dev $ fgrep 'Intel ICH (82801AA)' -r . ./sound/pci/ich.c: "Intel ICH (82801AA)" },
dmesgが報告してくるメッセージを手がかりにガサ入れ。
対応リストを見ると、一番初期のICHだね。最近だと、ICH7までサポートされ てる。
飛んで来そうな所は、ここかなあ?
static __inline void ich_wr(struct sc_info *sc, int regno, uint32_t data, int size) { switch (size) { case 1: bus_space_write_1(sc->nabmbart, sc->nabmbarh, regno, data); break; case 2: bus_space_write_2(sc->nabmbart, sc->nabmbarh, regno, data); break; case 4: bus_space_write_4(sc->nabmbart, sc->nabmbarh, regno, data); break; } }
watch kernel
どうも、dspと言う奴は、ロジック部とアナログ部(コーディックと称するよう だ)に分かれるようだ。上で捉えたのは、そのうちのロジック部。まずは、こ ちらを追跡。
logic
初回はopenに費される。
(gdb) bt #0 ich_wr (sc=0xb84e480, size=1, regno=<optimized out>, data=<optimized out>) at /usr/src/sys/dev/sound/pci/ich.c:235 #1 ichchan_setblocksize (obj=<optimized out>, data=0xb84e4e4, blocksize=<optimized out>) at /usr/src/sys/dev/sound/pci/ich.c:504 #2 0x00dcda99 in CHANNEL_SETBLOCKSIZE (obj=0xb99e490, data=0xb84e4e4, blocksize=1024) at ./channel_if.h:117 #3 chn_resizebuf (c=<optimized out>, c@entry=0x3cf8400, latency=<optimized out>, blkcnt=<optimized out>, blksz=<optimized out>) at /usr/src/sys/dev/sound/pcm/channel.c:1887 #4 0x00dce483 in chn_setlatency (c=0x3cf8400, latency=193585664) at /usr/src/sys/dev/sound/pcm/channel.c:1979 #5 chn_notify (c=0x3cf8400, flags=8) at /usr/src/sys/dev/sound/pcm/channel.c:2353 #6 0x00dcd6e1 in chn_resizebuf (c=<optimized out>, latency=<optimized out>, latency@entry=-2, blkcnt=<optimized out>, blksz=<optimized out>) at /usr/src/sys/dev/sound/pcm/channel.c:1842 #7 0x00dcbfd9 in chn_setparam (c=0xb9b8800, format=1048584, speed=8000) at /usr/src/sys/dev/sound/pcm/channel.c:2026 #8 0x00dcbd01 in chn_reset (c=0xb9b8800, fmt=1048584, spd=8000) at /usr/src/sys/dev/sound/pcm/channel.c:1145 #9 0x00dcff86 in dsp_open (i_dev=0xbb95400, flags=<optimized out>, mode=<optimized out>, td=0xcd00780) at /usr/src/sys/dev/sound/pcm/dsp.c:610 #10 0x00ed994e in devfs_open (ap=0xcb849e4) at /usr/src/sys/fs/devfs/devfs_vnops.c:1288 #11 0x0144efe8 in VOP_OPEN_APV (vop=0x1aa5fc4 <devfs_specops>, a=0xcb849e4) at vnode_if.c:434 #12 0x01108336 in VOP_OPEN (vp=0x11b89a8c, mode=<optimized out>, cred=<optimized out>, td=<optimized out>, fp=0xccf6e4c) at ./vnode_if.h:218 #13 vn_open_vnode (vp=0x11b89a8c, fmode=2, cred=0xcc6d000, td=0xcd00780, fp=0xccf6e4c) at /usr/src/sys/kern/vfs_vnops.c:444 #14 0x01107f6e in vn_open_cred (ndp=0xcb84b3c, flagp=0xcb84bec, cmode=0, vn_open_flags=16, cred=0xcc6d000, fp=0xccf6e4c) at /usr/src/sys/kern/vfs_vnops.c:331 #15 0x010fe52e in openatfp (td=td@entry=0xcd00780, dirfd=<optimized out>, path=0x4007cb "Z\377s*\276\235", <incomplete sequence \350\034>, pathseg=<optimized out>, flags=2, mode=0, fpp=0x0) at /usr/src/sys/kern/vfs_syscalls.c:1164 #16 0x010fe232 in kern_openat (td=0xcd00780, dirfd=193585664, pathseg=UIO_USERSPACE, mode=2, path=<optimized out>, flags=<optimized out>) at /usr/src/sys/kern/vfs_syscalls.c:1269 #17 sys_openat (td=0xcd00780, uap=0xcd00a18) at /usr/src/sys/kern/vfs_syscalls.c:1102 #18 0x0143deff in syscallenter (td=<optimized out>) at /usr/src/sys/i386/i386/../../kern/subr_syscall.c:188 #19 syscall (frame=0xcb84ce8) at /usr/src/sys/i386/i386/trap.c:1161 #20 0xffc03479 in ?? () :
数度continueすると、ioctlが顔を出してくる。
(gdb) bt 10 #0 ich_wr (sc=0xb84e480, size=1, regno=<optimized out>, data=<optimized out>) at /usr/src/sys/dev/sound/pci/ich.c:235 #1 ichchan_setblocksize (obj=<optimized out>, data=0xb84e4e4, blocksize=<optimized out>) at /usr/src/sys/dev/sound/pci/ich.c:504 #2 0x00dcda99 in CHANNEL_SETBLOCKSIZE (obj=0xb99e490, data=0xb84e4e4, blocksize=1024) at ./channel_if.h:117 #3 chn_resizebuf (c=<optimized out>, c@entry=0x3cf8400, latency=<optimized out>, blkcnt=<optimized out>, blksz=<optimized out>) at /usr/src/sys/dev/sound/pcm/channel.c:1887 #4 0x00dce483 in chn_setlatency (c=0x3cf8400, latency=193585664) at /usr/src/sys/dev/sound/pcm/channel.c:1979 #5 chn_notify (c=0x3cf8400, flags=8) at /usr/src/sys/dev/sound/pcm/channel.c:2353 #6 0x00dcd6e1 in chn_resizebuf (c=<optimized out>, latency=<optimized out>, latency@entry=-2, blkcnt=<optimized out>, blksz=<optimized out>) at /usr/src/sys/dev/sound/pcm/channel.c:1842 #7 0x00dcbfd9 in chn_setparam (c=0xb9b8800, format=1048584, speed=8000) at /usr/src/sys/dev/sound/pcm/channel.c:2026 #8 0x00dcc01d in chn_setformat (c=0xb9b8800, format=1048584) at /usr/src/sys/dev/sound/pcm/channel.c:2076 #9 0x00dd30f1 in dsp_ioctl (i_dev=0xbb95400, cmd=3221508102, arg=0xcb84b68 "\001", mode=<optimized out>, td=0xcd00780) at /usr/src/sys/dev/sound/pcm/dsp.c:1529 (More stack frames follow...)
更に進めると、終了待ちのループでグルグルするのだな。
(gdb) bt #0 ich_wr (size=<optimized out>, sc=<optimized out>, regno=<optimized out>, data=<optimized out>) at /usr/src/sys/dev/sound/pci/ich.c:235 #1 ich_intr (p=0xb84e480) at /usr/src/sys/dev/sound/pci/ich.c:651 #2 0x00fecd7a in intr_event_execute_handlers (ie=0x3c4c300, p=<optimized out>) at /usr/src/sys/kern/kern_intr.c:1167 #3 ithread_execute_handlers (ie=0x3c4c300, p=<optimized out>) at /usr/src/sys/kern/kern_intr.c:1180 #4 ithread_loop (arg=0xb99e500) at /usr/src/sys/kern/kern_intr.c:1268 #5 0x00fea0e5 in fork_exit (callout=0xfecb90 <ithread_loop>, arg=0xb99e500, frame=0x3abfce8) at /usr/src/sys/kern/kern_fork.c:1151 #6 0xffc0348e in ?? ()
codec
A/D とか D/A いわゆる、録音と再生をつかさどるアナログ部ね。どこで担当 してるかメーカー名で検索。
sakae@fb:/sys/dev/sound $ fgrep 'SigmaTel' -l -r . ./pcm/ac97.c
ここに登録されてる関数にBPを置いてみたけど、どれもヒットせず。ミキサー とかを利用しないと、呼び出される事は、無いのかな。それとも、インテルの 石の中に、すっぽり吸収されちゃって、出る幕が無いのかな?
STAC9700 こんな石のスペックシートでも眺めながら、お茶を濁しましょうか ね。
sys_ioctl
今度は、システムコールを追求
Breakpoint 1, kern_ioctl (td=0xccfdb40, fd=0, com=2150396949, data=0xcb61b68 "\002k") at /usr/src/sys/kern/sys_generic.c:727 727 AUDIT_ARG_FD(fd); (gdb) bt #0 kern_ioctl (td=0xccfdb40, fd=0, com=2150396949, data=0xcb61b68 "\002k") at /usr/src/sys/kern/sys_generic.c:727 #1 0x0108ef19 in sys_ioctl (td=0xccfdb40, uap=0xccfddd8) at /usr/src/sys/kern/sys_generic.c:709 #2 0x0143deff in syscallenter (td=<optimized out>) at /usr/src/sys/i386/i386/../../kern/subr_syscall.c:188 #3 syscall (frame=0xcb61ce8) at /usr/src/sys/i386/i386/trap.c:1161 #4 0xffc03479 in ?? () :
継続すると、
Breakpoint 1, kern_ioctl (td=0xccfe3c0, fd=10, com=2147775606, data=0xcb6bb68 "\017\003") at /usr/src/sys/kern/sys_generic.c:\ 727 727 AUDIT_ARG_FD(fd); (gdb) c Continuing. Breakpoint 1, kern_ioctl (td=0xccfe3c0, fd=3, com=3221508102, data=0xcb6bb68 "\001") at /usr/src/sys/kern/sys_generic.c:727 727 AUDIT_ARG_FD(fd); (gdb) c Continuing. Breakpoint 1, kern_ioctl (td=0xccfe3c0, fd=3, com=3221508101, data=0xcb6bb68 "") at /usr/src/sys/kern/sys_generic.c:727 727 AUDIT_ARG_FD(fd); :
fdに色々出てくるなあ。これって雑音? ならば
truss
sakae@fb:/tmp $ truss -o LOG ./a.out sakae@fb:/tmp $ grep ioctl LOG ioctl(3,SOUND_PCM_WRITE_CHANNELS,0xffbfeb64) = 0 (0x0) ioctl(3,SNDCTL_DSP_SETFMT,0xffbfeb64) = 0 (0x0) ioctl(3,SNDCTL_DSP_SPEED,0x404934) = 0 (0x0) ioctl(3,SNDCTL_DSP_SETFRAGMENT,0xffbfeb64) = 0 (0x0) ioctl(3,SNDCTL_DSP_GETODELAY,0xffbfeb64) = 0 (0x0) ioctl(3,SNDCTL_DSP_GETODELAY,0xffbfeb64) = 0 (0x0) ioctl(3,SNDCTL_DSP_GETODELAY,0xffbfeb64) = 0 (0x0) ioctl(3,SNDCTL_DSP_GETODELAY,0xffbfeb64) = 0 (0x0) ioctl(3,SNDCTL_DSP_GETODELAY,0xffbfeb64) = 0 (0x0)
北里柴三郎先生みたいに、純粋培養成功。
この場合は、ktrace/kdumpを使うより楽です。
rain
USBHDDのFreeBSDを利用してきたけど、ノーパソのコンソールは利用してない。 スケリーン・セーバーを動かして画面の焼け付け(LCDでそんな事が起きるかは、 不問)防止したい。OpenBSDならOFFに出来たんだけど。
手軽に使えるものないか? たとえばdialogとかでだ。が、どう探しても、そ んな機能は無い(当然か)。ならば、cursesだな。
cursesでメダカっぽい動きを表現する こんなのが有った。けど、余計なnumpy とかを利用してる。もっとシンプルなのでいいのにな。Demoに、rain.py なんてのがあった。これ、ピッタリですよ。
ただ、このまま使うと、線条降水帯の豪雨のごとく、すさまじい速さで雨が落 ちてくる。心臓に良くないぞ。もっと慈雨がいいんだけど。
ch = stdscr.getch() if ch == ord('q') or ch == ord('Q'): return elif ch == ord('s'): stdscr.nodelay(0) elif ch == ord(' '): stdscr.nodelay(1) curses.napms(50)
こんな風になってた。多分50ms間隔で雨が降る設定だろう。200に変更したら、 穏やかな雨になった。これにて一件落着。でもいいんだけど、どんな意味が有 るか確認。
Python 3.11.9 (main, Jul 9 2024, 01:16:22) [Clang 17.0.6 (https://github.com/llvm/llvm-project.git llvmorg-17.0.6-0-g600970 on freebsd13 Type "help", "copyright", "credits" or "license" for more information. >>> import curses >>> help(curses)
pythonに聞けがいいんだろうけど、nodelayは掲載されていなかった。中途半 端です。こういう時は、manだな。
sakae@fb:~ $ man nodelay curs_inopts(3X) curs_inopts(3X) NAME cbreak, nocbreak, echo, noecho, halfdelay, intrflush, keypad, meta, nodelay, notimeout, raw, noraw, noqiflush, qiflush, timeout, wtimeout, typeahead - curses input options nodelay The nodelay option causes getch to be a non-blocking call. If no input is ready, getch returns ERR. If disabled (bf is FALSE), getch waits until a key is pressed.
1/0のように一見して時間の指定かと思ったら、True/Falseのエイリアスとし て利用してんだな。デモなんだから、紛らわしい記述はヤメてくれ。
なお、こやつはpython3系で姿を消している。 python2 には存在してた。3系は、もはやプラットフォームだから、何か他の ものと混ぜて使ってくださいって方針になっちまったのか。
ついでに見つけた、これ、格好いいな。 matrix-rain
etc
複数の最新生成AIサービスを無料で体験できる、GMO教えてAIの「天秤AI」がスゴイ
価格.com のAI版ってか、お試し自由の奴だな。
Windows NT系はUNIX系と比較して設計上のどういう点が先進的だったのか?
温故知新 先進的だったのね。