HAGIWO KICKに HIHATを加えた Twin Drams
概要
今回は、HAGIWO作品のドラムモジュールを作ることにした。下記の2つの記事を読み進めているうちに、2つのモジュールを一つにまとめられる気がしてきたのでトライしてみたら、思いのほかうまくいった。
ほぼ、HAGIWO作品の丸写しで、工夫したのは2つのモデルを統合するコード。といっても、オリジナルのHAGIWO コードを一部いじっただけ。本文
回路図
僭越ながら、HAGIWOさんのブログから回路図を拝借。2つのモデルのI/Oピンを図のように揃えた。
D3ピンをLEDの出力に割り当て、"HIHAT"にあったDecay用のLPF回路はそのまま使っている。LPF回路がないと、"HIHAT"でピー音が出たのでこのようにした。
"KICK"のOUTPUT D10は、LPFを2段階にして-12dBにしている。これを"HIHAT"でもそのまま使う。
蛇足ながら、KICADで描いた回路図とPCBレイアウト
code
元の2つのコードをトグルスイッチ(D2ピン)で切り換える。"HIHAT"にあったDecay出力とNPNトランジスタ回路の動作をソフトに置き換えた。
// 13_14_kick_or_Hihat.ino
#include <avr/io.h>
const static word fand[180] PROGMEM = {
0, 17, 34, 52, 69, 86, 103, 120, 137, 154, 171, 187, 203, 219, 234, 250, 264, 279, 293, 307, 321, 334, 347, 359, 371, 383, 394, 404, 414, 424, 433, 441, 449, 456, 463, 469, 475, 480, 485, 489, 492, 495, 497, 498, 499, 500, 499, 498, 497, 495, 492, 489, 485, 480, 475, 469, 463, 456, 449, 441, 433, 424, 414, 404, 394, 383, 371, 359, 347, 334, 321, 307, 293, 279, 264, 250, 234, 219, 203, 187, 171, 154, 137, 120, 103, 86, 69, 52, 34, 17, 0, -18, -35, -53, -70, -87, -104, -121, -138, -155, -172, -188, -204, -220, -235, -250, -265, -280, -294, -308, -322, -335, -348, -360, -372, -384, -395, -405, -415, -425, -434, -442, -450, -457, -464, -470, -476, -481, -486, -490, -493, -496, -498, -499, -500, -500, -500, -499, -498, -496, -493, -490, -486, -481, -476, -470, -464, -457, -450, -442, -434, -425, -415, -405, -395, -384, -372, -360, -348, -335, -322, -308, -294, -280, -265, -250, -235, -220, -204, -188, -172, -155, -138, -121, -104, -87, -70, -53, -35, -18
};
const static word harm[180] PROGMEM = {
0, 416, 365, 321, 334, 307, 279, 279, 254, 232, 227, 203, 185, 178, 155, 140, 131, 110, 97, 88, 68, 57, 48, 30, 21, 11, -4, -11, -21, -34, -40, -48, -60, -63, -71, -80, -82, -88, -95, -95, -101, -104, -103, -107, -109, -106, -109, -107, -103, -104, -101, -95, -95, -88, -82, -80, -71, -63, -60, -48, -40, -34, -21, -11, -4, 11, 21, 30, 48, 57, 68, 88, 97, 110, 131, 140, 155, 178, 185, 203, 227, 232, 254, 279, 279, 307, 334, 321, 365, 416, 0, -417, -366, -322, -335, -308, -280, -280, -255, -233, -228, -204, -186, -179, -156, -141, -132, -111, -98, -89, -69, -58, -49, -31, -22, -12, 3, 10, 20, 33, 39, 47, 59, 62, 70, 79, 81, 87, 94, 94, 100, 103, 102, 106, 108, 105, 108, 106, 102, 103, 100, 94, 94, 87, 81, 79, 70, 62, 59, 47, 39, 33, 20, 10, 3, -12, -22, -31, -49, -58, -69, -89, -98, -111, -132, -141, -156, -179, -186, -204, -228, -233, -255, -280, -280, -308, -335, -322, -366, -417
};
//decay wavetable
const static word decay_table[100] PROGMEM = {
100, 96, 94, 92, 91, 89, 87, 86, 84, 82, 81, 79, 78, 76, 74, 73, 71, 70, 68, 67, 65, 64, 63, 61, 60, 58, 57, 56, 54, 53, 52, 51, 49, 48, 47, 46, 44, 43, 42, 41, 40, 39, 38, 37, 36, 34, 33, 32, 31, 31, 30, 29, 28, 27, 26, 25, 24, 23, 23, 22, 21, 20, 19, 19, 18, 17, 17, 16, 15, 15, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, 8, 7, 7, 6, 6, 6, 6, 5, 5, 5, 5, 4, 4, 3, 3, 2, 1
};
// Pin definitions
int TRG = 5;
int SOUT = 10;
int LED = 3;
int TOG = 2;
// Global variables
unsigned int frq = 60000; // Default frequency for Kick waveform
float duty = 0.5;
int wave = 1000;
// Variables for Kick waveform generation
int h = 0;
int i = 0;
byte j = 1;
float k = 1;
int knob_tone_kick = 512;
int knob_harm_kick = 512;
int knob_attack_kick = 512;
int knob_decay_kick = 512;
int CV_harm_kick = 512;
// Variables for Hihat and Noise
//noise setting
unsigned int frq_hihat = 1000; // 周波数
float duty_hihat = 0.5;//pin10 for noise
unsigned int knob_pitch_hihat = 1;//knob AD
unsigned int knob_tone_hihat = 1;//knob AD
unsigned int CV_tone_hihat = 1;//CV AD
//decay setting
unsigned int knob_decay_hihat = 0;//knob AD
unsigned int CV_decay_hihat = 0;//CV AD
unsigned int decay_time_hihat = 0;
unsigned int d_duty_hihat = 100;//pin3 for decay
int i_hihat = 100;//decay wavetable reference
bool before_gate_hihat = 0;//0=no signal,1=gate in
bool gate_hihat = 0;//0=decay out end , 1=during decay out
long trigTimer_hihat = 0;//for generate decay time
bool prev_toggle_state ; // 前回のトグルスイッチの状態
bool isKickMode;
void setup() {
pinMode(SOUT, OUTPUT);
pinMode(TOG, INPUT_PULLUP);
pinMode(LED, OUTPUT);
// 初期トグル状態に応じてモードを設定
isKickMode = digitalRead(TOG) == HIGH;
if (isKickMode) {
setupKickMode();
} else {
setupHihatMode();
}
}
void loop() {
bool currentToggleState = digitalRead(TOG) == HIGH;
// トグル状態が変更された場合にモードを切り替え
if (currentToggleState != isKickMode) {
isKickMode = currentToggleState;
if (isKickMode) {
setupKickMode();
} else {
setupHihatMode();
}
}
// モードに応じて適切な出力を生成
if (isKickMode) {
generateKickWaveform();
} else {
generateHihatNoise();
}
}
void setupKickMode() {
// Timer1設定
TCCR1A = 0b00100001; // Set Timer1 for PWM mode on Channel B
TCCR1B = 0b00010001; // Set Timer1 prescaler and mode
OCR1A = (unsigned int)(8000000 / frq);
OCR1B = (unsigned int)(8000000 / frq * duty);
// Timer2停止
TCCR2A = 0b00000000; // Stop Timer2
TCCR2B = 0b00000000; // Stop Timer2
}
void setupHihatMode() {
// Timer1設定を保持
TCCR1A = 0b00100001; // Set Timer1 for PWM mode on Channel B
TCCR1B = 0b00010001; // Set Timer1 prescaler and mode
OCR1A = (unsigned int)(8000000 / 1000); // Hihatの基本周波数設定
OCR1B = (unsigned int)(8000000 / 1000 * 0.5); // Hihatのデューティサイクル設定
// Timer2設定
TCCR2A = 0b00100011; // Timer2 をPWMモードで設定
TCCR2B = 0b00001011; // Timer2 をPWMモードで設定
OCR2A = (unsigned int)(8000000 / 20000); // Timer2 の周波数設定
OCR2B = (unsigned int)(8000000 / 20000 * 0.5); // Timer2 のデューティサイクル設定
}
void generateKickWaveform() {
// Implement Kick waveform generation logic here
static int old_trig_kick = 0;
static int trig_kick = 0;
old_trig_kick = trig_kick;
trig_kick = digitalRead(TRG);
digitalWrite(LED, trig_kick);
if (old_trig_kick == 0 && trig_kick == 1) {
h = 0;
i = 0;
j = 0;
k = 1;
}
i++;
if (i > 179) {
knob_tone_kick = analogRead(0) / 64;
knob_harm_kick = (1023 - analogRead(1)) / 32;
knob_decay_kick = analogRead(3) / 16;
knob_attack_kick = analogRead(5) / 16;
CV_harm_kick = analogRead(6) / 32;
knob_harm_kick = knob_harm_kick - CV_harm_kick;
if (knob_harm_kick >= 31) {
knob_harm_kick = 31;
}
h++;
i = 0;
j = j + 5;
if (knob_decay_kick <= 32) {
k = k * (98 - (32 - knob_decay_kick)) / 100 - 10 / 100;
} else {
if (h < (16 - knob_tone_kick) / 2) {
k = 0.8 + h / 16 / 5;
} else {
k = k * (94 - (knob_decay_kick - 32)) / 100 - 10 / 100;
}
}
}
delayMicroseconds((knob_tone_kick) * j);
if (h == 0 && i < knob_attack_kick && knob_attack_kick >= 5) {
wave = (pgm_read_word(&(fand[random(0, 179)]))) * 32 + (pgm_read_word(&(harm[i]))) * (32 - knob_harm_kick);
} else if (h <= 1 && i > 25 && i < 90) {
wave = (pgm_read_word(&(fand[i]))) * (32 + knob_attack_kick / 3) + (pgm_read_word(&(harm[i]))) * (32 - knob_harm_kick);
} else {
wave = (pgm_read_word(&(fand[i]))) * 32 + (pgm_read_word(&(harm[i]))) * (32 - knob_harm_kick);
}
wave = wave * k;
wave = wave / 32 + 500;
duty = (float)wave / 1000;
OCR1B = (unsigned int)(8000000 / frq * duty);
}
void generateHihatNoise() {
// Implement Hihat and Noise generation logic here
// Hihat and Noise generation
before_gate_hihat = gate_hihat;//for gate signal detect
//------------------decay time setting---------------
knob_decay_hihat = analogRead(3);
CV_decay_hihat = analogRead(5);
decay_time_hihat = (knob_decay_hihat + CV_decay_hihat ) * 5;
//------------------noise setting-----------
knob_pitch_hihat = analogRead(1);
knob_tone_hihat = analogRead(0);
CV_tone_hihat = analogRead(6);
//------------------detect gate signal--------------
gate_hihat = digitalRead(TRG);
if (before_gate_hihat == 0 && gate_hihat == 1) {
i_hihat = 0;
}
//--------------10pin pwm setting-----------------
//MAKE SOME NOISE!!
frq_hihat = random(1 + knob_tone_hihat * 2 + CV_tone_hihat * 2, 2 + knob_tone_hihat * 2 + CV_tone_hihat * 2 + knob_pitch_hihat * 2);
duty_hihat = (float)random(1, 999) / 1000;
// TOP値指定
OCR1A = (unsigned int)(8000000 / frq_hihat );
//--------------3pin pwm setting-----------------
// TOP値指定
OCR2A = (unsigned int)(8000000 / 20000 );
if (knob_decay_hihat <= 970) {//make decay wave
if (i_hihat < 99 && (micros() - trigTimer_hihat >= decay_time_hihat)) {
i_hihat++;
d_duty_hihat = (pgm_read_word(&(decay_table[i_hihat]))) ;
trigTimer_hihat = micros();
}
} else {//decay knob max = constant output ( no decay )
d_duty_hihat = 100;
}
// Duty比指定
OCR2B = (unsigned int)(8000000 / 20000 * d_duty_hihat / 300);//300 is magic number
// D3ピンのデケイ信号でD10ピンのノイズ信号を制御
OCR1B = (unsigned int)(8000000 / frq_hihat * duty_hihat * d_duty_hihat / 100);
}
完成
ハード構成
- 電源:+12V (基板上でLODにより +5V を生成)
- マイコン:Atmega328P(16MHzのセラロックをつけ、単体使用)
入力
analogRead(0)
: トーン設定(Kick)analogRead(1)
: ハーモニクス設定(Kick)analogRead(3)
: ディケイ設定(KickとHihat)analogRead(5)
: アタック設定(Kick)およびディケイ設定(Hihat)analogRead(6)
: CV入力(Kickのハーモニクス、Hihatのトーン)
PWM設定
- Timer1 (Channel A): KickまたはHihatの基本周波数に応じたPWM出力設定
- Timer1 (Channel B): KickまたはHihatのデューティサイクル設定
- Timer2 (Channel A): Hihatの基本周波数設定
- Timer2 (Channel B): ディケイのPWM設定