OLのスタートチャイマー
1. はじめに
OL(オリエンテーリング)のスタート時にスタートチャイマーを使うことがあります。これは、公式な現在時刻を示すとともに、スタートのタイミングを音で知らせてくれる大きな時計板です。スタートは、多くの場合、2分毎の00分に行われるため、その数秒前に呼び鈴がなり、スタート時に別の音で知らせてます。
主に子供向けにオリエンテーリングを開催してきているのですが、いつもスタートチャイマーは借りてばかり。それなら自前で用意しようと思いました。
これを、最初はハードウエアで作ろうと思いました。時計の心臓部はRaspbery piで、表示部は大型7セブLEDで作れば、技術的には可能です。しかし、大型7セグのLEDの価格を見てビックリ。1個2000円程度~でした。時計の性質上、LEDは6個必要です。この他、部品等を考えると、予算は2万円を超えそうです。また、ハードウエアを作る作業時間を考えると(これは、楽しくもあるのですが)、ちょっとしり込みしてしまいました。
それなら、ソフトウエアで作って、タブレットPCに表示させるのでも良いのではないか?と思いました。正規のスタートチャイマーより小さくて存在感に欠けますが、機能として十分でしょう。
というのが、モチベーションで、スタートチャイマーをソフトウエアで作ってみることにします。
2. Python にて作ってみる
まずは、勉強もかねてPython
にて作ってみることにしました。
時計の部分は、簡単なデジタル時計Pythonスクリプトのページを参考に、音を出す部分は、モリカトロン開発者ブログを参考にさせていただきました。
こちらが、そのコードです。ここでは、音を出す部分で、pyaudioパッケージを使用しています。
from tkinter import * from datetime import datetime import numpy as np # install : conda install numpy import pyaudio # install : conda install pyaudio import time # オーディオ準備 SAMPLE_RATE = 44100 def play(s: pyaudio.Stream, freq: float, duration: float): samples = np.sin(np.arange(int(duration * SAMPLE_RATE)) * freq * np.pi * 2 / SAMPLE_RATE) s.write(samples.astype(np.float32).tostring()) p = pyaudio.PyAudio() # ストリームを開く stream = p.open(format=pyaudio.paFloat32, channels=1, rate=SAMPLE_RATE, frames_per_buffer=1024, output=True) # キャンバス準備 c = Canvas(width = 1000, height = 700, background = '#000000') c.pack() try: while True: now = datetime.now() s = '{0:0>2d}:{1:0>2d}:{2:0>2d}'.format(now.hour, now.minute, now.second) c.create_rectangle(0, 0, 1000, 700, outline = '#000000', fill = '#000000') c.create_text(500, 350, text = s, font = ('', 200), fill = 'red') c.update() time.sleep(0.2) if not now.minute % 2 == 0 and now.second == 0: play(stream, 261.626, 2.0) # note#60 C4 ド if not now.minute % 2 == 0 and now.second == 55: play(stream, 261.626, 0.5) # note#60 C4 ド if not now.minute % 2 == 0 and now.second == 57: play(stream, 261.626, 0.2) # note#60 C4 ド if not now.minute % 2 == 0 and now.second == 58: play(stream, 261.626, 0.2) # note#60 C4 ド if not now.minute % 2 == 0 and now.second == 59: play(stream, 261.626, 0.2) # note#60 C4 ド except: pass
実行すると、ちゃんと時刻が表示され、音もなります。
しかし、時間が経つと秒の表示が飛び飛びになり、音も正規の時刻からずれたり鳴らなかったりします。これでは、スタートチャイマーとして使えません。
このプログラムでは、音を毎回作成しているので、処理が重いのかと思い、pygame
を使って、予め用意した*.wavファイルを実行するようにしてみましたが、やっぱり症状が同じ。。。
インタプリタだから駄目なのかとおもい、pyinstaller
で*.exeファイルを作ろうとしましたが、こちらは何故か上手くコンパイルができず。。。
マルチスレッドとか使えば良いのかもしれませんが、この作業は手っ取り早く終わらせたかったので、ここで断念することにしました。
3. HTML + Javascriptで作ってみる
かといって、スタートチャイマーを諦めきれなかったので、今度はHTML+Javascriptで作ってみることにしました。時計部はできるとしても、果たして音が出るのか心配でしたが、簡単に調べただけでもHTML5で音が扱えることが分かり一安心。早速作ってみることに。
時計部に関しては、にしし ふぁくとりーのページ、音関係はJavaScript で Beep 音を鳴らす方法を参考にさせていただきました。
できあがったhtml(index.html)コードがこちら。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <style type="text/css"> <!-- span.time1 {font-size: 15em; color:red;} --> </style> <title>START TIMER</title> </head> <body> <span class="time1"><p id="RealtimeClockArea2">時計表示</p></span> <button onclick="beep()">START</button> <script src="start_t.js"></script> </body> </html>
javascript(start_t.js)はこちら。
function set2fig(num) { var ret; if( num < 10 ) { ret = "0" + num; } else { ret = num; } return ret; } function showClock2() { var nowTime = new Date(); var nowHour = set2fig( nowTime.getHours() ); var nowMin = set2fig( nowTime.getMinutes() ); var nowSec = set2fig( nowTime.getSeconds() ); var msg = nowHour + ":" + nowMin + ":" + nowSec; document.getElementById("RealtimeClockArea2").innerHTML = msg; if( nowSec == 0){ beep(); } if( nowSec % 55 == 0){ beep(); } if( nowSec % 57 == 0){ beep(); } if( nowSec % 58 == 0){ beep(); } if( nowSec % 59 == 0){ beep(); } } function beep(){ var base64 = "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" var sound = new Audio("data:audio/wav;base64," + base64); sound.play(); } setInterval('showClock2()',1000);
時計は表示されるのですが、何故か音が鳴るときもあれば、鳴らないときも。。。強制的に一度音を出さないと上手く鳴りません。ですので、ボタンを置いて強制的に一度音を出すようにしています。何が原因か未だにわからず。。。
でも何とか完成。良かった。