Phonetica (Sender)

経緯
今回,私の好奇心の矛先はiOSアプリの製作へと向けられた. なぜかと聞かれても"好奇心に理由など無いから"と応えたいところだが,素直に言ってしまえば暇であったからが一番妥当か. または,製作に必要な道具が既に揃っていたからとも言える. プログラミングと他のものづくりとの決定的な違いは,道具および材料が揃い易いか難いかであると言えるだろう. それだけに,今現在,世界で最も職人の多い職業ともなっている情報産業の発展ぶりには納得ゆく.

話を戻す. iOSアプリの製作をするにあたり,まずは初対面のObjective-Cと仲良くなる必要があった. とは言っても,C言語を完全にサポートしているのでC言語を知っている人ならばなに不自由なくプログラミング出来る. Cをオブジェクト指向に仕上げた言語であるとのことなので,C++を流暢に喋れる人ならば,主に文法,デリゲート,メッセージといった違いさえササっと吸収すれば,後はゴリゴリとプログラミング出来るはずである. SmallTalkから取り込んだオブジェクト指向であるとのことで,C言語とは似ても似つかぬ面白い書き方が目立つ言語であったが,試行錯誤し使ってゆくと,独特な文法だけに覚えやすかったり. またメソッドを呼び出す方法が,SmallTalkから来たものなので,メッセージのやりとりで説明できる点は興味をそそられた. 言語の話はここでやめないとアプリの話が出来なくなると判断したゆえ,この話題はここにて終えて,次にアプリの紹介をする.

Phoneticaという名前のアプリをつくった. アンドロイド版もつくろうかと考えたが,開発環境(デバイス)が無かったので諦めた. というのもこのアプリ,カメラを使って通信ごっこをする機能を有するので,ソフトウェアシミュレータだけでは代替は無理だろうとの判断からである. やはり実機でテストするのが良い.

以前このブログでこんな記事を書いた.
フォネティックアルファベット
これがなんであるかはリンク先の記事を参照して欲しい. この記事では,ECMAScriptで書いた,ブラウザ上で動作する,アルファベットとフォネティックコードとの変換器を紹介した. これをiOS用に作り変えることに決めた. 理由としては,初めて使う言語の練習になると考えたからである. 変換器を実現するには,ユーザの入力を取得する,文字列を操作する,配列(辞書)を用意する,配列を操作および参照する,文字列をフォーマットしてディスプレイへ出力する,と言ったプログラミングの基本となること重点的に扱うからである. この機能を実装してゆく内に,プログラム言語の基礎を自然と学べると考えたのだ.

Phoneticaという名前は,フォネティックコードを意味する"Phonetic"に,iOSアプリ製作の"最初"の作品であることを意味して,アルファベットの始めの文字である"a"を加えたところにある. また試作品程度の完成度を目指した,すなわちα版との意味も"a"にはある.

Phoneticaは"Sender (送信者)"モードと"Receiver (受信者)"モードの2種類のモード(機能)が備わっている. このアプリひとつで,コードを送信出来,受信も出来るわけである. 通信にはモールスコードを使う. 初めはフォネティックコードへと変換出来ればそれで満足するはずであったが,つくってゆく内にiOS用ライブラリをもっと使ってみたくなり,次第に軸がモールスコードへと傾いていった. 折角なので通信出来たら面白いだろうと考えた次第,現在に至る. 製作過程で欲が出たのだ. なのでこのアプリ,名前はフォネティックコードを意識しているが,中身はモールスが主で,フォネティックがおまけとなってしまった. まぁお陰様でObjective-Cと仲良く成れたので良しである. 今回の記録ではSenderモードの使い方と裏話をする. なお,ここで使用する画像(Screenshot)は開発用のものなので,実際リリースされているものとは多少異なる点が有ること予め了承いただきたい.

