2016年11月06日

FM音源をArduboyで動かしてみた の続き


前回の続きです。
やはり16ステップのシーケンスが1つ使えるだけでは物足りないので、シーケンサー周りをグレードアップしました。
具体的には

・シーケンスは16個(個々のシーケンスは16ステップで変わらず)
・シーケンスの連続演奏
・シリアルポート経由でのシーケンスのインポート/エクスポート
・EEPROMへの保存

といった機能を実装しました。
そのほか、

・直前の音を消さない「タイ」と直前の音を消す「休符」を分けて実装
・1ステップを四分音符から八分音符へ変更

などの拡張も行っています。
以下のGitHubリポジトリも更新しました。(前回のものはリリースv1.0として保存してあります。)

boochow/abSynth-FM: FM Synthesizer with step sequencer for Arduboy

前回からコードを整理せずにそのまま拡張したので、相変わらず中身はきれいではありません。
行数は現時点でインベーダーゲームより多い1700行超になっていますが、ビットマップやプリセットで用意した曲データ、Protonで開発するためのシリアルポート入力の代替コードなどを含んでいます。

プログラミングとしては、シーケンスの選択や通信を行うユーザーインタフェースと、シリアルポートからの入力のパーサが主な追加部分です。
データの受信中は、誤ったデータや通信の中断によってArduboy側がロックされてしまわないように、メインループから呼ばれて通信バッファが空(Serial.available()がゼロ)だった場合は必ずメインループへ戻ってボタン入力の処理を行うようにしています。

GUIは今回かなりたくさん書いた結果、ある程度スタイルができてきました。
コードはどう考えても現在のスタイルではなくOOPで書き直したほうが良いと思われますので、時間と意欲があればクラスライブラリ化したいところです。

現在重要な役割を担っているのは「process_button()」という関数で、ボタンの状態管理を行います。
引数は

・bool *state ・・・ 現在のボタンのオンオフ
・uint8_t *repeat_count ・・・ 長押し判定のためのカウンタ(処理不要の場合はNULL)
・uint8_t keycode ・・・ 処理対象のボタンの指定
・void(*release_func)() ・・・ ボタンを離したときの処理を行う関数
・void(*repeat_func)()) ・・・ 長押しのときの処理を行う関数
・返り値(bool) ・・・ 画面更新をする必要があるときはtrue

となっています。
例えば、Aボタンの処理はメインループの中で
process_button(&g_keystat.a, &g_keystat.a_repeat, A_BUTTON, a_released, a_long_press);

というようにコールしています。
a_releasedにAボタンが押されたときの処理、a_long_pressはAボタンが長押しされたときの処理を書きます。

ゲームと違って、GUIでは、ボタン操作への反応は押された時ではなく離された時に行うのが基本です。
反応の速さよりも、最後の最後までユーザに選択肢を残すことが重要だからです。
タグ:Arduboy
posted by boochow at 18:53| Comment(0) | Arduino | このブログの読者になる | 更新情報をチェックする

2016年10月24日

FM音源をArduboyで動かしてみた


先々週、PWMを使ったシンセサイザをArduboyに移植しましたが、その記事の中でも触れたpcm1723さんの「FMMelody」をArduboyで動かしてみました。

基本的にはタイマー周りの設定をしてあげるだけで動作しました。
鳴らしてみると、圧電スピーカーでもはっきりと音色の違いが表れます。
FM音源は倍音成分が多いので、圧電スピーカー向きの音質なのかもしれません。

パラメータをいろいろいじって遊びたくなったので、パラメータ設定のGUIと、曲が一応変更可能なステップシーケンサーをArduboyで実装しました。
始めてみると、狭い画面にいかに詰め込むかに熱中してしまい、週末をつぶして一気に作成しました。
コードはこちらに置いてあります。

boochow/abSynth-FM: FM Synthesizer with step sequencer for Arduboy

動作させるには、「Arduboy2」ライブラリが必要です。
今回は、ユーザーインタフェースを作りながらコーディングしていったこともあり、コードはあまり綺麗ではありません。
機会があったら見直したいところです。

