From Karaoke
女房に誘われて、久しぶりに からおけ へ行ってきた。入場料がわりに頼む必要 がある、ドリンク(430円)と、一人80円/時間の部屋代だけで済むとは、一体これで 儲かるのかと心配になる。
一曲毎に採点してしてくれるってのは、昔有ったかなあ? 今日の最高得点は、89 点だった。しつこく同じ曲を何度も歌ったけど、採点はぶれなかった。一体、どう いう仕組みなんだろう? ちょっと気になったぞ。後、曲毎に消費カロリーが 表示されてたけど、こちらは、ぶれが無い事からすると、DBにでも保存してるのを ただ表示してるだけだな。カロリーを気にするおばさん対策と思うぞ。
ちなみに、最高得点だった曲は、「鉄腕アトム」。この曲には思い出があって、 昔、東南アジアからの研修生受け入れの宴会で歌った時、大受けして彼らと 何度も歌ったんです。中国とか台湾にもしっかりアニメが浸透してるなあ。
と言う事で、少々音関係に興味が出てきた。マイクで音を拾って、波形を見る。 その波形を、周波数ドメインで見るなんて事をやってみたい。
波形の作成
が、マイクは、生憎持ち合わせていないので、取り合えずサイン波を合成してやればいいだろう。 どうせやるなら、昔学校で習った、AM放送の原理を再現してみる。 さて、言語は何にしよう? 最近触っていない ruby でやってみる。
#!/usr/local/bin/ruby DATAN = 256 # Wave size Sin_amp = { # Table of Freq vs Amp, freq => amp 3 => 0.3, } Car = 64 def cal_point(n) k =0.0 Sin_amp.each do |freq, amp| f = freq.to_i k = 1 + amp * Math::sin(2 * Math::PI * f * n / DATAN) end val = k * Math::cos(2 * Math::PI * Car * n / DATAN) val end DATAN.times do |n| printf("%.6f\n", cal_point(n) ) end
TBSなら954KHzだけど、このプログラムでは64(と言う、搬送波)、アナウンサーの声 の変わりに、周波数3と言う信号波、振幅は、搬送波に対して0.3倍(だから、変調率で言うと、 0.3で、良かったかな? 中学生の時、アマチュア無線の資格を取った時以来だから 、ちょっと自信なさげ)
出力は、電波じゃなくて、標準出力へ放出される。
FFT するぞ
次は、スペクトラムアナライザーの心臓部、FFTだ。うーん。FFTねぇ。ネットを 探してみると、rubyだけで書かれた例も見つかったけど、上と同じRubyを使うのも なんだかつまんないので、別の路線を考える。
思い出したのが、「プログラミング言語SCHEME」に出ていた例 これを頂いてきて、読み込みルーチンを付け加えてやろう。最近ご無沙汰してる gaucheを使う事にする。
#!/usr/local/bin/gosh ;;; Original by fft.ss ;;; Copyright (C) 1996 R. Kent Dybvig ;;; from "The Scheme Programming Language, 2ed" by R. Kent Dybvig (define DATA_FILE "wavein.txt") (define (w-powers n) (let ((pi (* (acos 0.0) 2))) (let ((delta (/ (* -2.0i pi) n))) (let f ((n n) (x 0.0)) (if (= n 0) '() (cons (exp x) (f (- n 2) (+ x delta)))))))) (define (evens w) (if (null? w) '() (cons (car w) (evens (cddr w))))) (define (interlace x y) (if (null? x) '() (cons (car x) (cons (car y) (interlace (cdr x) (cdr y)))))) (define (split ls) (let split ((fast ls) (slow ls)) (if (null? fast) (values '() slow) (call-with-values (lambda () (split (cddr fast) (cdr slow))) (lambda (front back) (values (cons (car slow) front) back)))))) (define (butterfly x w) (call-with-values (lambda () (split x)) (lambda (front back) (values (map + front back) (map * (map - front back) w))))) (define (rfft x w) (if (null? (cddr x)) (let ((x0 (car x)) (x1 (cadr x))) (list (+ x0 x1) (- x0 x1))) (call-with-values (lambda () (butterfly x w)) (lambda (front back) (let ((w (evens w))) (interlace (rfft front w) (rfft back w))))))) (define (fft x) (rfft x (w-powers (length x)))) ;;; below line's add by S.K. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (make-list file) (define (build lis x fp) (if (eof-object? x) (reverse lis) (build (cons x lis) (read fp) fp))) (call-with-input-file file (lambda (fp) (build '() (read fp) fp)))) (define (other-way-make-list file) ;; Care order of val list (with-input-from-file file (lambda () (port-fold-right (lambda (x lis) (cons x lis)) '() read)))) (define (do-fft file) (let ([x (make-list file)]) (for-each (lambda (v) (format #t "~s\n" (abs v))) (fft x)))) (do-fft DATA_FILE)
入力は、ファイル渡しで、出力は、標準出力に出す事にした。
表示部の作成
次は、結果の表示だ。グラフを書く事になるので、スマートに済ませてしまおう。 Rを使って、一機にpngファイルを作る事にする。
#!/usr/local/bin/Rscript # pdf(file="Rplots.pdf") png(file="Rplots.png") par(bg="grey95", mar=c(4,4,1,1), mfrow=c(2,1)) wave <- read.table("wavein.txt") plot(wave$V1, type="l") spec <- read.table("specout.txt") n <- length(spec$V1) s <- spec$V1[1:(n/2)] plot(s/n, type="h") dev.off()
お好みで、PDFにも吐き出せるぞ。
全部をまとめてみる。
さて、部品は揃った。後は、これらを結合するんだけど、相場は shell を使う んだろうけど、後々の事(拡張)を考えて、Makefileにしておく。 そうすれば、make am とか(今回)、FM波形が必要なら、適当に波形を合成して make fm とするだけだ。
am: ./AMgen.rb > wavein.txt ./FFT.scm > specout.txt ./disp.R xv Rplots.png #acroread Rplots.pdf
結果発表
某メーカーもびっくりの、よくばり表示器です。
参考にデジタル信号処理 を上げておきます。