楽器をつくる:シーケンサ編

差分

このページの2つのバージョン間の差分を表示します。

この比較画面へのリンク

両方とも前のリビジョン 前のリビジョン
次のリビジョン
前のリビジョン
楽器をつくる:シーケンサ編 [2018/12/26 12:26] – [Layerの配列化] baba楽器をつくる:シーケンサ編 [2020/06/20 11:28] (現在) – [発音] baba
行 1: 行 1:
 ====== 最初に ====== ====== 最初に ======
-このページではプログラミング初学者の人が簡単なミュージックシーケンサを作成するまでをまとめたウェブサイトになります。自作デバイス上でArduinoを動かすことを想定していますが、教材用にProcessingで動作するソフトウェア紹介します。+このページではプログラミング初学者の人が簡単なミュージックシーケンサを作成するまでをまとめたウェブサイトになります。自作デバイス上でArduinoを動かすことを想定していますが、教材用にProcessingで動作するソフトウェアとして紹介します。配列の宣言の仕方など、Arduinoの場合と少し異なりますので、適時読み替えてください
  
 ====== 設計 ====== ====== 設計 ======
行 9: 行 9:
 </html> </html>
  
-重要なのは想定する一つ一つの部品や機能をソフトウェア上でclassとして作成していってください。いわゆるオブジェクト指向で考えていきます。シーケンサの機能としては、レイヤー階層になっており、各レイヤーごとに4x4のボタンが用意されています。ボタンを押すごとにそのボタンのonとoffを切り替えることができます。onになったボタンはタイムラインに従って自動で音をします。+重要なのは想定する一つ一つの部品や機能をソフトウェア上でclassとして作成していってください。いわゆるオブジェクト指向で考えていきます。シーケンサの機能としては、レイヤー階層になっており、各レイヤーごとに4x4のボタンが用意されています。ボタンを押すごとにそのボタンのonとoffを切り替えることができます。onになったボタンはタイムラインに従って自動で発します。 
 + 
 +実際に完成するソフトウェアは下記のようなものです。では一緒にやってみましょう。 
 +<html> 
 +<iframe width="560" height="315" src="https://www.youtube.com/embed/jLvlKYu8v5w" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> 
 +</html>
  
-実際にコードを作成していく際、ベースから作る場合と、トップから作っていく場合がありますが、 
-馬場の普段の作り方は、トップから作っていく方法になります。一緒にやってみましょう。 
  
 ====== 最初のコード ====== ====== 最初のコード ======
行 103: 行 106:
   }   }
   void update()   void update()
-  {+  {   
     // マウスがボタン上にあるとき(ボタン押下処理)     // マウスがボタン上にあるとき(ボタン押下処理)
-    if ( sqrt( pow(x-mouseX, 2) + pow(y-mouseY, 2) ) < size/2 ) { +    if ( dist(x, y, mouseX, mouseY) < size/2 ) { 
-      + 
       // マウスボタンが押されている、且つ一つ前の状態では押されていなかったとき       // マウスボタンが押されている、且つ一つ前の状態では押されていなかったとき
       if ( mousePressed == true && previous_mousePressed == false) {       if ( mousePressed == true && previous_mousePressed == false) {
         status = !status; // falseならtrue, trueならfalseにする         status = !status; // falseならtrue, trueならfalseにする
-        + 
         // 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:
   }   }
 } }
-{{ :楽器をつくる:スクリーンショット_2018-12-22_9.11.29.png?nolink |}} 
 void draw() void draw()
 { {
行 157: 行 156:
 </file> </file>
  
-ここらで少しButtonクラスの記述が増えて来たので、別ファイルにクラスの記述を移します。クラスの記述部分をすべてButtonというタブに移します。+ここらで少しButtonクラスの記述が増えて来たので、下記のスクリーンショットのようにクラスの記述を別ファイルに移します。クラスの記述部分をすべてButtonというタブに移します。
  
 {{ :楽器をつくる:スクリーンショット_2018-12-22_9.11.29.png?nolink |}} {{ :楽器をつくる:スクリーンショット_2018-12-22_9.11.29.png?nolink |}}
  
 ====== タイムライン(自動演奏のための時間)機能を追加 ====== ====== タイムライン(自動演奏のための時間)機能を追加 ======
-は次にタイムラインを追加します。タイムラインは決められたテンポで順番にボタンのON、OFFに従って自動演奏を行います。そこでまず決められたテンポを算出するクラスとして、Metroクラスを作ります。常時このMetroをupdate()することで、指定テンポでtrueを返す設計にします。まずは次のようなクラスを作成します。下記コードは先程のButtonクラスと同様に、新規タブからMetroとして作成してください。+各ボタンのON/OFFの制御に関して上記までで記述できたので,次にタイムラインを追加します。タイムラインは決められたテンポで順番にボタンのON、OFFに従って自動演奏を行います。そこでまず決められたテンポを算出するクラスとして、Metroクラスを作ります。常時このMetroをupdate()することで、指定テンポでtrueを返す設計にします。まずは次のようなクラスを作成します。下記コードは先程のButtonクラスと同様に、新規タブからMetroとして作成してください。
  
 <file .pde Metro.pde> <file .pde Metro.pde>
行 181: 行 180:
   }   }
   int ms;   int ms;
-  long ms_stamp;+  long ms_stamp; // long はintよりも桁数の多い整数を扱うときに利用します.
 } }
 </file> </file>
行 297: 行 296:
   * {{ :楽器をつくる:drumsound.zip |}}   * {{ :楽器をつくる:drumsound.zip |}}
  