GUIは例によってpaint.netでお絵かきしながら考えました。
VSTiベースのソフトウェアシンセでよくあるような、見栄え重視のコントローラをイメージしました。

それを先日作ったツールでデータ化しています。
その静止画の上に線や小さなビットマップを上書きすることでGUIを作っています。
paint.netのようなお絵かきツールは、画像の中の座標が簡単に調べられるので、こういうコーディングをするときに便利です。

loopの中は一般的なArduboyのアプリケーションと若干違っていて、画面の書き換えが最小限になるようにしています。
画面周りの処理を重くすると音のほうがとぎれがちになるためです。

Arduboy、シーケンサー付きのシンセサイザーとしてはTeenage EngineeringのPOシリーズよりも小型軽量かもしれませんね。
オーディオアウトがあればチップチューン用に使えたかもしれません。
まあ、分解して線を引き出せば良いのですが。
下のビデオの方はたぶんそれをやっていると思います。


ちなみに分解写真が以下のスレッドにありますが、圧電ブザーの線を引き出すだけなら簡単にできそうです。

Kickstarter Edition numbering - General - Community
タグ:Arduboy
posted by boochow at 01:50| Comment(0) | Arduino | このブログの読者になる | 更新情報をチェックする

2016年10月20日

Make: Japanでご紹介いただきました

今日のMake: Japanで、このブログの最近のArduboy関連記事(画像作成ツール、シェル、PWMサウンド)をご紹介いただきました。

Make: Japan | Arduboyプログラミングに役立つツールとノウハウ

ありがとうございます。
掲載に至るには、お世話になった数々のOSS/フリーソフトに加えてYouTubeとGitHubとミクさんが重要な役割を果たしたと思われます。
こちらにも感謝です。
タグ:Arduboy
posted by boochow at 23:28| Comment(0) | 日記 | このブログの読者になる | 更新情報をチェックする

2016年10月16日

Arduboy用画像作成ツール 解説

前回紹介したツールは基本的には単純なのですが、ウインドウ上に画像を表示する際の処理が若干分かりにくいかもしれません。

自分自身の備忘も兼ねて、解説を書いておきます。

下が表示部分の演算です。
ofRectangle bounds = ofRectangle(0, 0, img.getWidth(), img.getHeight());
ofRectangle sub = ofRectangle(0, 0, 512, 600);
sub.translate(offset);
sub = sub.getIntersection(bounds);

ofSetColor(255,255,255);
img.drawSubsection(sub.x - offset.x, sub.y - offset.y, sub.getWidth(), sub.getHeight(), sub.x, sub.y);

abimager01.png

imgが表示しようとしている画像で、スケーリングなどの処理は済んでいます。
スクリーン上での表示領域は左上が(0,0)、右下が(512, 600)で、これは固定値としています。

imgを実際に表示させるのが最後の行の命令drawSubsectionです。
これは、画像の一部を画面に描画する命令です。
パラメータは最初の2つが画面上の描画領域の左上の点(図のA)です。
次の2つが描画する画像(グレーの領域)の幅と高さです。
その次の二つが描画したい部分(グレーの領域)の左上の「画像データ上での」座標(図のB)です。
上のプログラムは、これら3組のパラメータを求める処理をしています。

img全体の領域(黒枠)を表す矩形がboundsです。

画像の中でスクリーンに表示したい領域(図の赤枠の領域)はスクロールで移動します。
画像の左上を(0,0)とすると、赤枠領域の左上の座標は、スクロールによってoffsetだけずれています。
赤枠の領域の大きさは、スクリーン上での表示領域の大きさ(512×600)と同じです。

赤枠と画像が重なっている領域が実際に描画すべき領域で、図ではグレーで表しています。
この領域は矩形同士の重なり部分を算出する演算getIntersectionで求めています。
その結果をsubに代入していますので、最終的にはsubはグレーの領域を表す矩形となります。

subの左上の点は図のBの座標に相当し、これを-offsetだけ移動することによって、スクリーン上での位置であるAの座標が得られます。
タグ:Arduboy
posted by boochow at 21:33| Comment(0) | Windows | このブログの読者になる | 更新情報をチェックする

openFrameworksでArduboy用画像作成ツール

