ATMEGA328を搭載した Arduino Duemilanove 互換機で音をPWM D/A変換出力するシンセサイザーライブラリです。
Revisão | 3c6fdeedb6f0127f4fa8ced72842e82e8be5716d (tree) |
---|---|
Hora | 2020-04-18 23:56:21 |
Autor | ![]() |
Commiter | Akiyoshi Kamide |
Doc/keywords update, and macro refactored
@@ -1,5 +1,5 @@ | ||
1 | 1 | // |
2 | -// PWM DAC Synthesizer ver.20200413 | |
2 | +// PWM DAC Synthesizer ver.20200418 | |
3 | 3 | // by Akiyoshi Kamide (Twitter: @akiyoshi_kamide) |
4 | 4 | // http://kamide.b.osdn.me/pwmdac_synth_lib/ |
5 | 5 | // https://osdn.jp/users/kamide/pf/PWMDAC_Synth/ |
@@ -16,17 +16,7 @@ | ||
16 | 16 | #define cbi16(sfr, bit) (_SFR_WORD(sfr) &= ~_BV(bit)) |
17 | 17 | #define sbi16(sfr, bit) (_SFR_WORD(sfr) |= _BV(bit)) |
18 | 18 | |
19 | -// Function-to-array generator | |
20 | -#define FX2(f,x) f(x), f(x + 1) | |
21 | -#define FX4(f,x) FX2(f,x), FX2(f,x + 2) | |
22 | -#define FX8(f,x) FX4(f,x), FX4(f,x + 4) | |
23 | -#define FX16(f,x) FX8(f,x), FX8(f,x + 8) | |
24 | -#define FX32(f,x) FX16(f,x),FX16(f,x + 16) | |
25 | -#define FX64(f,x) FX32(f,x),FX32(f,x + 32) | |
26 | -#define FX128(f,x) FX64(f,x),FX64(f,x + 64) | |
27 | -#define ARRAY128(f) {FX128(f,0)} | |
28 | -#define ARRAY256(f) {FX128(f,0),FX128(f,128)} | |
29 | - | |
19 | +// Default parameter | |
30 | 20 | #ifndef PWMDAC_OUTPUT_PIN |
31 | 21 | #define PWMDAC_OUTPUT_PIN 3 |
32 | 22 | #endif |
@@ -37,7 +27,8 @@ | ||
37 | 27 | #define PWMDAC_POLYPHONY 6 |
38 | 28 | #endif |
39 | 29 | |
40 | -// Built-in wavetable generator | |
30 | +// | |
31 | +// Built-in wavetable functions | |
41 | 32 | // x = Phase angle : 0x00...0x80(PI_radian)...0xFF |
42 | 33 | // f(x) = Wave voltage at the x : 0x00(min)...0x80(center)...0xFF(max) |
43 | 34 | #define PWMDAC_SQUARE_WAVE(x) (((x) < 0x80 ? 0x00 : 0xFF) / PWMDAC_POLYPHONY) |
@@ -46,17 +37,26 @@ | ||
46 | 37 | #define PWMDAC_MAX_VOLUME_SINE_WAVE(x) ((byte)( 0x80 * (1 + sin(PI * (x) / 0x80)) )) |
47 | 38 | #define PWMDAC_SINE_WAVE(x) ((byte)( 0x80 * (1 + sin(PI * (x) / 0x80)) / PWMDAC_POLYPHONY )) |
48 | 39 | #define PWMDAC_SHEPARD_TONE(x) ((byte)( 0x80 * (8 \ |
49 | - +sin(PI * (x) / 0x80) \ | |
50 | - +sin(PI * (x) / 0x40) \ | |
51 | - +sin(PI * (x) / 0x20) \ | |
52 | - +sin(PI * (x) / 0x10) \ | |
53 | - +sin(PI * (x) / 0x08) \ | |
54 | - +sin(PI * (x) / 0x04) \ | |
55 | - +sin(PI * (x) / 0x02) \ | |
56 | - +sin(PI * (x) / 0x01) \ | |
57 | - ) / (8 * PWMDAC_POLYPHONY) )) | |
40 | + +sin(PI * (x) / 0x80) \ | |
41 | + +sin(PI * (x) / 0x40) \ | |
42 | + +sin(PI * (x) / 0x20) \ | |
43 | + +sin(PI * (x) / 0x10) \ | |
44 | + +sin(PI * (x) / 0x08) \ | |
45 | + +sin(PI * (x) / 0x04) \ | |
46 | + +sin(PI * (x) / 0x02) \ | |
47 | + +sin(PI * (x) / 0x01) \ | |
48 | + ) / (8 * PWMDAC_POLYPHONY) )) | |
49 | + | |
50 | +#define FX2(f,x) f(x), f(x + 1) | |
51 | +#define FX4(f,x) FX2(f,x), FX2(f,x + 2) | |
52 | +#define FX8(f,x) FX4(f,x), FX4(f,x + 4) | |
53 | +#define FX16(f,x) FX8(f,x), FX8(f,x + 8) | |
54 | +#define FX32(f,x) FX16(f,x), FX16(f,x + 16) | |
55 | +#define FX64(f,x) FX32(f,x), FX32(f,x + 32) | |
56 | +#define FX128(f,x) FX64(f,x), FX64(f,x + 64) | |
57 | +#define FX256(f,x) FX128(f,x), FX128(f,x + 128) | |
58 | 58 | |
59 | -#define PWMDAC_CREATE_WAVETABLE(table, function) PROGMEM const byte table[] = ARRAY256(function) | |
59 | +#define PWMDAC_CREATE_WAVETABLE(table, func) PROGMEM const byte table[] = { FX256(func,0) } | |
60 | 60 | |
61 | 61 | // [Phase-correct PWM dual-slope] |
62 | 62 | // TCNTn value changes to: |
@@ -277,7 +277,7 @@ class PWMDACSynth { | ||
277 | 277 | wavetable = this->channel->wavetable; |
278 | 278 | envelope = this->channel->envelope; |
279 | 279 | modulation = &(this->channel->modulation); |
280 | - static PROGMEM const unsigned long phase_speed_table[] = ARRAY128(PHASE_SPEED_OF); | |
280 | + static PROGMEM const unsigned long phase_speed_table[] = { FX128(PHASE_SPEED_OF,0) }; | |
281 | 281 | dphase32_original = pgm_read_dword(phase_speed_table + (this->note = note)); |
282 | 282 | dphase32_bended = this->channel->bendedPitchOf(dphase32_original); |
283 | 283 | dphase32_real = dphase32_bended + dphase32_moffset; |
@@ -1,17 +1,17 @@ | ||
1 | 1 | |
2 | 2 | [PWMDAC_Synth - PWM DAC synthesizer library for Arduino] |
3 | 3 | |
4 | -ver.20200405 | |
4 | +ver.20200418 | |
5 | 5 | |
6 | 6 | https://osdn.jp/users/kamide/pf/PWMDAC_Synth/wiki/FrontPage |
7 | 7 | |
8 | -Arduinoで動作する簡易シンセサイザライブラリです。 | |
9 | 8 | |
10 | 9 | これは、CAmiDion |
11 | 10 | |
12 | 11 | http://www.yk.rim.or.jp/~kamide/music/chordhelper/hardware/ |
13 | 12 | |
14 | -の2号機以降で実装した音源をライブラリ化したものです。 | |
13 | +の2号機以降で実装した音源をライブラリとして独立させた、 | |
14 | +Arduino互換機用のPWMシンセサイザライブラリです。 | |
15 | 15 | |
16 | 16 | |
17 | 17 | ●Arduinoで楽器を作ろうとして、こんな問題にぶち当たったことはありませんか? |
@@ -52,6 +52,8 @@ Arduino | ||
52 | 52 | 基本的な使い方を下記に示します。なお、ノートオン、ノートオフなど、音の出し方については |
53 | 53 | Arduino IDE のメニューからたどれる「スケッチの例」を参考にしてください。 |
54 | 54 | |
55 | + 【サンプルコード】 | |
56 | + // | |
55 | 57 | // 必要に応じ、PWMDAC_Synth.h をインクルードする前に #define で下記を指定できます。 |
56 | 58 | // |
57 | 59 | #define PWMDAC_OUTPUT_PIN 3 // PWM出力ピン番号(省略可:下記「●出力ピン(PWM)」参照) |
@@ -101,9 +103,14 @@ Arduino | ||
101 | 103 | デフォルトは6重和音です。 |
102 | 104 | PWMDAC_POLYPHONY に数値を設定してコンパイルし直すことで増減可能です。 |
103 | 105 | |
104 | - 同時発音数を増やすと、その分、割り込み処理 ISR() の実行に時間がかかります。 | |
105 | - 割り込み以外の処理を行うための時間的余裕がなくなった時点で動作しなくなります。 | |
106 | - そうなる寸前が、事実上の同時発音数の上限です。 | |
106 | + ※ 同時発音数を増やしていくと、その分 | |
107 | + ISR() (Interrupt Service Routine: 割り込みサービスルーチン)における | |
108 | + ループ回数が増え、多忙になってきます。あまりに多忙過ぎて loop() を実行する | |
109 | + 暇がなくなると、動作不能に陥ります。そうなる寸前が、同時発音数の事実上の上限です。 | |
110 | + | |
111 | + ※ ISR() などで動作速度が要求されるため、PWMDAC_Synth.h には | |
112 | + #pragma GCC optimize ("-O3") | |
113 | + が指定されています。コンパイルの最適化基準を、このライブラリだけ速度優先にするためです。 | |
107 | 114 | |
108 | 115 | 特定のMIDIチャンネルに対しボイスアサインの優先度を上げることもできます。 |
109 | 116 | 後述の「●チャンネル優先度指定」を参照。 |
@@ -126,27 +133,23 @@ Arduino | ||
126 | 133 | |
127 | 134 | ●MIDIチャンネル操作 |
128 | 135 | |
129 | - PWM_SYNTH のメソッドで、 | |
136 | + PWMDACSynth::getChannel() で | |
130 | 137 | |
131 | 138 | MIDIチャンネル番号(1〜16) ⇔ MIDIチャンネルへのポインタ |
132 | 139 | |
133 | - を相互変換できます。 | |
134 | - | |
135 | - MidiChannel *getChannel(char channel) | |
136 | - char getChannel(MidiChannel *cp) | |
137 | - | |
138 | - MIDIチャンネルへのポインタを介してチャンネル単位に持たせる | |
139 | - パラメータを操作できます。 | |
140 | + を相互変換できます。MIDIチャンネルへのポインタを介して、 | |
141 | + チャンネル単位に持たせるパラメータを操作できます。 | |
142 | + (操作方法については PWMDAC_Synth.h を参照) | |
140 | 143 | |
141 | 144 | なお、ノートオン、ノートオフ、ピッチベンドなど、今出ている音 |
142 | - (アサインされているボイス)に即座に反映させるには、 | |
143 | - MidiChannel ではなく PWMDACSynth のメンバ関数を呼び出す必要があります。 | |
145 | + (アサインされているボイス)に即座に反映させるには、PWMDACSynth のほうで | |
146 | + 用意しているメンバ関数を呼び出し、チャンネルとボイスを同時に更新する必要があります。 | |
144 | 147 | |
145 | 148 | |
146 | 149 | ●MIDI関数 |
147 | 150 | |
148 | - PWM_SYNTH 静的オブジェクトには、MIDIライブラリの MIDI.setHandleXxxx() に | |
149 | - 直接指定できるよう、引数の順序や型を合わせた関数をいくつか用意しています。 | |
151 | + PWMDACSynth で定義されたいくつかの static オブジェクトは、 | |
152 | + MIDIライブラリの MIDI.setHandleXxxx() に直接指定できる形式になっています。 | |
150 | 153 | |
151 | 154 | ただし、接続相手のMIDIデバイスによっては、NOTE OFF の代わりに |
152 | 155 | velocity=0 の NOTE ON を送ってくることがあるので、その場合だけ |
@@ -158,39 +161,29 @@ Arduino | ||
158 | 161 | |
159 | 162 | ●音色変更 |
160 | 163 | |
161 | - 波形とエンベロープパラメータ(ADSR)を MidiChannel クラスの | |
162 | - wavetable と env_param に指定することで、音色を変更できます。 | |
163 | - | |
164 | - ADSRの設定は EnvelopeParam クラスを介して行います。 | |
165 | - コンストラクタで次の値を指定して初期化できます。 | |
166 | - | |
167 | - ・attack_time - アタック時間(大きいほどノートオン直後の立ち上がりがゆっくり) | |
168 | - ・decay_time - ディケイ時間(大きいほどノートオン後の減衰がゆっくり) | |
169 | - ・sustain_level - サスティンレベル(減衰が止まったあと維持する音量) | |
170 | - ・release_time - リリース時間(大きいほどノートオフ後の減衰がゆっくり) | |
171 | - | |
172 | - 値の範囲は sustain_level が 0〜255、それ以外は 0〜15 です。 | |
173 | - 実時間は loop() 内で update() を呼び出す頻度によって変わります。 | |
164 | + 波形テーブルとADSRエンベロープパラメータを、それぞれ MidiChannel クラスの | |
165 | + wavetable と envelope[] に指定することで、音色を変更できます。 | |
174 | 166 | |
175 | - 各ADSRパラメータ値へのポインタは getParam() メソッドで取得できます。 | |
167 | + また、Instrument 構造体定数をプログラムメモリ領域に作り、それを | |
168 | + programChange() に指定するだけで、波形とエンベロープを一度に設定できます。 | |
169 | + MIDIのプログラムチェンジを実装するときに便利です。 | |
176 | 170 | |
171 | + 【波形】 wavetable | |
177 | 172 | 波形テーブルは、要素数256のbyte型PROGMEM配列として生成します。 |
178 | 173 | それを wavetable に指定することで、波形を切り替えることができます。 |
179 | - | |
180 | - 波形テーブル配列の生成は、PWMDAC_Synth.h 上で定義された | |
181 | - マクロを使い、必要な波形テーブルだけを生成してください。 | |
182 | - | |
183 | - このマクロは、同時発音数が増えても信号レベルが255を超えて音割れせず、 | |
184 | - かつ最大音量の出るような計算式になっています。 | |
185 | - これに習って自分で波形を定義すれば、それを指定して | |
186 | - 独自の音色を生み出すことも可能です。 | |
187 | - | |
188 | - プログラムメモリ領域に Instrument 構造体定数を作って、 | |
189 | - それを指定することで音色を変更することもできます。 | |
190 | - MIDIのプログラムチェンジの実装にはこの方法がおすすめです。 | |
191 | - 現在のバージョンでは、プログラムチェンジ対応に伴い、 | |
192 | - エンベロープの初期化に Instrument 構造体定数を使うインターフェースに | |
193 | - してRAMへの展開を最小限に抑えられるようにしました。 | |
174 | + 波形関数のマクロを PWMDAC_CREATE_WAVETABLE() マクロに指定するか、 | |
175 | + またはそれにならって自分で配列を定義することで、任意の波形を生成することが可能です。 | |
176 | + | |
177 | + ※ 波高は(255÷同時発音数:デフォルト6重和音のとき42)を超えないようにしてください。 | |
178 | + 同時発音数いっぱいに鳴らしたときに最大音量255を超えてオーバーフローし、ノイズが | |
179 | + 発生する恐れがあります。 | |
180 | + | |
181 | + 【ADSRエンベロープ】 envelope[] | |
182 | + ADSR_ATTACK_VALUE - アタック時間(0〜15、大きいほどノートオン直後の立ち上がりがゆっくり) | |
183 | + ADSR_DECAY_VALUE - ディケイ時間(0〜15、大きいほどノートオン後の減衰がゆっくり) | |
184 | + ADSR_SUSTAIN_VALUE - サスティンレベル(0〜255、減衰が止まったあと維持する音量) | |
185 | + ADSR_RELEASE_VALUE - リリース時間(0〜15、大きいほどノートオフ後の減衰がゆっくり) | |
186 | + 実時間は loop() 内で update() を呼び出す頻度によって変わります。 | |
194 | 187 | |
195 | 188 | |
196 | 189 | ●チャンネル優先度指定 |
@@ -217,10 +210,6 @@ Arduino | ||
217 | 210 | 多重和音感が失われることがありますので、適宜調整してください。 |
218 | 211 | |
219 | 212 | |
220 | -●その他の関数 | |
221 | - | |
222 | - その他、利用できる関数についてはソースコードのヘッダ PWMDAC_Synth.h を参照。 | |
223 | - | |
224 | 213 | |
225 | 214 | 作者:@きよし - Akiyoshi Kamide |
226 | 215 | http://www.yk.rim.or.jp/~kamide/ |
@@ -6,10 +6,8 @@ | ||
6 | 6 | # Datatypes (KEYWORD1) |
7 | 7 | ####################################### |
8 | 8 | |
9 | -EnvelopeParam KEYWORD1 | |
10 | 9 | Instrument KEYWORD1 |
11 | 10 | MidiChannel KEYWORD1 |
12 | -VoiceStatus KEYWORD1 | |
13 | 11 | PWMDACSynth KEYWORD1 |
14 | 12 | |
15 | 13 | ####################################### |
@@ -24,7 +22,6 @@ pitchBend KEYWORD2 | ||
24 | 22 | controlChange KEYWORD2 |
25 | 23 | systemReset KEYWORD2 |
26 | 24 | getChannel KEYWORD2 |
27 | -setEnvelope KEYWORD2 | |
28 | 25 | setPriority KEYWORD2 |
29 | 26 | |
30 | 27 | ####################################### |
@@ -35,3 +32,4 @@ PWMDAC_NOTE_A_FREQUENCY LITERAL1 | ||
35 | 32 | PWMDAC_POLYPHONY LITERAL1 |
36 | 33 | PWMDAC_OUTPUT_PIN LITERAL1 |
37 | 34 | PWMDAC_CHANNEL_PRIORITY_SUPPORT LITERAL1 |
35 | + |