電子楽器を自作するシリーズ

電子楽器を作りたい話⑪ 発信部

 今までだいぶ迷走していた発信回路ですが決まったのでの記事を書きました。

 まず、分周器を使っているので出力は矩形波なのが望ましいです。また、自分の中のこだわりとしてセンサーは押すのではなく触れるだけで反応してくれるものが好ましいです。
 以上のことから以下のようなシステムを採用することにしました。

humanR_回路図
 もはや回路図を乗せる必要があるのかわかりませんが、それぞれの抵抗のマイコン側に触れることでその端子の入力電圧を下げ反応させています。また、マイコンは前回作ったモノを使用することにします。
 また、プログラムはA0~A4で音階や変化記号を押し、D2~D9でド~ドまでの一オクターブを弾けるようにします。


 このシステムは分周器の記事を書く前にはできていたのですが、記事を書くのがなぜか遅くなってしまいました。例によって、新せさいあー本体はもうほぼ完成してるのですぐに記事を書きたいと思います。それではまた今度。

シンセサイザーを作った話

  作っていたシンセサイザーもどきが完成したのでその報告を。

 今まで11回にわたって使う技術の紹介をしてきたのですが、それをひとまとめにしたものができました。
electronium_回路図

 左上から、発信回路、積分回路、分周回路、パスコン、ディレイ、左下に行って、オーバードライブ、動作確認LED、反転回路、加算回路、となっています。
 これを配線図にすると
electronium_プリント基板
 こうなりました。基板サイズはケースのサイズに合わせてあるので右側に余裕ができました。そのため拡張性が高くなっています。
 はんだ付け始めてからミスに気付いた部分が多々あるので、セラコンが一つ裏についていたりしますが気にしてはいけません。

 はんだ付けをすべて終わらせてケースに取り付けるとこのような感じになります。
前面

DSC01146
後面

DSC01147

 前面についているボリューム類はエフェクターのものです。また、背面から左側面に半田メッキ船がついており、これを触りながらセンサーに触れることで音を鳴らすことができます。

 基板にはピンソケットがついており、これをジャンパワイヤでつなぐことで音を変化させます。


 私はほとんど楽器を弾くことができないので演奏の動画は上げないでおきます。気が向いたら何か演奏してみようと思います。 
 
 今回はここまで、次はずっと作ると言っていて結局作っていないVVVFをそろそろちゃんと作りたいと思います。それではまた今度。

デジタルテルミンを作った話

 今まで一回も測距センサーを使ったことないので使ってみようということで、テルミンを作ってみました。

 今回使ってみたセンサーは、シャープ製のGP2Y0A21YKを使いました。
DSC01148
これです。有名な話ですが、このセンサーは左からVo,GND,Vccとなっており、それぞれの色のイメージとは違うので気を付けなければいけません。

 プログラムは下のように適当に書きます。

#define BEATTIME 20 //音を出している時間(msec)
#define SPEAKER 12 //スピーカーの出力ピン番号
int val1=0;
  
void setup() {
Serial.begin(9600);
}

void loop() {
  val1=analogRead(0);
  Serial.println(val1);
  tone(SPEAKER,val1,BEATTIME) ; // ド
  delay(BEATTIME) ;
}

 適当にデータを拾って適当に出力します。センサーの出力がArduinoのアナログインプットで100~600ぐらいなのでそのままそれを周波数として発信させてもいいと思います。

 今回はここまでです。それではまた今度。