abimager.png

Arduboy用の画像変換ツールについては以前もちらっと書きましたが、一応動作するようになりました。
いつものことながら、コードはGitHubに上げてあります。

boochow/ArduboyImager

コンパイル済みのバイナリも一応こちらに置いておきます。
[2016/11/12追記]Mac版はこちらです。

説明ビデオも作りました。


ファイルを開いて、マウスの左クリックで切り取り、右下の「a C array」をクリックすればC言語用の配列定数が、「AbShell commands」をクリックすればabshell用のコマンド列が、クリップボードにコピーされます。

C arrayの例:
{
//width = 16, height = 16
0x09, 0xb0, 0x06, 0x49, 0x09, 0x36, 0x46, 0x48, 0x19, 0x07, 0x32, 0x08, 0x05, 0x13, 0x4e, 0x2d,
0x00, 0x00, 0x40, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x60, 0xc8, 0x90,
};


AbShell commandsの例:
clear
bitmap 0 0 16 16
.x 09b00649093646481907320805134e2d0000400008000000000040000060c890


カラー画像を白黒へ変換するディザリングの処理は、下記で公開されていたライブラリを使わせていただきました。(古いためか、そのままだと動作しなかったので、若干修正しています。)

ofxDither | julapy

今回使ったのはopenFrameworksという以前も紹介したツールキットです。
画像処理に関しては、かなり機能が充実していますし、Javaではなくネイティブで動作するので高速です。

今回作ったアプリケーションは、画像処理はopenFrameworksとofxDitherですべて行っていますので、実質的には200行程度のものです。
その内、かなりの部分をGUIのハンドリングが占めており、開発の所要時間も、GUIライブラリの学習とユーザーインタフェースのデザインが半分以上だと思います。

使用したGUIライブラリは、標準的に用意されているofxGuiです。
ドキュメントはあまり整備されていないのですが、openFrameworksの付属のサンプルを見れば何とかなります。
提供されるGUI部品は、CGを生成するアプリを作るために必要な本当の最低限という感じで、ボタンもチェックボックスも見かけは同じですし、扱えるのは数値と論理値だけで、テキストを入出力する部品もありません。

典型的な、数値をスライダーで設定する場合、画像のスケーリングを例にとると、GUIに関するコードは以下のようになります。(抜粋です。)

ofApp.h :
		ofxPanel imageGui;
ofParameter<float> scale;
void scaleChanged(float & v);


ofApp.c :
void ofApp::setup() {
imageGui.setup("Image","settings-image.xml");
scale.addListener(this, &ofApp::scaleChanged);
imageGui.add(scale.set("Scale", 1.0, 0.125, 2));
}

void ofApp::scaleChanged(float &v) {
updateImages();
}

void ofApp::draw(){
imageGui.draw();
}


上記のコードでは、変数scaleはそれ単体でfloat型の変数として動作しますが、Listenerが設定されていて、値が変化するとofApp::scaleChangedが自動で呼ばれます。
(自動で呼びたくない場合は、値の変更にsetWithoutEventNotificationsを用います。)

ofxPanelは複数のGUIコンポーネントをまとめるオブジェクトで、scaleの値を変更するスライダーがofxPanelのインスタンスであるimageGuiへaddによって追加されています。


マウス操作のユーザーインタフェースデザインをどうするかは、一週間ばかり悩みました。

・マウスポインタの直下のエリアが自動で白黒化されるインタフェースが気に入ったので、これを活かしたい
・クリックすると、そのとき白黒になっている部分が取り込まれてデータを出力するのも自然で良い
・白黒化された状態の画像をウインドウ右側で常に確認できる機能も捨てがたい
・クリック&ドラッグで画像をスクロールさせたい

を全部実現するにはどうするか。
最終版では、白黒化された画像の領域を3つ(マウスポインタ直下、その画像を定位置で表示して確認、クリックして取り込んだ画像の確認)用意することで落ち着きました。
これによって、取り込んだ画像と現在の画像の比較をすることもできるようになりました。
posted by boochow at 17:22| Comment(0) | Windows | このブログの読者になる | 更新情報をチェックする
人気記事