差分
このページの2つのバージョン間の差分を表示します。
両方とも前のリビジョン 前のリビジョン 次のリビジョン | 前のリビジョン最新のリビジョン両方とも次のリビジョン | ||
楽器をつくる:シーケンサ編 [2018/12/23 17:45] – [タイムライン(自動演奏のための時間)機能を追加] baba | 楽器をつくる:シーケンサ編 [2020/06/20 11:27] – [タイムライン(自動演奏のための時間)機能を追加] baba | ||
---|---|---|---|
行 1: | 行 1: | ||
====== 最初に ====== | ====== 最初に ====== | ||
- | このページではプログラミング初学者の人が簡単なミュージックシーケンサを作成するまでをまとめたウェブサイトになります。自作デバイス上でArduinoを動かすことを想定していますが、教材用にProcessingで動作するソフトウェアで紹介します。 | + | このページではプログラミング初学者の人が簡単なミュージックシーケンサを作成するまでをまとめたウェブサイトになります。自作デバイス上でArduinoを動かすことを想定していますが、教材用にProcessingで動作するソフトウェアとして紹介します。配列の宣言の仕方など、Arduinoの場合と少し異なりますので、適時読み替えてください。 |
====== 設計 ====== | ====== 設計 ====== | ||
行 9: | 行 9: | ||
</ | </ | ||
- | 重要なのは想定する一つ一つの部品や機能をソフトウェア上でclassとして作成していってください。いわゆるオブジェクト指向で考えていきます。シーケンサの機能としては、レイヤー階層になっており、各レイヤーごとに4x4のボタンが用意されています。ボタンを押すごとにそのボタンのonとoffを切り替えることができます。onになったボタンはタイムラインに従って自動で音を発話します。 | + | 重要なのは想定する一つ一つの部品や機能をソフトウェア上でclassとして作成していってください。いわゆるオブジェクト指向で考えていきます。シーケンサの機能としては、レイヤー階層になっており、各レイヤーごとに4x4のボタンが用意されています。ボタンを押すごとにそのボタンのonとoffを切り替えることができます。onになったボタンはタイムラインに従って自動で発音します。 |
+ | |||
+ | 実際に完成するソフトウェアは下記のようなものです。では一緒にやってみましょう。 | ||
+ | < | ||
+ | <iframe width=" | ||
+ | </ | ||
- | 実際にコードを作成していく際、ベースから作る場合と、トップから作っていく場合がありますが、 | ||
- | 馬場の普段の作り方は、トップから作っていく方法になります。一緒にやってみましょう。 | ||
====== 最初のコード ====== | ====== 最初のコード ====== | ||
行 103: | 行 106: | ||
} | } | ||
void update() | void update() | ||
- | { | + | { |
// マウスがボタン上にあるとき(ボタン押下処理) | // マウスがボタン上にあるとき(ボタン押下処理) | ||
- | if ( sqrt( pow(x-mouseX, | + | if ( dist(x, y, mouseX, mouseY) < size/2 ) { |
- | + | ||
// マウスボタンが押されている、且つ一つ前の状態では押されていなかったとき | // マウスボタンが押されている、且つ一つ前の状態では押されていなかったとき | ||
if ( mousePressed == true && previous_mousePressed == false) { | if ( mousePressed == true && previous_mousePressed == false) { | ||
status = !status; // falseならtrue, | status = !status; // falseならtrue, | ||
- | | + | |
// trueなら明るめのグレーにする | // trueなら明るめのグレーにする | ||
if( status == true ){ | if( status == true ){ | ||
行 121: | 行 124: | ||
} | } | ||
} | } | ||
- | | + | |
previous_mousePressed = mousePressed; | previous_mousePressed = mousePressed; | ||
} | } | ||
+ | |||
int x; // x座標 | int x; // x座標 | ||
int y; // y座標 | int y; // y座標 | ||
行 132: | 行 135: | ||
boolean previous_mousePressed; | boolean previous_mousePressed; | ||
} | } | ||
+ | |||
Button[] button = new Button[16]; | Button[] button = new Button[16]; | ||
- | + | ||
void setup() | void setup() | ||
- | { | + | { |
- | + | size(500, 500); | |
- | size(500, 500); | + | |
for ( int i = 0; i < 16; i++ ) { | for ( int i = 0; i < 16; i++ ) { | ||
button[i] = new Button(); | button[i] = new Button(); | ||
行 146: | 行 146: | ||
} | } | ||
} | } | ||
- | {{ : | ||
void draw() | void draw() | ||
{ | { | ||
行 157: | 行 156: | ||
</ | </ | ||
- | ここらで少しButtonクラスの記述が増えて来たので、別ファイルにクラスの記述を移します。クラスの記述部分をすべてButtonというタブに移します。 | + | ここらで少しButtonクラスの記述が増えて来たので、下記のスクリーンショットのようにクラスの記述を別ファイルに移します。クラスの記述部分をすべてButtonというタブに移します。 |
{{ : | {{ : | ||
====== タイムライン(自動演奏のための時間)機能を追加 ====== | ====== タイムライン(自動演奏のための時間)機能を追加 ====== | ||
- | では次にタイムラインを追加します。タイムラインは決められたテンポで順番にボタンのON、OFFに従って自動演奏を行います。そこでまず決められたテンポを算出するクラスとして、Metroクラスを作ります。常時このMetroをupdate()することで、指定テンポでtrueを返す設計にします。まずは次のようなクラスを作成します。下記コードは先程のButtonクラスと同様に、新規タブからMetroとして作成してください。 | + | 各ボタンのON/ |
<file .pde Metro.pde> | <file .pde Metro.pde> | ||
行 181: | 行 180: | ||
} | } | ||
int ms; | int ms; | ||
- | long ms_stamp; | + | long ms_stamp; |
} | } | ||
</ | </ | ||
行 233: | 行 232: | ||
</ | </ | ||
- | ここまでで、sq.pde, | + | ここまでで、sq.pde, |
+ | |||
+ | ====== 発音 ====== | ||
+ | それでは、ボタンを押した箇所だけ発音するプログラムまで進めてみましょう。ここまでですでにどのボタンがONになっているか、シーケンスの位置はどこにあるのかがわかっているので、あとはそのタイミングで適切な音を鳴らせばOKです。まずは音を鳴らす代わりにprintで動作をチェックします。どの鳴らすべき音のタイミングの箇所に" | ||
+ | |||
+ | <file .pde sq.pde> | ||
+ | Button[] button = new Button[16]; | ||
+ | Metro metro; | ||
+ | int play_pos = 0; | ||
+ | int play_pos_previous = 0; | ||
+ | |||
+ | void setup() | ||
+ | { | ||
+ | size(500, 500); | ||
+ | for ( int i = 0; i < 16; i++ ) { | ||
+ | button[i] = new Button(); | ||
+ | button[i].setup(100+(i%4)*100, | ||
+ | } | ||
+ | |||
+ | // Metroの初期化 | ||
+ | metro = new Metro(); | ||
+ | metro.set(100); | ||
+ | |||
+ | } | ||
+ | |||
+ | |||
+ | void draw() | ||
+ | { | ||
+ | background(0); | ||
+ | for ( int i = 0; i < 16; i++ ) { | ||
+ | button[i].update(); | ||
+ | button[i].draw(); | ||
+ | } | ||
+ | if( metro.update() ){ | ||
+ | |||
+ | // ボタンが押されているなら | ||
+ | if( button[play_pos_previous].status == true ){ | ||
+ | button[play_pos_previous].c = color(200); | ||
+ | } | ||
+ | // 一つ前の該当ボタンは 50 の色に戻す | ||
+ | else{ | ||
+ | button[play_pos_previous].c = color(50); | ||
+ | } | ||
+ | |||
+ | // シーケンス箇所を一つ進める | ||
+ | play_pos++; | ||
+ | if( play_pos > 15 )play_pos = 0; | ||
+ | |||
+ | // そのシーケンス箇所は 150 の白色にする | ||
+ | button[play_pos].c = color(250); | ||
+ | if( button[play_pos].status == true ){ | ||
+ | println(" | ||
+ | } | ||
+ | |||
+ | |||
+ | // 古いシーケンス場所として保存しておく。 | ||
+ | play_pos_previous = play_pos; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | 実行してみると、ボタンを押した箇所にシーケンスが来たときのみ、Make some soundsと文字列がデバッグ画面に表示されたかと思います。では次にこのprint文の位置に対して実際に音がなるコードを追加します。MIDIを利用すると広がりがありますが、このページでは学習目的でなるべく簡素にしておきたいので、音声ファイルを再生するだけにしておきます。利用する音声ファイルはMinimライブラリのサンプルにあるDrumMachineの BD.wav(バスドラ), | ||
+ | * {{ : | ||
+ | |||
+ | ではsound libraryを利用して、それらを読み込み、シーケンスのタイミングに合わせて発話するようにします。今回はCHH.wavを利用します。 | ||
+ | |||
+ | <file .pde sq.pde> | ||
+ | import processing.sound.*; | ||
+ | SoundFile sound; | ||
+ | |||
+ | Button[] button = new Button[16]; | ||
+ | Metro metro; | ||
+ | int play_pos = 0; | ||
+ | int play_pos_previous = 0; | ||
+ | |||
+ | void setup() | ||
+ | { | ||
+ | size(500, 500); | ||
+ | for ( int i = 0; i < 16; i++ ) { | ||
+ | button[i] = new Button(); | ||
+ | button[i].setup(100+(i%4)*100, | ||
+ | } | ||
+ | |||
+ | // Metroの初期化 | ||
+ | metro = new Metro(); | ||
+ | metro.set(100); | ||
+ | |||
+ | // 再生音声ファイルの読み込み | ||
+ | sound = new SoundFile(this, | ||
+ | } | ||
+ | |||
+ | |||
+ | void draw() | ||
+ | { | ||
+ | background(0); | ||
+ | for ( int i = 0; i < 16; i++ ) { | ||
+ | button[i].update(); | ||
+ | button[i].draw(); | ||
+ | } | ||
+ | if( metro.update() ){ | ||
+ | |||
+ | // ボタンが押されているなら | ||
+ | if( button[play_pos_previous].status == true ){ | ||
+ | button[play_pos_previous].c = color(200); | ||
+ | } | ||
+ | // 一つ前の該当ボタンは 50 の色に戻す | ||
+ | else{ | ||
+ | button[play_pos_previous].c = color(50); | ||
+ | } | ||
+ | |||
+ | // シーケンス箇所を一つ進める | ||
+ | play_pos++; | ||
+ | if( play_pos > 15 )play_pos = 0; | ||
+ | |||
+ | // そのシーケンス箇所は 150 の白色にする | ||
+ | button[play_pos].c = color(250); | ||
+ | if( button[play_pos].status == true ){ | ||
+ | println(" | ||
+ | sound.play(); | ||
+ | } | ||
+ | |||
+ | // 古いシーケンス場所として保存しておく。 | ||
+ | play_pos_previous = play_pos; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Layerクラスを考える ====== | ||
+ | さあ、ハイハットのチチチでなんとなくリズム刻めてきました。先程ダウンロードした音声ファイルにはこの他バスドラ、ハイハットがあるので、これらもシーケンス内で再生したいと考えます。しかしこの場合、ハイハットの音に対してどのように他の音を割り当てるかを考えなければいけません。シーケンサでは通常このようなものに対してレイヤー機能を利用します。つまりこれまで作成してきたものはハイハットレイヤーであり、この他に、バスドラ、スネアレイヤーを追加します。レイヤーの概念としてはボタンの上位層にあたるので、ここまでの sq.pde 内の処理をLayerクラスに移築し、Layerクラス内でButtonクラスを扱うようにします。というわけで、Layer.pdeを作成します。これまで通り、新規タブからLayerファイルを作成し、sq.pdeのsetup()での内容をLayerに移行してみます。まずはlayerを配列にせず、一枚だけのレイヤーでbuttonを包括するように記述し直します。新規に作成するLayer.pdeの他、sq.pdeも修正します。修正したコードは下記に示します。sq.pdeがスッキリしたのがわかると思います。 | ||
+ | |||
+ | <WRAP group> | ||
+ | <WRAP half column> | ||
+ | <file .pde sq.pde> | ||
+ | Metro metro; | ||
+ | Layer layer; | ||
+ | |||
+ | int play_pos = 0; | ||
+ | int play_pos_previous = 0; | ||
+ | |||
+ | void setup() | ||
+ | { | ||
+ | size(500, 500); | ||
+ | |||
+ | // Metroの初期化 | ||
+ | metro = new Metro(); | ||
+ | metro.set(100); | ||
+ | |||
+ | layer = new Layer(); | ||
+ | layer.setup(); | ||
+ | } | ||
+ | |||
+ | |||
+ | void draw() | ||
+ | { | ||
+ | background(0); | ||
+ | for ( int i = 0; i < 16; i++ ) { | ||
+ | layer.button[i].update(); | ||
+ | layer.button[i].draw(); | ||
+ | } | ||
+ | if( metro.update() ){ | ||
+ | layer.update(); | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | <WRAP half column> | ||
+ | <file .pde Layer.pde> | ||
+ | import processing.sound.*; | ||
+ | |||
+ | class Layer { | ||
+ | |||
+ | Layer() { | ||
+ | } | ||
+ | |||
+ | void setup() | ||
+ | { | ||
+ | button | ||
+ | for ( int i = 0; i < 16; i++ ) { | ||
+ | button[i] = new Button(); | ||
+ | button[i].setup(100+(i%4)*100, | ||
+ | } | ||
+ | // 再生音声ファイルの読み込み | ||
+ | sound = new SoundFile(sq.this, | ||
+ | } | ||
+ | void update() | ||
+ | { | ||
+ | // ボタンが押されているなら | ||
+ | if( button[play_pos_previous].status == true ){ | ||
+ | button[play_pos_previous].c = color(200); | ||
+ | } | ||
+ | // 一つ前の該当ボタンは 50 の色に戻す | ||
+ | else{ | ||
+ | button[play_pos_previous].c = color(50); | ||
+ | } | ||
+ | |||
+ | // シーケンス箇所を一つ進める | ||
+ | play_pos++; | ||
+ | if( play_pos > 15 )play_pos = 0; | ||
+ | |||
+ | // そのシーケンス箇所は 150 の白色にする | ||
+ | button[play_pos].c = color(250); | ||
+ | if( button[play_pos].status == true ){ | ||
+ | println(" | ||
+ | sound.play();// | ||
+ | } | ||
+ | |||
+ | // 古いシーケンス場所として保存しておく。 | ||
+ | play_pos_previous = play_pos; | ||
+ | } | ||
+ | |||
+ | Button[] button; | ||
+ | SoundFile sound; | ||
+ | int play_pos; | ||
+ | int play_pos_previous; | ||
+ | } | ||
+ | |||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | 上記をもう少し、クラスとしてしっかりまとめてみます。修正したい箇所は以下の2点です。 | ||
+ | - metroのtrueなタイミングはlayer.update()で勝手に判断してほしい | ||
+ | - button[i].draw() を呼び出さず、layer.draw()で一括して呼び出したい。 | ||
+ | - buttonのstatusのupdate()は別途更新できるようにしたい(Layerが配列になった場合、任意のlayerのbutton statusだけ変更したいため) | ||
+ | |||
+ | ただし、layer.update()は現在Metroのtrueなタイミングに合わせて更新しているため、素早い更新が必要なbutton.update()との整合が取れていません。なので、ここで、layer.update()の仕様を少し変更します。MetroのtrueタイミングをLayer.update()内で判断できるようにします。以下が修正後の sq.pde, Layer.pde になります。 | ||
+ | |||
+ | <WRAP group> | ||
+ | <WRAP half column> | ||
+ | <file .pde sq.pde> | ||
+ | Metro metro; | ||
+ | Layer layer; | ||
+ | |||
+ | int play_pos = 0; | ||
+ | int play_pos_previous = 0; | ||
+ | |||
+ | void setup() | ||
+ | { | ||
+ | size(500, 500); | ||
+ | frameRate(1000); | ||
+ | |||
+ | // Metroの初期化 | ||
+ | metro = new Metro(); | ||
+ | metro.set(100); | ||
+ | |||
+ | layer = new Layer(); | ||
+ | layer.setup(); | ||
+ | } | ||
+ | |||
+ | |||
+ | void draw() | ||
+ | { | ||
+ | background(0); | ||
+ | |||
+ | layer.updateButtonStatus(); | ||
+ | layer.update(metro.update()); | ||
+ | layer.draw(); | ||
+ | } | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | <WRAP half column> | ||
+ | <file .pde Layer.pde> | ||
+ | import processing.sound.*; | ||
+ | |||
+ | class Layer { | ||
+ | |||
+ | Layer() { | ||
+ | } | ||
+ | |||
+ | void setup() | ||
+ | { | ||
+ | button | ||
+ | for ( int i = 0; i < 16; i++ ) { | ||
+ | button[i] = new Button(); | ||
+ | button[i].setup(100+(i%4)*100, | ||
+ | } | ||
+ | // 再生音声ファイルの読み込み | ||
+ | sound = new SoundFile(sq2.this, | ||
+ | } | ||
+ | void updateButtonStatus() | ||
+ | { | ||
+ | for ( int i = 0; i < 16; i++ ) { | ||
+ | button[i].update(); | ||
+ | } | ||
+ | } | ||
+ | void update(boolean _metro) | ||
+ | { | ||
+ | if ( _metro == false ) { | ||
+ | |||
+ | return; | ||
+ | } | ||
+ | |||
+ | // ボタンが押されているなら | ||
+ | if ( button[play_pos_previous].status == true ) { | ||
+ | button[play_pos_previous].c = color(200); | ||
+ | } | ||
+ | // 一つ前の該当ボタンは 50 の色に戻す | ||
+ | else { | ||
+ | button[play_pos_previous].c = color(50); | ||
+ | } | ||
+ | |||
+ | // シーケンス箇所を一つ進める | ||
+ | play_pos++; | ||
+ | if ( play_pos > 15 )play_pos = 0; | ||
+ | |||
+ | // そのシーケンス箇所は 150 の白色にする | ||
+ | button[play_pos].c = color(250); | ||
+ | if ( button[play_pos].status == true ) { | ||
+ | sound.play();// | ||
+ | } | ||
+ | |||
+ | // 古いシーケンス場所として保存しておく。 | ||
+ | play_pos_previous = play_pos; | ||
+ | } | ||
+ | |||
+ | void draw() | ||
+ | { | ||
+ | for ( int i =0; i < 16; i++ ) { | ||
+ | button[i].draw(); | ||
+ | } | ||
+ | } | ||
+ | Button[] button; | ||
+ | SoundFile sound; | ||
+ | int play_pos; | ||
+ | int play_pos_previous; | ||
+ | } | ||
+ | |||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | layer.update()関数にmetro.update()を渡すだけですべての更新ができるようになったほか、レイヤー描画もdraw()関数で一度にできるようになりました。結果として sq.pdeのdraw()関数が非常にスッキリしました。 | ||
+ | ====== Layerの配列化 ====== | ||
+ | さて、ここまで来たところで、現在のlayerを配列で宣言し直すことで、複数の楽器を割り当てることができます。ただし現状のままではどの配列のレイヤーにしても必ずハイハットの音になってしますので、layer.setup()の際に引数として読み込むファイル名を指定できるようにします。そしてlayerを配列として、それぞれ別の音を読み込みむように設定します。またキーボードの1, | ||
+ | <WRAP group> | ||
+ | <WRAP half column> | ||
+ | <file .pde sq.pde> | ||
+ | Metro metro; | ||
+ | Layer[] layer; | ||
+ | |||
+ | int play_pos = 0; | ||
+ | int play_pos_previous = 0; | ||
+ | int selected_layer = 0; | ||
+ | |||
+ | void setup() | ||
+ | { | ||
+ | size(500, 500); | ||
+ | frameRate(1000); | ||
+ | |||
+ | // Metroの初期化 | ||
+ | metro = new Metro(); | ||
+ | metro.set(100); | ||
+ | |||
+ | layer = new Layer[3]; | ||
+ | |||
+ | for ( int i = 0; i < 3; i++ ) { | ||
+ | layer[i] = new Layer(); | ||
+ | } | ||
+ | layer[0].setup(" | ||
+ | layer[1].setup(" | ||
+ | layer[2].setup(" | ||
+ | } | ||
+ | |||
+ | |||
+ | void draw() | ||
+ | { | ||
+ | background(0); | ||
+ | |||
+ | layer[selected_layer].updateButtonStatus(); | ||
+ | boolean flg = metro.update(); | ||
+ | for( int i = 0; i < 3; i++ ){ | ||
+ | layer[i].update(flg); | ||
+ | } | ||
+ | layer[selected_layer].draw(); | ||
+ | |||
+ | fill(255); | ||
+ | text(" | ||
+ | |||
+ | } | ||
+ | |||
+ | void keyPressed() | ||
+ | { | ||
+ | switch(key) | ||
+ | { | ||
+ | case ' | ||
+ | selected_layer = 0; | ||
+ | break; | ||
+ | case ' | ||
+ | selected_layer = 1; | ||
+ | break; | ||
+ | case ' | ||
+ | selected_layer = 2; | ||
+ | break; | ||
+ | } | ||
+ | |||
+ | // Screenshot用 | ||
+ | if ( key == ' | ||
+ | String name = year()+" | ||
+ | save(name); | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | <WRAP half column> | ||
+ | <file .pde Layer.pde> | ||
+ | import processing.sound.*; | ||
+ | |||
+ | class Layer { | ||
+ | |||
+ | Layer() { | ||
+ | } | ||
+ | |||
+ | void setup(String filename) | ||
+ | { | ||
+ | button | ||
+ | for ( int i = 0; i < 16; i++ ) { | ||
+ | button[i] = new Button(); | ||
+ | button[i].setup(100+(i%4)*100, | ||
+ | } | ||
+ | // 再生音声ファイルの読み込み | ||
+ | sound = new SoundFile(sq.this, | ||
+ | } | ||
+ | void updateButtonStatus() | ||
+ | { | ||
+ | for ( int i = 0; i < 16; i++ ) { | ||
+ | button[i].update(); | ||
+ | } | ||
+ | } | ||
+ | void update(boolean _metro) | ||
+ | { | ||
+ | if ( _metro == false ) { | ||
+ | |||
+ | return; | ||
+ | } | ||
+ | |||
+ | // ボタンが押されているなら | ||
+ | if ( button[play_pos_previous].status == true ) { | ||
+ | button[play_pos_previous].c = color(200); | ||
+ | } | ||
+ | // 一つ前の該当ボタンは 50 の色に戻す | ||
+ | else { | ||
+ | button[play_pos_previous].c = color(50); | ||
+ | } | ||
+ | |||
+ | // シーケンス箇所を一つ進める | ||
+ | play_pos++; | ||
+ | if ( play_pos > 15 )play_pos = 0; | ||
+ | |||
+ | // そのシーケンス箇所は 150 の白色にする | ||
+ | button[play_pos].c = color(250); | ||
+ | if ( button[play_pos].status == true ) { | ||
+ | sound.play();// | ||
+ | } | ||
+ | |||
+ | // 古いシーケンス場所として保存しておく。 | ||
+ | play_pos_previous = play_pos; | ||
+ | } | ||
+ | |||
+ | void draw() | ||
+ | { | ||
+ | for ( int i =0; i < 16; i++ ) { | ||
+ | button[i].draw(); | ||
+ | } | ||
+ | } | ||
+ | Button[] button; | ||
+ | SoundFile sound; | ||
+ | int play_pos; | ||
+ | int play_pos_previous; | ||
+ | } | ||
+ | |||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | ====== 最後に少しだけカスタマイズ ====== | ||
+ | 以上で基本的な機能を実装することができました。最後に少しだけカスタマイズを行ってみます。 | ||
+ | - いまどのレイヤーを選択しているか分かりづらいので、レイヤーごとにシーケンスの色を変更する(1: | ||
+ | |||
+ | この機能を実装するには Layerクラス内にcolorを保持しておく必要があります。特に今回の場合はシーケンス色はデフォルトの白ではなく、 | ||
+ | カスタムできるようにしておく必要があるわけです。下記のように sq.pde と Layer.pde を修正すれば良いです。 | ||
+ | |||
+ | <WRAP group> | ||
+ | <WRAP half column> | ||
+ | <file .pde sq.pde> | ||
+ | Metro metro; | ||
+ | Layer[] layer; | ||
+ | |||
+ | int play_pos = 0; | ||
+ | int play_pos_previous = 0; | ||
+ | int selected_layer = 0; | ||
+ | |||
+ | void setup() | ||
+ | { | ||
+ | size(500, 500); | ||
+ | frameRate(1000); | ||
+ | |||
+ | // Metroの初期化 | ||
+ | metro = new Metro(); | ||
+ | metro.set(100); | ||
+ | |||
+ | layer = new Layer[3]; | ||
+ | |||
+ | for ( int i = 0; i < 3; i++ ) { | ||
+ | layer[i] = new Layer(); | ||
+ | } | ||
+ | layer[0].setup(" | ||
+ | layer[0].setSqColor(color(250, | ||
+ | |||
+ | layer[1].setup(" | ||
+ | layer[1].setSqColor(color(0, | ||
+ | |||
+ | layer[2].setup(" | ||
+ | layer[2].setSqColor(color(0, | ||
+ | } | ||
+ | |||
+ | |||
+ | void draw() | ||
+ | { | ||
+ | background(0); | ||
+ | |||
+ | layer[selected_layer].updateButtonStatus(); | ||
+ | boolean flg = metro.update(); | ||
+ | for( int i = 0; i < 3; i++ ){ | ||
+ | layer[i].update(flg); | ||
+ | } | ||
+ | layer[selected_layer].draw(); | ||
+ | |||
+ | fill(255); | ||
+ | text(" | ||
+ | |||
+ | } | ||
+ | |||
+ | void keyPressed() | ||
+ | { | ||
+ | switch(key) | ||
+ | { | ||
+ | case ' | ||
+ | selected_layer = 0; | ||
+ | break; | ||
+ | case ' | ||
+ | selected_layer = 1; | ||
+ | break; | ||
+ | case ' | ||
+ | selected_layer = 2; | ||
+ | break; | ||
+ | } | ||
+ | |||
+ | // Screenshot用 | ||
+ | if ( key == ' | ||
+ | String name = year()+" | ||
+ | save(name); | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | <WRAP half column> | ||
+ | <file .pde Layer.pde> | ||
+ | import processing.sound.*; | ||
+ | |||
+ | class Layer { | ||
+ | |||
+ | Layer() { | ||
+ | } | ||
+ | |||
+ | void setup(String filename) | ||
+ | { | ||
+ | button | ||
+ | for ( int i = 0; i < 16; i++ ) { | ||
+ | button[i] = new Button(); | ||
+ | button[i].setup(100+(i%4)*100, | ||
+ | } | ||
+ | // 再生音声ファイルの読み込み | ||
+ | sound = new SoundFile(sq.this, | ||
+ | |||
+ | color_sq = color(250); | ||
+ | } | ||
+ | void updateButtonStatus() | ||
+ | { | ||
+ | for ( int i = 0; i < 16; i++ ) { | ||
+ | button[i].update(); | ||
+ | } | ||
+ | } | ||
+ | void update(boolean _metro) | ||
+ | { | ||
+ | if ( _metro == false ) { | ||
+ | |||
+ | return; | ||
+ | } | ||
+ | |||
+ | // ボタンが押されているなら | ||
+ | if ( button[play_pos_previous].status == true ) { | ||
+ | button[play_pos_previous].c = color(200); | ||
+ | } | ||
+ | // 一つ前の該当ボタンは 50 の色に戻す | ||
+ | else { | ||
+ | button[play_pos_previous].c = color(50); | ||
+ | } | ||
+ | |||
+ | // シーケンス箇所を一つ進める | ||
+ | play_pos++; | ||
+ | if ( play_pos > 15 )play_pos = 0; | ||
+ | |||
+ | // そのシーケンス箇所は color_sq の白色にする | ||
+ | button[play_pos].c = color(color_sq); | ||
+ | if ( button[play_pos].status == true ) { | ||
+ | sound.play();// | ||
+ | } | ||
+ | |||
+ | // 古いシーケンス場所として保存しておく。 | ||
+ | play_pos_previous = play_pos; | ||
+ | } | ||
+ | |||
+ | void draw() | ||
+ | { | ||
+ | for ( int i =0; i < 16; i++ ) { | ||
+ | button[i].draw(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void setSqColor(color _color){ | ||
+ | color_sq = _color; | ||
+ | } | ||
+ | Button[] button; | ||
+ | SoundFile sound; | ||
+ | int play_pos; | ||
+ | int play_pos_previous; | ||
+ | color color_sq; | ||
+ | } | ||
+ | |||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | 最後に出来上がったファイル一式を置いておきます。 | ||
+ | * 完成したProcessingファイルの一式:{{ : |