マルチバイブレータとか音階とかの話

 また、音の出るなんかを作ろうとしているのでいろいろやっています。VVVFは、なぜか作った基板のハーフブリッジドライバの出力が同相になるので、またいろいろ余裕があるときにどうにかします。

 まあマルチバイブレータはLチカなのでできない人はいないと思うのですが、周波数の計算式とか諸々を忘れるので、そのメモになります。

 まず、マルチバイブレータの回路図はこのようになります。1
 周波数fはf=1/1.38RCです。まあ、時定数の問題なので、普通に求めようと思っても求まります。
 今回、自分は可変周波数にしたいので、Rを10kΩのBカーブの二連スライド抵抗にしました。また、Rcを100Ω,Cを0.2μFにしました。


 次に音階と周波数の話になります。
 音階のことはよくわからないのですが、440Hzのラの音が基準となっています。ピアノだと、このラの音を0として-48~+39までの音があるそうです。それぞれの周波数は"=440*2^(A1/12)"を計算すると出てきます。例えば、この数式をエクセルなどにコピペして計算してあげると、ピアノの音階の表ができます。


 そんなこんなで適当に計算すると、二連スライダで基準のラを挟む1オクターブを入れるためにはCを0.2μFにし、二連スライダの前に3848~3886Ωの抵抗を入れればよいことになります。やたらとシビアですけど、気にしてはいけません。

 今回はここまでです。徐々にトライアックの扱いに慣れたいと思っているので、次はトライアック関係のことでもしようかと思います。それではまた今度。

モーターを鳴らしてみた話

 前回の記事でも言ってましたが、なんか音の出るものを作りたかったのでその開発をしてみました。

 インターフェースを面白くしたかったのですが、技術力が足りないのでいつか使った"CapSense"を使いました。

 1オクターブは12音なので、これをCapSenceで読み取るには12個のデジタルピンが必要になります。これは一台のArduino UNOで扱うのは厳しいです。また、なるべく出力側のコードは軽くしたいので、Arduino MEGAも使い、Arduino2台体制で作ることになりました。そのほかにもいろいろこだわりがあり、大まかなシステムはこのようになりました。electronium
 今回初めて知ったのですが、Arduino MEGAはSCL,SDAはD20,D21ピンでUNOとは違うのでその点注意しなければなりません。

DSC01284
DSC01283
 LCDを使いたかったのはただの趣味です。
________________________________
|押してるキー 選択してるオクターブ |
|出力周波数                                            |
|_______________________________|
 こんな感じに表示させます。

DSC01281
 入力インターフェースはこんな感じです。
 前に書いた記事のセンサーを大量に並べただけです。


 プログラムはこのようになりました。難しいことは何もやってません。
・Master
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x3F, 16, 2); // I2C: 0x3F, 16x2 LCD

int ca, cb;
int freq;
void setup() {
  Wire.begin();
  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  pinMode(13, OUTPUT);
}

void loop() {
  Wire.requestFrom(8, 2);

  while (Wire.available()) {
    ca = Wire.read();
    cb = Wire.read();
    Serial.print(ca);
    Serial.print(",");
    Serial.println(cb);
  }


  lcd.setCursor(0, 0);
  if (ca < 10) {
    lcd.print(" ");
  }
  lcd.print(ca);
  lcd.setCursor(3, 0);
  lcd.print(cb);

  lcd.setCursor(0, 1);
  freq = 440 * pow(2, (ca + 3 + (cb - 2) * 12) / 12.0);
  if (ca == 13) {
    lcd.print("No Sounds");
    delay(10);
    noTone(13);
  } else {
    if (freq < 1000) {
      lcd.print(" ");
    }
    lcd.print(freq);
    lcd.print("Hz   ");
    tone(13, freq);
  }
  delayMicroseconds(1000000 / freq);
}

・Slave
#include <Wire.h>
#include <CapSense.h>

#define thre 60

CapSense   cs_35_22 = CapSense(35, 22);
CapSense   cs_35_23 = CapSense(35, 23);
CapSense   cs_35_24 = CapSense(35, 24);
CapSense   cs_35_25 = CapSense(35, 25);
CapSense   cs_35_26 = CapSense(35, 26);
CapSense   cs_35_27 = CapSense(35, 27);
CapSense   cs_35_28 = CapSense(35, 28);
CapSense   cs_35_29 = CapSense(35, 29);
CapSense   cs_35_30 = CapSense(35, 30);
CapSense   cs_35_31 = CapSense(35, 31);
CapSense   cs_35_32 = CapSense(35, 32);
CapSense   cs_35_33 = CapSense(35, 33);
CapSense   cs_35_34 = CapSense(35, 34);

