プログラムを読んでみよう③ ―― サンプリングsampling.c
スプーンを打つ平均時間間隔が求められたら、次はその時間間隔で入力される命令コードを読みとる処理へ移ります。sampling.cの中のSampleHits()関数で処理します。
基本的には、図6のようにしかるべきタイミングでスプーンが打たれたかどうかをチェックするだけです。
しかし人間の操作すること、完全に厳密なリズムでスプーンをたたくことは至難のわざです。そこで、本来のタイミングの前後いくらかの時間の間にスプーンが打たれればそれを「打った」と判断するように「やさしさ」を設計しています。本プログラムでは、図7のようにスプーンを打つ平均時間間隔の±25%の間にスプーンをたたけば、たたいたと認識するようにしています。
ではプログラムです。
まず23行目では音をサンプリングする時間幅を求めています。±25%ということは、幅は50%に相当しますので、2で割ることで求められます。26行目のwaittimeは、冒頭の4発目から最初の音のサンプリングを開始するまでの時間です。変数intervalには先にMeasureIntervals()で求めたスプーンをたたいた平均時間(=サンプリング間隔)が入っています。ここから、sampletimeの半分、つまりはサンプリング間隔の25%を引くことで、図7における計測の4発目からサンプリングを始める水色の領域の手前までの時間が求まります。27行目では時間待ち関数Wait1msec()に求めた値を渡して時間待ちしています。
この時点では、まだ計測のときに点灯させてLEDが灯ったままですから、29行目で消灯させます。27行目の時間待ちがちょうどいい点灯持続時間としても働いています。
32行目はIsHit()関数を呼んで、先に求めたsampletime時間の間、スプーン音の有無を調べます。音が検出されれば、命令コードを入れる変数codeに1を入れ、34行目で1番目のLEDを点灯させます。
40行目では、命令コードの2発目以降のための待ち時間を求めています。1発目のタイミングから+25%過ぎており、次のサンプリングタイミング-25%までの時間は、intervaltime - sampletimeで求められます(図8)。
2発目から4発目までは点灯させるLEDが違うだけなので、42行目からfor文で3回処理を繰り返しています。繰り返しのための変数iを2から4までにしているのは、MeasureIntervals()のときと同様、そのまま点灯させるLEDの番号に使うためです。さて、for文の中では43行目にシフト演算があります。4回のスプーンたたきによる命令コードを変数に順に入れていく工夫です。スプーン音があった場合は47行目で1を加算して、それが次のループで一桁シフトされ…を繰り返します。第8話の論理演算を覚えている方は、47行目の記述がcode = code | 1 ;
でもよいことに気づかれるでしょう。
このfor文は3回回すだけですので、しかるべきタイミングでスプーンを打ったとき、この変数codeがどのように変化するか机上でトレースしてみてください。もちろん、ここだけ取りだしたテストプログラムを作ってSM+で見てみるのがより確実です。