-ではsound libraryを利用して、それらを読み込み、シーケンスのタイミングに合わせて発話するようにします。今回はCHH.wavを利用します。+ではsound libraryを利用して(Minim Libraryでもよいですが,Version3以上からはsound libraryが公式の音を扱うライブラリになった為)、それらを読み込み、シーケンスのタイミングに合わせて発話するようにします。今回はCHH.wavを利用します。
  
 <file .pde sq.pde> <file .pde sq.pde>
行 361: 行 360:
  
 ====== Layerクラスを考える ====== ====== Layerクラスを考える ======
-さあ、ハイハットのチチチでなんとなくリズム刻めてきました。先程ダウンロードした音声ファイルにはこの他バスドラ、ハイハットがあるので、これらもシーケンス内で再生したいと考えます。しかしこの場合、ハイハットの音に対してどのように他の音を割り当てるかを考えなければいけません。シーケンサでは通常このようなものに対してレイヤー機能を利用します。つまりこれまで作成してきたものはハイハットレイヤーであり、この他に、バスドラ、スネアレイヤーを追加します。レイヤーの概念としてはボタンの上位層にあたるので、ここまでの sq.pde 内の処理をLayerクラスに移築していきます。というわけで、Layer.pdeを作成します。これまで通り、新規タブからLayerファイルを作成し、sq.pdeのsetup()での内容をLayerに移行してみます。まずはlayerを配列にせず、一枚だけのレイヤーでbuttonを包括するように記述し直します。新規に作成するLayer.pdeの他、sq.pdeも修正します。修正したコードは下記に示します。sq.pdeがスッキリしたのがわかると思います。+さあ、ハイハットのチチチでなんとなくリズム刻めてきました。先程ダウンロードした音声ファイルにはこの他バスドラ、ハイハットがあるので、これらもシーケンス内で再生したいと考えます。しかしこの場合、ハイハットの音に対してどのように他の音を割り当てるかを考えなければいけません。シーケンサでは通常このようなものに対してレイヤー機能を利用します。つまりこれまで作成してきたものはハイハットレイヤーであり、この他に、バスドラ、スネアレイヤーを追加します。レイヤーの概念としてはボタンの上位層にあたるので、ここまでの sq.pde 内の処理をLayerクラスに移築し、Layerクラス内でButtonクラスを扱うようにします。というわけで、Layer.pdeを作成します。これまで通り、新規タブからLayerファイルを作成し、sq.pdeのsetup()での内容をLayerに移行してみます。まずはlayerを配列にせず、一枚だけのレイヤーでbuttonを包括するように記述し直します。新規に作成するLayer.pdeの他、sq.pdeも修正します。修正したコードは下記に示します。sq.pdeがスッキリしたのがわかると思います。
  
 <WRAP group> <WRAP group>
行 710: 行 709:
 </WRAP> </WRAP>
  
 +====== 最後に少しだけカスタマイズ ======
 +以上で基本的な機能を実装することができました。最後に少しだけカスタマイズを行ってみます。
 +  - いまどのレイヤーを選択しているか分かりづらいので、レイヤーごとにシーケンスの色を変更する(1:赤、2:緑、3:青)
 +
 +この機能を実装するには 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); // 100[ms]おきにtrueを返す
 +
 +  layer = new Layer[3];
 +
 +  for ( int i = 0; i < 3; i++ ) {
 +    layer[i] = new Layer();
 +  }
 +  layer[0].setup("CHH.wav");
 +  layer[0].setSqColor(color(250,0,0));
 +  
 +  layer[1].setup("SD.wav");
 +  layer[1].setSqColor(color(0,250,0));
 +
 +  layer[2].setup("BD.wav");
 +  layer[2].setSqColor(color(0,0,250));
 +}
 +
 +
 +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("Layer: " + str(selected_layer+1), 20, 20);
 +  
 +}
 +
 +void keyPressed()
 +{
 +  switch(key)
 +  {
 +  case '1':
 +    selected_layer = 0;
 +    break;
 +  case '2':
 +    selected_layer = 1;
 +    break;
 +  case '3':
 +    selected_layer = 2;
 +    break;
 +  }
 +
 +  // Screenshot用
 +  if ( key == 's' ) {
 +    String name = year()+"-"+month()+"-"+day()+"-"+hour()+"-"+minute()+"-"+second() + ".png";
 +    save(name);
 +  }
 +}
 +</file>
 +</WRAP>
 +
 +<WRAP half column>
 +<file .pde Layer.pde>
 +import processing.sound.*;
 +
 +class Layer {
 +
 +  Layer() {
 +  }
 +
 +  void setup(String filename)
 +  {
 +    button  = new Button[16];
 +    for ( int i = 0; i < 16; i++ ) {
 +      button[i] = new Button();
 +      button[i].setup(100+(i%4)*100, 100+(i/4)*100, false);
 +    }
 +    // 再生音声ファイルの読み込み
 +    sound = new SoundFile(sq.this, filename);
 +    
 +    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;
 +}
 +
 +</file>
 +</WRAP>
 +</WRAP>
 +
 +最後に出来上がったファイル一式を置いておきます。
 +  * 完成したProcessingファイルの一式:{{ :楽器をつくる:sq.zip |}}
  • /home/users/2/lolipop.jp-4404d470cd64c603/web/ws/data/attic/楽器をつくる/シーケンサ編.1545794796.txt.gz
  • 最終更新: 2018/12/26 12:26
  • by baba