CapSense   cs_38_36 = CapSense(38, 36);
CapSense   cs_38_37 = CapSense(38, 37);

int freqa;
int freqb=3;
int numbb=2;

void setup() {
  Wire.begin(8);
  Wire.onRequest(requestEvent);
}

void loop() {
  int tonea[14];
  tonea[0] =  cs_35_22.capSense(30);
  tonea[1] =  cs_35_23.capSense(30);
  tonea[2] =  cs_35_24.capSense(30);
  tonea[3] =  cs_35_25.capSense(30);
  tonea[4] =  cs_35_26.capSense(30);
  tonea[5] =  cs_35_27.capSense(30);
  tonea[6] =  cs_35_28.capSense(30);
  tonea[7] =  cs_35_29.capSense(30);
  tonea[8] =  cs_35_30.capSense(30);
  tonea[9] =  cs_35_31.capSense(30);
  tonea[10] =  cs_35_32.capSense(30);
  tonea[11] =  cs_35_33.capSense(30);
  tonea[12] =  cs_35_34.capSense(30);
  tonea[13] = 0;
  
  int toneb[6];
  toneb[0] =  cs_38_36.capSense(30);
  toneb[1] =  cs_38_37.capSense(30);
  toneb[2] = 0;
  
  int numa = 13;
  int numb = 2;

  for (int i = 0; i < 13; i++) {
    if (tonea[i] > tonea[numa]) {
      numa = i;
    }
  }
  if (tonea[numa] < 60) {
    numa = 13;
  }
  freqa = numa;
  
  for (int i = 0; i < 2; i++) {
    if (toneb[i] > toneb[numb]) {
      numb = i;
    }
  }
  if (toneb[numb] < 60) {
    numb = 2;
  }
  
  if(numbb==2 && numb==0 &&freqb>0){
    freqb--;
  }
  
  if(numbb==2 && numb==1 &&freqb<4){
    freqb++;
  }
  
  numbb=numb;
  
  delay(10);
}

void requestEvent() {

  Wire.write(freqa);
  Wire.write(freqb);
}

 こんな感じです。適当なプログラムですが、自分では満足しているのでよしとします。

 ほんとはこれにフィルターとかエフェクターとか掛けようとも思っていたんですが、自分に音楽センスがないので止めました。その代わりなんか面白いことできないかと思ってたんですが、モーターを鳴らしたことがなかったので鳴らしてみました。
 まあただ出力信号を2sk2232で増幅してモーターに流すだけです。
 こんな感じでなります。

 今回はここまでです。なんか全然まとまってないんですがまあいいです。最近はデスク周りの整備をしたので次回はその記事でも書きます。それではまた今度。

ProcessingとArduinoでModulinもどきを作った話

 お久しぶりです。春休みに入って暇になり、留学がなくなり暇になり、春休みが伸びて暇をしています。電動バイクは減速機が作れずにn回目の計画破棄をしたので進捗はないです。

 そんなこんなでなんとなくModulinもどきを作ってみました。
 Modulinはこんなやつです。


 これは確かどこかのシンセサイザー会社が限定で作ったモジュラーシンセサイザーで、センサーだけでものすごく高かったので、自分は楽器が弾けませんが作ってみました。

 作るにあたって、このセンサーと同じようなものや同じような働きのセンサーがありました。それで長いこと探していた時に、この前の記事の時に使っていたものを見つけました。このsoft potというセンサーはは秋月では売ってなく、千石では短いものしかなく、マルツにも在庫がなく、スイッチサイエンスは取扱しておらず、国内で買うことがたぶんできない状態でした。それなので今回はMouserから買いました。なんか関税とかかかって多少割高になりますがこれで買うことができました。

 システムは下のようになります。今回、処理はほぼすべてProcessingで行ったのでArduinoはセンサーの値をまとめてシリアルで送っているだけです。
