前回は,一続きの処理をタイマー割り込みを使って分割して行うサンプルを紹介しました。このような処理は,JavaScriptや組込みプログラミングなど,さまざまな場面で必要になります。
今回は,より複雑な処理を見やすく書く方法について説明します。
オートリピートをどう作るか
さっそくサンプルプログラムを見てみましょう。これはメトロノームで,数字は1分間の拍数(いわゆるBPM)です。
リスト1 metronom.html
<HTMLM><HEAD><TITLE>metronom</TITLE>
<SCRIPT type="text/javascript"><!--
var tempo = 0;
var accval = 0;
var arm = 0;
var t0_interval = 50;
var repeat_delay = 0;
var repeat_val = 0;
function update_tempo(val)
{
tempo += val;
if (tempo < 10)
tempo = 10;
else if (tempo > 300)
tempo = 300;
document.getElementById("tempo").innerHTML = tempo + "";
}
function int_t0()
{
accval += tempo;
if (accval >= 60000 / t0_interval) {
accval -= 60000 / t0_interval;
arm ^= 1;
document.getElementById("box").style.marginLeft = (arm)? "0px" : "200px";
}
if (repeat_delay <= 0)
;
else if (repeat_delay < 10)
repeat_delay++;
else
update_tempo(repeat_val);
}
function press_plus()
{
update_tempo(repeat_val = 1);
repeat_delay = 1;
}
function press_minus()
{
update_tempo(repeat_val = -1);
repeat_delay = 1;
}
function release()
{
repeat_delay = 0;
}
function init()
{
tempo = parseInt(document.getElementById("tempo").innerHTML);
setInterval(int_t0, t0_interval);
document.onmouseup = release;
}
// --></SCRIPT>
</HEAD><BODY onload="init()">
<H1>metronom</H1>
<DIV id="box" style="margin-left:0px; width:50px; height:50px; background:#f00;">
</DIV>
<P><BIG><BIG>
<SPAN onmousedown="press_minus()">[-1]</SPAN>
<SPAN id="tempo">120</SPAN>
<SPAN onmousedown="press_plus()">[+1]</SPAN>
</BIG></BIG></P>
</BODY></HTML>
[-1]や[+1]のボタンを押し続けると,連続して数字が変わるようになっています。いわゆるオートリピートです。ソースを見る前に,どうやって実現するかを考えてみてください。ヒントですが,ボタンが離されたときに発生する割り込みを利用しています。
リピート処理をしているあいだ,メトロノームの動作が止まってもいいのであれば,処理を並列的に行う必要はありません。しかし,リピートしているあいだもメトロノームの動作を続けたいのであれば,タイマー割り込みの中で2つの処理を行う必要があります。このような場面は,プログラムを書いていればいくらでもでてきますね。
まずメトロノームの処理ですが,50ミリ秒のタイマー割り込みをそのまま使うと,飛び飛びの間隔しか実現できません。たとえば毎分60拍の場合は,タイマー割り込み20回で1拍になります。その1つ上は,タイマー割り込み19回で1拍ですが,これは毎分に換算すると約63拍です。つまり,その中間の毎分61拍という指定はできないことになります。これではメトロノームとして困りますね。
そこで解決策として,拍数を変数に積算していき,変数がオーバーフローしたときに拍を刻むようにします。拍数が大きければ,頻繁にオーバーフローすることになります。たとえは毎分60拍のとき,50ミリ秒ごとに変数に60を加算すると,1秒で1200増えます。このときに拍を刻んで変数を1200減らせば,余った分がきちんと残るようになります。
毎分61拍の場合,先ほどのやり方では正確に刻むことはできませんでした。しかし積算方式であれば,まず20回目の割り込みで1220になります。ここで拍を刻んで1200を引くと,20残ります。次は,通算40回目の割り込みで1240になり,拍を刻んで40残ります。次は60回目で1260になり,60残ります。そして,79回目で1219になり,19残ります。このように,自動的に20回と19回が組み合わさって,平均すると毎分61拍が刻めることになります。
次に,これと平行して,オートリピートの処理を行います。オートリピートは,スイッチを押すとまず1回カウントされ,押し続けると連続でカウントされるものです。キーを押し続けると連続で文字が入力されますが,これをマウスでもおこなおうというものです。
マウスの場合,押したときに一度割り込みが発生しますが,そのあとは何も起きません。従って,押したときにグローバル変数をセットしておいて,タイマー割り込みでリピート処理をおこないます。タイマー割り込みをそのまま使うと,押した瞬間から毎秒20カウントのスピードで増えてしまいますので,最初の10カウントは無視するようにします。また,マウスを離したら,この処理を止めるようにします。
グローバル変数に保存するのは,マウスを押してからどれくらい経ったかと,押されたのがどのボタンかの2つです。前者は,最初の10カウントを無視するのに使います。後者は,リピートのときに数を増やすのか減らすのかを判断するのに使います。
こうして,2つのタイマー処理が並列に行えるようになりました。

