2016年7月17日日曜日

駅の発車メロディ/ベルを再現するソフト 「発メロ駅気分」

さて、僕は昔、「発メロ駅気分」というフリーソフトを公開しました。
...それからはや1年。アップデートも済ませ、ようやく安定軌道に乗っております。そんな中で、このブログで発メロ駅気分の紹介をするに至りました。

発メロ駅気分の概要

最新バージョン(2016年6月現在) 1.1.16.2
主に鉄道駅で使用されている発車メロディを再現します。
このソフトはフリーソフトで、完全無料です。寄付なども含め、金銭類は一切請求いたしません

このソフトでできること

  • 好きな音源・戸閉放送を使用して、駅の発車メロディその他を再現できます。
  • 実物のボタンと接続することができます。
  • ボタン押下音(ボタンを押したときの「カチ」という音)を鳴らせます。
  • 新幹線発車メロディのように、発車メロディを鳴らし始めて一定時間経過後に戸閉放送をかぶせる(逆も可)ことができます。(New!)
詳しくは下の方に説明動画を添付しておりますのでご覧ください。

動作環境

  • Windows Vista以上
  • .NET Framework 2.0以上がインストールされていること(Windows Vista以上なら大丈夫です)

発メロ駅気分の使い方

発メロ駅気分の使用方法です。なお、この動画は1.0.15.1(旧バージョン)時のものです。現段階の最新バージョンにある、「戸閉放送をかぶせる」機能の説明はありません。

「戸閉放送をかぶせる」の使い方


バージョン1.1.16.2を起動した直後の状態です。下の方に追加されている「戸閉放送をかぶせる」というのが今回の新機能です。
チェックボックスにチェックを入れてみてください。そして、右側の入力欄を「9000」にして、発車メロディを鳴らしてみましょう。

どうでしょう?新幹線みたいですよね?これが今回の新機能となります。
なお、今「9000」と入力した欄が遅延の長さを設定するところです。単位はミリ秒で、1ミリ秒は1000分の1秒です。
ここの欄を調整することで、ON押から放送が流れるまでのタイムラグが調節できます。いろいろ試して、いい値を見つけてみてください。マイナスの値(つまり、戸閉放送が先に鳴る)も設定できます。

ダウンロード

ダウンロードはVectorからお願いします。

2016年7月8日金曜日

[PHP]ノロノロ関数、array_pushは極力使うな・count()よりもisset()

今回はPHPのお話です。
PHPには「これでもか」と多くの配列関数があります。

その中でも大変遅い関数がありますので紹介しようと思います。

array_push()

コイツは遅い。ため息が出るほど遅いです。

そもそもarray_push()は、配列の最後に値を追加する関数です。

$hoge = array('ね', 'うし', 'とら');

array_push($hoge, 'う', 'たつ');
/*
[
    "ね",
    "うし",
    "とら",
    "う",
    "たつ"
]
*/
でもこうするくらいだったら、
$hoge[] = 'う';
$hoge[] = 'たつ';
こうしたほうが早いです。ネット情報によると、10倍近く速度が違うらしいです。
ここまで差が出ると、中では一体何をしてるんだと疑いたくなります(^_^;;)。買い物を頼んだら1週間かかって帰ってくるようなものです。
array_push()は追加後の要素数を返しますが、それならcount()を使って個別に調べたほうがいいと思います。

count()