Senderモード
アプリを立ち上げると次の画面が表示される.
Senderモードのメインビュー
これは通信ごっこをする際,送信者が使うモードである. アルファベットをフォネティックコードもしくはモールスコードへと変換する機能を有する. 使い方は画面中央やや下に記されているが,ここで改めて画像つきで説明する. まずは,藍色をしたテキストフィールドに,コードへ変換したい文字列を入力する. 英数字を50文字まで入力出来る. 記号が入るとエラーが表示され,コードへ変換されない. 入力を終えたら"Encode (エンコード)"ボタンをタップするか,ソフトウェアキーボードの"return"キーをタップするとNATOフォネティックコードへと変換される. 下のように.
テキストフィールドへ文字列"lisp"と入力した結果.
当初の目的はこれにて達成. しかし身から出た欲のため,この後幾らかの時間とPCを動かすための電気が犠牲となる. モールスコード機能の付加である. 欲による犠牲. 歴史上にも似たような話は沢山ある. さて,中央やや上に見える,緑色のスイッチをタップすると,スイッチがMorse(モールス)側へと寄る. この状態で再び"Encode"ボタンをタップしてみよう. すると今度は国際標準のモールスコードへと変換される. あら不思議.
モールスコードは,Dots(.)が短音,Dashes(-)が長音を表している.
この後説明する機能は,カメラ用のフラッシュライトとスピーカが内蔵されているiOSデバイスだと面白くなる. 表示されたモールス信号をタップしてみよう. すると下のようなアクションシートが現れる.
アクションシートには通信手段の選択肢が表示されている.
フラッシュライトの付いていないiOSデバイスには"Flash"の項目が表示されない. "Beep"についても同じく,内蔵スピーカの無いiOSデバイスでは表示されない. さて,"Copy"をタップすると結果のモールスコードがクリップボードへとコピーされる. メールに貼り付けても良し,メモとしてとっておくのも良しである. "Flash"をタップすると,結果のモールスコードに従ってフラッシュライトが点滅する. 0.2秒を単位(Unit)として,短音は1Unit,長音は3Units分光り,点間は1Unit,文字間は3Units,単語間は7Units分間を空ける. 船乗りがライトを使ってピカピカとモールスコードを送受しあうイメージである. "Beep"をタップすると内蔵スピーカから音が出る. 44.1kHzでサンプリングされた523.25Hz(C:ピアノのドの音)の正弦波である. この機能を実装するにあたり,iOSはビジュアル関連のプログラムより,オーディオ関連のプログラムの方が難しいと感じた. なお,Beepの場合モールスの単位(Unit)は1Unitが0.1秒である(バージョン2.2では0.07秒となる).

気になる右下の"i”ボタンをタップすると,アルファベットとモールスコードの対応表が表示される.
国際基準のモールスコード対応表


技術的背景,おもにアルゴリズム
アルファベットとコードを変換するにあたり,その対応表に当たる仕組みを用意する必要が有った. それには配列を用いた. Objective-Cでは,配列ではなくディクショナリ(辞書)と呼ぶのが正しい. 辞書ならば,ある要素を参照するためのキー(インデックス)は数字なので,for文でループする時に使いやすい. このアプリでは,次のような3種類の辞書(配列)を定義した.
natoDict = [NSArray arrayWithObjects:@"Alpha", @"Bravo", @"Charlie", … ,nil];
morseDict = [NSArray arrayWithObjects:@".-", @"-...", @"-.-.", … ,nil];
alphabetDict = [NSArray arrayWithObjects:@"a", @"b", @"c", … ,nil];
上から順に,NATOのフォネティックコード,モールスコード,アルファベットが値として代入されている. いずれの辞書もa,b,c,d...1,2,3...0の順番に代入されている. これがミソ. 文字列(英数字)の変換工程は次のとおりである. 例として文字列"CD"をモールスコードへと変換するところを紹介する.
resultとcurrentCharを空文字""で初期化;
入力された文字列を取得;
if (入力文字列に記号が含まれているか?) {
    YESなら警告を表示してreturn;
}
for(int c=0; c<入力文字数; c++){
    入力文字列c番目の文字を,変数currentCharへ代入;
    for(int index=0; index<alphabetDictの要素数; index++){
        if (currentCharとalphabetDictのindex番目の要素は等しいか?) {//YES
            resultへ"morseDictからindex番目の要素を取得した文字列"を結合;
        }
    }
}
画面上のUILabel(ラベル)へresultを出力;