Untitled Diagram

 センサーには今回、つまみ用の可変抵抗10個、スライド抵抗1個、モーメンタリトグルスイッチ1個、SoftPot 500mm 1個、圧力センサー1個、を使いました。これらの値をArduino MEGAで読み取りProcessingに送っています。

 Processingに送られたデータはMinimライブラリのパラメータにします。MinimはProcessingのライブラリで、これを使うことで様々な波形を出したりそれにエフェクトをかけたりすることができます。今回はこちらのブログを参考にVCO,VCF,VCA,EG,LFOを実装しました。

 これらをいい感じに実装すると下のようになりました。
ah5uf-ml17t
a2mbv-4c1fh

 実際にならしてみるとこんな感じです。
 音楽ができないので引くことはできないのですが自分的には結構いい感じにできたので満足しています。

 今回はここまでです。自分の中ではいい感じにできたので良かったです。それではまた今度。

プログラム(Arduino)
int recv_data_a;
int recv_data_b;
int data=10;
void setup() {
  Serial.begin(250000);
}

void loop() {
  delay(1);
  int value0 = analogRead(0);
  int value1 = analogRead(1);
  int value2 = analogRead(2);
  int value3 = analogRead(3);
  int value4 = analogRead(4);
  int value5 = analogRead(5);
  int value6 = analogRead(6);
  int value7 = analogRead(7);
  int value8 = analogRead(8);
  int value9 = analogRead(9);
  int value10 = analogRead(10);
  int value11 = analogRead(11);
  int value12 = analogRead(12);
  int value13 = analogRead(13);
  recv_data_a = analogRead(14);
  int value15 = analogRead(15);
  if(450 < recv_data_b && recv_data_b < 600){
    if(recv_data_a > 600){
      data++;
    }
    if(recv_data_a < 450){
      data--;
    }
  }
  recv_data_b = recv_data_a;
  Serial.write('H');
  Serial.write(highByte(value0));
  Serial.write(lowByte(value0));
  Serial.write(highByte(value1));
  Serial.write(lowByte(value1));
  Serial.write(highByte(value2));
  Serial.write(lowByte(value2));
  Serial.write(highByte(value3));
  Serial.write(lowByte(value3));
  Serial.write(highByte(value4));
  Serial.write(lowByte(value4));
  Serial.write(highByte(value5));
  Serial.write(lowByte(value5));
  Serial.write(highByte(value6));
  Serial.write(lowByte(value6));
  Serial.write(highByte(value7));
  Serial.write(lowByte(value7));
  Serial.write(highByte(value8));
  Serial.write(lowByte(value8));
  Serial.write(highByte(value9));
  Serial.write(lowByte(value9));
  Serial.write(highByte(value10));
  Serial.write(lowByte(value10));
  Serial.write(highByte(value11));
  Serial.write(lowByte(value11));
  Serial.write(highByte(value12));
  Serial.write(lowByte(value12));
  Serial.write(highByte(value13));
  Serial.write(lowByte(value13));
  Serial.write(highByte(data));
  Serial.write(lowByte(data));
  Serial.write(highByte(value15));
  Serial.write(lowByte(value15));
  Serial.println(value15);
}


プログラム(Processing)
import ddf.minim.spi.*;
import ddf.minim.signals.*;
import ddf.minim.*;
import ddf.minim.analysis.*;
import ddf.minim.ugens.*;
import ddf.minim.effects.*;
import  processing.serial.*;