count()は目立って遅い関数ではありません。しかし、使い方によっては速度面で損をしているかも。
count()は配列の要素数を返します。でも、場合によっては要素数を知らなくても済むことだってあるのです。
例えば、
//こうするくらいだったら、
if(count($hoge) === 0){
//こうしたほうが早い
if(isset($hoge[0])){

//また、こうするくらいだったら、
if(count($hoge) >= 6){
//この方が早い
if(isset($hoge[5]){
isset()は関数のような見た目をしていますが、実は違います。これ、if()while()と同じ、言語構造なのです。当然、呼び出しのオーバーヘッドがない分早いです。
ちなみに、empty()も言語構造です。

まとめ

他にもネット上にはいろいろな情報が転がってます。array_search()は遅い、型が事前にわかってるなら==より===のが速い、intval()strval()よりキャストした方がいい、など。

ただ、時と場合によりけりです。具体的な配列の要素数を知りたいなら上で紹介した方法よりもcount()の方がましでしょう。確かにPHPには遅い関数や構文が多いですが(そもそも言語自体遅い)、場面に応じて使い分けられるようにしましょう。

2016年3月31日木曜日

プログラミングに「メモ帳」を使うな?BOM・改行コードについて考える


お久しぶりです。chiko ryoです。

さて今回は、Windowsにデフォでついてくる「メモ帳」でテキストファイルを保存したときに頭を悩ませる「BOM」と「改行コード」について考えてみようと思います。

メモ帳でUTF-8を使うとBOMがついてくる

メモ帳で「名前を付けて保存」すると嫌でもBOMが付いてきます。BOMなしUTF-8を得るには何かしらのツールが必要になります。
そもそもBOMというのは、「このファイルの文字コードはUTF-8ですよー」という意味の制御コードです。文字化け防止にもなるいい奴なのですが、プログラミングでは邪魔になります。
<meta charset="utf-8">
<?php
  echo file_get_contents(/* BOMつきUTF-8空ファイル */);
?>
<div style="background-color:#000;color:#fff;">上に隙間があるはずです。開発者ツールでみると制御記号が入っているのがわかります。</div>
黒いdivの上に変な隙間があるのがお分かりいただけるかと思います。これがBOMの仕業です。

BOM対策

対策としては、
  • メモ帳を使わない(他のソフトを使う)
  • 保存後に手作業で削除
あたりでしょうか。メモ帳ではBOMなしUTF-8が保存できないのでこうすることになります。

メモ帳では改行コードがCR+LFになる

これもなかなかの問題です。

Windowsでは基本的に改行コードはCR+LFです。きっとWindowsで作成したテキストがMac(CR派)やLinux(LF派)でも表示されるように、という配慮(であってほしい)です。

ただ、文字コードがCRだけ/LFだけのテキストファイルをメモ帳で開こうとすると、
メモ帳は「改行はCR+LF」と思っている → LF(CR)が単独でくる → メモ帳は改行だと思わない
といった具合で改行が潰れます。

改行コード問題への対策

  • メモ帳を使わない(他のソフトを使う)
  • 変換する
メモ帳以外のソフトとしては、いろいろと選択肢があります。ちなみに私はサクラエディタを使っています。
また、改行コード問題に関しては、moreコマンドを使えばWindows標準で変換できるようです。詳しくは テキスト・ファイルの行末コードを変更するとかの情報が参考になると思います。

いかがでしょうか。今回はメモ帳のBOMと改行コードについてここまで論じてきましたが、結論としてはMSが強制メモ帳ではできないので他の策を講じる、ということになりそうです。

2015年11月3日火曜日

Javascriptでピタゴラス数を求める

普段管理人が通う学校の教師の提案で、「ピタゴラス数」を求めるプログラムを作ってみました。

ピタゴラス数って何?って方はWikipedia - ピタゴラス数をご覧ください。

ここでのmnは、ピタゴラス数の面積を求めるのに使用する自然数(m > n)とします。

では、さっそく実物を。

mまたはnの最大値 

処理結果

mの値 nの値 斜辺(m2 + n2) 他の一辺(m2 - n2) 他の一辺(2mn) (参考) 面積 (参考) mとnのG.C.D

参考程度にソースコードをはっておきます。

<!--
  <script>~</script>間はコメントアウトを省略
  しています。
-->
<script type="text/javascript">
function tagainiso(){
  // 剰余
  var r;
  var tagainiso_n = n;
  var tagainiso_m = m;
  gcd = 1;

  while ((r = tagainiso_m % tagainiso_n) != 0) {
    tagainiso_m = tagainiso_n;
    tagainiso_n = r;
  }
  gcd = tagainiso_n;
  if(gcd != 1) {
      n = n / gcd;
      m = m / gcd;
  }
}

function pitagras(){
  // 乱数その1
  m = Math.floor(Math.random()*document.getElementById("max").value + 1);
  //その2
  n = Math.floor(Math.random()*document.getElementById("max").value + 1);

  if(m < n) {  // m >= nにする
    var taihi = m;  //一時退避
    m = n;
    n = taihi;
  }
  tagainiso();
  //テーブル取得
  var table = document.getElementById("area");
  //行追加
  var tr = table.insertRow(-1);
  //セル挿入
  tr.insertCell(-1).innerHTML = m;
  tr.insertCell(-1).innerHTML = n;
  tr.insertCell(-1).innerHTML = m * m + n * n;
  tr.insertCell(-1).innerHTML = m * m - n * n;
  tr.insertCell(-1).innerHTML = 2 * m * n;
  tr.insertCell(-1).innerHTML = ( (m * m - n * n) * 2 * m * n ) / 2;
}
</script>

<p><b>m</b>または<b>n</b>の最大値&nbsp;<input type="number" size="5" id="max" value="50"></p>
<p><button onclick="pitagras()">実行</button></p>
<p>処理結果<br>
<table id="area" border="1">
  <tr>
    <td>mの値</td>
    <td>nの値</td>
    <td>斜辺(m<sup>2</sup> + n<sup>2</sup>)</td>
    <td>他の一辺(m<sup>2</sup> - n<sup>2</sup>)</td>
    <td>他の一辺(2mn)</td>
    <td>(参考) 面積</td>
  </tr>
</table>
</p>

作り方を知りたい方は、コメントいただければ解説します。

2015年10月29日木曜日

Javascriptで最大公約数を求める(ユークリッドの互除法)

今回は、数学的なお話。ユークリッドの互除法をJavascriptでしてみようと思います。

まず...
ユークリッドの互除法とは?という方へ。

ユークリッドの互除法は、簡単に最大公約数を求めるための計算法です。説明より例を示した方がいいかと思いますので、例題を一つ。
(例題) 1071と1029の最大公約数を求めよ。

  • 1071 を 1029 で割った余りは 42
  • 1029 を 42 で割った余りは 21
  • 42 を 21 で割った余りは 0
  • よって、答えは 42

では、これをプログラムに書き起こしてみましょう。関数名は最大公約数の略であるG. C. D.としました。

function gcd(m, n) {
  // 剰余
  var r;

  if(m < n) {  // m >= nにする
    r = m;  //一時退避
    m = n;
    n = r;
  }

  while ((r = m % n) != 0) {
    m = n;
    n = r;
  }

  return n;
}

2015年10月28日水曜日

C#で外部スイッチとやり取りする

パソコンと(2極)スイッチで通信する方法を考えてみました。(動作未確認)
※2極スイッチとは、簡単なON/OFFだけを切り替えるスイッチのこと。

そんなとき問題になるのは、

  • パソコンとスイッチをどうやって繋ぐか
  • ソフト側でどうやってON/OFFを認識するか
です。

まず考えたのは、USBで接続する方法。USBポートでスイッチと接続できないかと。しかし、これではドライバを作らないといけないし、スイッチ側に自己紹介する機能を持たせなければやり取りは不可能です。

次に考えたのがシリアルポートを使う方法です。1ビットのシリアル接続が可能なら、ON/OFFも可能なのではないか。
結論から先に書くと、「できました」。

RS-232C規格のシリアルポートを使った具体的なしくみです。
まず、DTR線の状態をONにしておきます。スイッチはDTR-(スイッチ)-DSRの順で接続しておきます。スイッチがONになると、DTRから出力された12Vの電流がDSRに流れることになります。スイッチをOFFにしたときは-12Vが正確に入力されるわけではありませんが、電流0はFalse扱いをうけるようです。

その理論(?)をもとに、C#でコードを書いてみました。

using System;

// シリアルポートクラスがある名前空間
using System.IO.Ports;

namespace myPort
{
  public class myPort1
  {

    // シリアルポートクラスのインスタンス
    SerialPort port = new SerialPort("COM1");

    // ポートをオープンする
    port.Open();

    // DTR線を出力
    port.DtrEnable = true;

    // ONかOFFかは次の値で確認する
    port.DsrHolding;

    // 例
    private void checkSwich(){
      if(port.DsrHolding)  // ON
      {
        MessageBox.Show("スイッチはONです。");
      }
      else
      {
        MessageBox.Show("スイッチはOFFです。");
      }
    }
  }
}

これで、ON/OFFの2極スイッチとパソコンとでやり取りができるようになりました。

なお、この記事を書くにあたって、どこでも発車ベルヘルプ - スイッチのつなぎ方を参考にいたしました。
ありがとうございました。

なお、この記事で記載された内容は、管理人(chiko ryo)では動作確認を行っておりません。くれぐれも自己責任でお願いします。また、このサンプルは著作権フリーですが、これを参考にしていかなる損害が出たとしても管理人(chiko ryo)は責任を負いませんのでご容赦ください。

2015年10月27日火曜日

JavascriptでINIファイルを解読する その3

JavascriptでINIファイルを解読するシリーズの第3弾です。

前回のJavascriptでINIファイルを解読する その2でお約束していた通り、INIを読み込んで連想配列に返す関数を完成させることができました。

さっそく、ソースをはります。

function myReadInI(myFname){

    var i = 0; // プロパティiはカウンタです。
    var t = null; // コメント除去に使用します。
    var ts = 0;   // これもです。
    var sec = null; //処理中のセクションを記録
    var pal = null; //処理中のパラメータ名を記録
    var returnData = {}; //返り値
 
    // オブジェクト生成
    var objFileSys = new ActiveXObject("Scripting.FileSystemObject");
 
    var objTextStream = objFileSys.GetFile(myFname).OpenAsTextStream(1, 0);
    /*読み込みモードを使用、文字コードはUnicodeです。(Unicode文字が扱えるようにするため)*/
 
    var data = new Array(); // 空の配列
 
    // ファイルが最後まで読み込まれるまでループ  読み込み
    while (!objTextStream.AtEndOfStream) {
 
        t = objTextStream.ReadLine();
        ts = t.indexOf(";"); // コメントを探す
        if(ts == -1){ // コメントがない
            data[i] = t;
        }else{ // コメントあり
            data[i] = t.substring(0, ts);
        }

        //解読

        if(data[i] != ""){  //空行でないか?

            if(data[i].indexOf("[") == 0){  //セクション行

                sec = data[i].slice(1, -1);    //セクション名を抽出する

                returnData[sec] = {}  //さらにパラメータ名の2次元連想配列が作れるようにする

            }else{                          //パラメータ行

                if(data[i].indexOf("=") != -1){  // "="を含む行であることを確認する

                    /* パラメータ名と値を分割(pal[0]がパラメータ名、pal[1]が値) */
                    pal = data[i].split("=");

                    /* 連想配列に代入する */
                    returnData [sec] [pal[0]] = pal[1];
                }

            }

        }
        i++;
    }

    return returnData;

}

前回からの変更点として、

  1. 引数が一つになった
  2. 戻り値が2次元連想配列になった
  3. 若干軽くなった
  4. 何度もファイルを読み込みなおす必要がなくなった
があげられます。また、空行があってもそのあとの行も読み込むようになったほか、セクションの閉じカッコ"]"の省略を禁止しました。


使い方
・引数

引数は1つ必要です。INIファイルまでのパス(絶対パス・相対パス)を文字列で渡します。
・戻り値
INIを解析した結果の2次元連想配列を返します。設定内容は(戻り値)["(セクション名)"]["(パラメータ名)"]で取得できます。

おそらくこのシリーズは今回で終わりとなります。
解説は要望があれば行います。ご希望であればコメントをお願いします。