入力文字列から先頭の文字"C"を取り出す. これはfor文にて回す.
先頭文字"C"と同じ値(文字)がalphabetDict内に存在するかを,for文を用いて調べてゆく.
"C"の場合,alphabetDictを3回ループした時に,値"C"が現れる.
for文は,変数indexを用い,0から辞書の要素数分ループをしているので,alphabetDictの値"C"を参照した瞬間にindexには2が代入されていることとなる.
morseDictのindex番目の要素を取り出す. この場合,indexは2なので文字列"-.-."が取り出される. "-.-."を変数resultへと結合(resultは""空文字で初期化されているので,そこへ文字列的に結合してやればよい). どの辞書もa,b,c...の順に要素が並んでいるので,アルファベットとコードは必然的にインデックスにより対応づけされている.
入力文字列から2文字目"D"を取り出す.
"D"と等しい値が出てくるまでfor文にてalphabetDictを走査する.
"D"と等しい値がindex=3の時に現れた!
morseDictのindex(3)番目の要素を取得. これつまり"-.."
"-.."を変数resultへ結合.
for文は0から入力文字数分だけループさせるため,これにて変換作業終了.
変数resultの中身をUILabel.textへ代入. これすなわち出力.

フォネティックコードへの変換も同じ要領である. モールスコードとフォネティックコードの切換はスイッチで行っている. プログラム的にはBOOLなフラグの中身を入れ替えているに過ぎないので,BOOL型の変数1つとif文1つで切換は簡単に実現出来る. なお,コードからアルファベットへの逆変換も,基本的に同じ処理をしている. 辞書の並び順をABC順ではなく,モールスコードのコード数の少ない順に並べると効率が良くなるだろう. と言うのもモールスコードでは,よく使われる英字はコード数が少なくなるよう割り当てられているからだ.

モールスコードの場合,フラッシュライトとブザーとで,コードを発信(送信)することが出来る. 仕組みは次に示すようにタイマを用いた.
"Flash"をタップするとflashInitを呼び出す. ここでモールスコードはDotsとDashesと空白のみで構成された文字列,例えば".-.- -.."であるとする.

-(void)flashInit {
    必要な変数の初期化(currentCode,n,unitの初期値代入);
    flashLoopの呼び出し;
}

-(void)flashLoop {
    モールスコードn文字目(DotかDashか空白か)を取得しcurrentCodeへ代入;
    switch(currentCode) {
        case ".": 引数を1*unit(0.2秒)としてflashOnを呼び出す;break;
        case "-": 引数を3*unit(0.6秒)としてflashOnを呼び出す;break;
        case " ": 2*unit(0.4秒)後にflashOffを呼び出す;break;
        default:デバッグ用に"変な文字混ざっているよ"とかログ出力;break;
    }
    n++;
    モールスコードすべてを走査し終えたか?YESならreturn;
}

-(void)flashOn:(float)time {
    フラッシュライトオン;
    引数time秒後にflashOffを呼び出す;
}

-(void)flashOff {
    フラッシュオフ;
    1*unit(0.2秒)後にflashLoopを呼び出す;
}
途中で停止出来るよう,タイマは定義する都度グローバル変数timerへと代入. "Stop"ボタンをタップされたら[timer invalidate],すならちタイマ停止命令を呼び出すようにした. Beepをタップした際も基本的に同じ動作をする. ただ1Unitが0.1秒(バージョン2.2なら0.07秒)になるだけの話である. またフラッシュライトオンの命令がビープ音再生へ,フラッシュライトオフの命令がビープ音停止へと変化するだけである. なお,ここで発するビープ音は,先に述べたとおり,44.1kHzでサンプリングした523.25Hz(C:ピアノのドの音)の周波数を持つ正弦波である. これは算術式$A\cdot\sin{\left(\frac{2\pi f}{n}\right)}$にてソフトウェア的に生成した離散正弦波(正体は辞書)をスピーカへ出力することで実現した. ここでAは振幅,fは周波数,nはサンプルレートを示す. Phoneticaの場合,A=0.75,f=523.25,n=44100である.

以上,Phonetica (Sender)についてザッと解説した. 次回は"Receiver"モードについて. 使い方と技術的背景,おもにアルゴリズムを紹介したい.
5423248434606727336 http://www.storange.jp/2013/08/phonetica-sender.html http://www.storange.jp/2013/08/phonetica-sender.html Phonetica (Sender) 2013-08-29T00:26:00+09:00 http://www.storange.jp/2013/08/phonetica-sender.html Hideyuki Tabata 200 200 72 72