Serial  serial;
Minim minim;
AudioOutput out;
// variance for VCO
Oscil oscil;
int octave;
// variance for VCF
MoogFilter filter;
// variance for VCA
ADSR adsr;
Multiplier multiplier;
// variance for LFO
Oscil vcoLfo, vcfLfo, vcaLfo;
float vcoLfoIntensity;
// variance for keyboard
boolean isbarLocked;
char lockedKey;
int recv_data_0;
int recv_data_1;
int recv_data_2;
int recv_data_3;
int recv_data_4;
int recv_data_5;
int recv_data_6;
int recv_data_7;
int recv_data_8;
int recv_data_9;
int recv_data_10;
int recv_data_11;
int recv_data_12;
int recv_data_13;
int recv_data_14;
int recv_data_15;
float freq[] = new float [1024];


void setup() {
  freq();
  size(1024, 100);
  textAlign(CENTER);
  fill(255);
  background( 0 );
  textSize(36);
  serial = new Serial( this, Serial.list()[1], 250000 );

  minim = new Minim(this);
  out = minim.getLineOut();
  oscil = new Oscil(440, 1.0, Waves.SQUARE);
  filter = new MoogFilter(10000, 0.0, MoogFilter.Type.LP);
  multiplier = new Multiplier(1.0);
  adsr = new ADSR(1.0, 1.0, 1.0, 0.5, 1.0);
  oscil.patch(filter).patch(multiplier).patch(adsr);
  vcoLfo = new Oscil(10, 0.0, Waves.SINE);
  vcoLfo.patch(oscil.frequency);
  vcfLfo = new Oscil(10, 0.0, Waves.SINE);
  vcfLfo.amplitude.setLastValue(0.0);
  vcfLfo.offset.setLastValue(10000);
  vcfLfo.patch(filter.frequency);
  vcaLfo = new Oscil(10, 0.0, Waves.SINE);
  vcaLfo.amplitude.setLastValue(0.0);
  vcaLfo.offset.setLastValue(1.0);
  vcaLfo.patch(multiplier.amplitude);
}

void setVcoWave() {//2
  switch(int(map(recv_data_2, 0, 1023, 0, 6))) {
    case(0):
    oscil.setWaveform(Waves.SINE);
    break;
    case(1):
    oscil.setWaveform(Waves.TRIANGLE);
    break;
    case(2):
    oscil.setWaveform(Waves.SAW);
    break;
    case(3):
    oscil.setWaveform(Waves.SQUARE);
    break;
  }
}

void setLfoWave() {//3
  switch(int(map(recv_data_3, 0, 1023, 0, 6))) {
    case(0):
    vcoLfo.setWaveform(Waves.SINE);
    vcfLfo.setWaveform(Waves.SINE);
    vcaLfo.setWaveform(Waves.SINE);
    break;
    case(1):
    vcoLfo.setWaveform(Waves.TRIANGLE);
    vcfLfo.setWaveform(Waves.TRIANGLE);
    vcaLfo.setWaveform(Waves.TRIANGLE);
    break;
    case(2):
    vcoLfo.setWaveform(Waves.SAW);
    vcfLfo.setWaveform(Waves.SAW);
    vcaLfo.setWaveform(Waves.SAW);
    break;
    case(3):
    vcoLfo.setWaveform(Waves.SQUARE);
    vcfLfo.setWaveform(Waves.SQUARE);
    vcaLfo.setWaveform(Waves.SQUARE);
    break;
  }
}

void setCutoffFrequency() {//4
  vcfLfo.amplitude.setLastValue(map(recv_data_4, 0, 1023, 0, 10000) / 2.0 * map(recv_data_8, 0, 1023, 0, 1));
  vcfLfo.offset.setLastValue(map(recv_data_4, 0, 1023, 0, 10000));
}

void setResonance() {//5
  filter.resonance.setLastValue(map(recv_data_5, 0, 1023, 0, 1));
}

void setLfoRate() {//6
  float value = map(recv_data_6, 0, 1023, 0, 100);
  vcoLfo.setFrequency(value);
  vcfLfo.setFrequency(value);
  vcaLfo.setFrequency(value);
}

//7

void setVcfIntensity() {//8
  float value = map(recv_data_8, 0, 1023, 0, 1);
  vcfLfo.amplitude.setLastValue(map(recv_data_4, 0, 1023, 0, 10000) / 2.0 * value);
}

void setVcaIntensity() {//9
  float value = map(recv_data_9, 0, 1023, 0, 1);
  vcaLfo.amplitude.setLastValue(map(recv_data_15, 0, 1023, 0, 1) / 2.0 * value);
}

void setParameters() {//10,11,12,13
  adsr.setParameters(1.0, map(recv_data_10, 0, 1023, 0, 1), map(recv_data_11, 0, 1023, 0, 1), map(recv_data_12, 0, 1023, 0, 1), map(recv_data_13, 0, 1023, 0, 1), 0.0, 0.0);
}

void setAmplitude() {//15
  float value = map(recv_data_15, 0, 1023, 0, 1);
  vcaLfo.amplitude.setLastValue(value / 2.0 * map(recv_data_9, 0, 1023, 0, 1));
  vcaLfo.offset.setLastValue(value);
}

void serialEvent(Serial port) {  
  if ( port.available() >= 33 ) {
    if ( port.read() == 'H' ) {
      //0
      int high = port.read();
      int low = port.read();
      recv_data_0 = high * 256 + low;
      //1
      high = port.read();
      low = port.read();
      recv_data_1 = high * 256 + low;
      //2
      high = port.read();
      low = port.read();
      recv_data_2 = high * 256 + low;
      //3
      high = port.read();
      low = port.read();
      recv_data_3 = high * 256 + low;
      //4
      high = port.read();
      low = port.read();
      recv_data_4 = high * 256 + low;
      //5
      high = port.read();
      low = port.read();
      recv_data_5 = high * 256 + low;
      //6
      high = port.read();
      low = port.read();
      recv_data_6 = high * 256 + low;
      //7
      high = port.read();
      low = port.read();
      recv_data_7 = high * 256 + low;
      //8
      high = port.read();
      low = port.read();
      recv_data_8 = high * 256 + low;
      //9
      high = port.read();
      low = port.read();
      recv_data_9 = high * 256 + low;
      //10
      high = port.read();
      low = port.read();
      recv_data_10 = high * 256 + low;
      //11
      high = port.read();
      low = port.read();
      recv_data_11 = high * 256 + low;
      //12
      high = port.read();
      low = port.read();
      recv_data_12 = high * 256 + low;
      //13
      high = port.read();
      low = port.read();
      recv_data_13 = high * 256 + low;
      //14
      high = port.read();
      low = port.read();
      recv_data_14 = high * 256 + low;
      //15
      high = port.read();
      low = port.read();
      recv_data_15 = high * 256 + low;

      setVcoWave();//2
      setLfoWave();//3
      setCutoffFrequency();//4
      setResonance();//5
      setLfoRate();//6
      setVcfIntensity();//8
      setVcaIntensity();//9
      setParameters();//10,11,12,13
      setAmplitude();//15
      
       print(recv_data_0);
       print(",");
       println(440*pow(2,freq[recv_data_0]/12.0));
       
    }
  }
}

void draw() {
  float waveH = 50;
  background(0);
  for(int i = 0; i < out.bufferSize()-1; i++)
  {
    point(i, 50 + out.left.get(i)*waveH);
  }
  stroke(255);
  if (recv_data_0>=18&&recv_data_1<=1000) {
    vcoLfo.amplitude.setLastValue(440*pow(2, (freq[recv_data_0]/12.0)+recv_data_14-10) / 2.0 * map(recv_data_7, 0, 1023, 0, 1));
    vcoLfo.offset.setLastValue(440*pow(2, (freq[recv_data_0]/12.0)+recv_data_14-10));
    adsr.unpatch(out);
    adsr.noteOn();
    adsr.patch(out);
    isbarLocked = true;
  } else {
    if (isbarLocked) {
      adsr.unpatchAfterRelease(out);
      adsr.noteOff();
      isbarLocked = false;
    }
  }
}
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計:

読者登録
LINE読者登録QRコード