ATMEGA328を搭載した Arduino Duemilanove 互換機で音をPWM D/A変換出力するシンセサイザーライブラリです。
リビジョン | 6d95498a142fd9774712c337285401f8153e2b71 (tree) |
---|---|
日時 | 2015-10-04 02:39:45 |
作者 | Akiyoshi Kamide <kamide@yk.r...> |
コミッター | Akiyoshi Kamide |
・連打時に同時発音数が落ちたように聞こえる問題の改善と、RAM使用量削減のため、Voiceの優先度を音量を重視した値に変更
・エンベロープパラメータをstructからclassに昇格し、enum定義のADSRパラメータを指定して値の参照や設定ができるようにした
@@ -1,5 +1,5 @@ | ||
1 | 1 | // |
2 | -// PWM DAC Synthesizer ver.20150927 | |
2 | +// PWM DAC Synthesizer ver.20151003 | |
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/ |
@@ -1,5 +1,5 @@ | ||
1 | 1 | // |
2 | -// PWM DAC Synthesizer ver.20150927 | |
2 | +// PWM DAC Synthesizer ver.20151003 | |
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/ |
@@ -51,12 +51,21 @@ | ||
51 | 51 | |
52 | 52 | #define PWMDAC_CREATE_WAVETABLE(table, function) PROGMEM const byte table[] = ARRAY256(function) |
53 | 53 | |
54 | -typedef struct _EnvelopeParam { | |
55 | - byte attack_time; // 0..15 | |
56 | - byte decay_time; // 0..15 | |
57 | - byte sustain_level; // 0..255 | |
58 | - byte release_time; // 0..15 | |
59 | -} EnvelopeParam; | |
54 | +enum AdsrStatus : byte {ADSR_OFF, ADSR_RELEASE, ADSR_SUSTAIN, ADSR_DECAY, ADSR_ATTACK}; | |
55 | + | |
56 | +class EnvelopeParam { | |
57 | + public: | |
58 | + EnvelopeParam() { } | |
59 | + EnvelopeParam(byte attack_time, byte decay_time, byte sustain_level, byte release_time) { | |
60 | + *getParam(ADSR_ATTACK) = attack_time; | |
61 | + *getParam(ADSR_DECAY) = decay_time; | |
62 | + *getParam(ADSR_SUSTAIN) = sustain_level; | |
63 | + *getParam(ADSR_RELEASE) = release_time; | |
64 | + } | |
65 | + byte *getParam(AdsrStatus adsr) { return param + (byte)adsr - 1; } | |
66 | + protected: | |
67 | + byte param[4]; | |
68 | +}; | |
60 | 69 | |
61 | 70 | class MidiChannel { |
62 | 71 | protected: |
@@ -128,17 +137,17 @@ class MidiChannel { | ||
128 | 137 | |
129 | 138 | class VoiceStatus { |
130 | 139 | public: |
131 | - enum AdsrStatus : byte {ADSR_OFF, ADSR_RELEASE, ADSR_SUSTAIN, ADSR_DECAY, ADSR_ATTACK}; | |
132 | 140 | AdsrStatus getAdsrStatus() const { return adsr; } |
133 | - boolean isOn() { return adsr > ADSR_OFF; } | |
141 | + boolean isSoundOn() { return adsr > ADSR_OFF; } | |
134 | 142 | boolean isNoteOn(byte note) { return this->note == note && adsr > ADSR_RELEASE; } |
135 | 143 | MidiChannel *getChannel() const { return channel; } |
136 | 144 | byte getNote() const { return note; } |
137 | 145 | unsigned int getVolume16() const { return volume.v16; } |
138 | 146 | inline byte getVolume8() const { return volume.v8[sizeof(volume.v8) - 1]; } |
139 | 147 | inline unsigned int nextPulseWidth() { return getVolume8() * nextWavePoint(); } |
140 | - unsigned long getTemperature() { | |
141 | - return (adsr << 16) | (adsr == ADSR_ATTACK ? UINT_MAX - volume.v16 : volume.v16); | |
148 | + unsigned int getTemperature() { | |
149 | + unsigned int t = volume.v16 >> 1; | |
150 | + return adsr == ADSR_ATTACK ? UINT_MAX - t : t; | |
142 | 151 | } |
143 | 152 | VoiceStatus() { reset(); } |
144 | 153 | void setChannel(MidiChannel *cp) { if( this->channel != cp ) reset(cp); } |
@@ -194,21 +203,21 @@ class VoiceStatus { | ||
194 | 203 | switch(adsr) { |
195 | 204 | case ADSR_ATTACK: { |
196 | 205 | unsigned long v32 = volume.v16; |
197 | - if( (v32 += (UINT_MAX >> channel->env_param.attack_time)) > UINT_MAX ) { | |
206 | + if( ( v32 += (UINT_MAX >> *(channel->env_param.getParam(ADSR_ATTACK))) ) > UINT_MAX ) { | |
198 | 207 | volume.v16 = UINT_MAX; adsr = ADSR_DECAY; break; |
199 | 208 | } |
200 | 209 | volume.v16 = v32; break; |
201 | 210 | } |
202 | 211 | case ADSR_DECAY: { |
203 | - unsigned int dv = volume.v16 >> channel->env_param.decay_time; | |
212 | + unsigned int dv = volume.v16 >> *(channel->env_param.getParam(ADSR_DECAY)); | |
204 | 213 | if( dv == 0 ) dv = 1; |
205 | 214 | long v32 = volume.v16; |
206 | - unsigned int s = (unsigned int)channel->env_param.sustain_level << 8; | |
215 | + unsigned int s = (unsigned int)(*(channel->env_param.getParam(ADSR_SUSTAIN))) << 8; | |
207 | 216 | if( (v32 -= dv) <= s ) { volume.v16 = s; adsr = ADSR_SUSTAIN; break; } |
208 | 217 | volume.v16 = v32; break; |
209 | 218 | } |
210 | 219 | case ADSR_RELEASE: { |
211 | - unsigned int dv = volume.v16 >> channel->env_param.release_time; | |
220 | + unsigned int dv = volume.v16 >> *(channel->env_param.getParam(ADSR_RELEASE)); | |
212 | 221 | if( dv == 0 ) dv = 1; |
213 | 222 | long v32 = volume.v16; |
214 | 223 | if( (v32 -= dv) < 0x100 ) { reset(); break; } |
@@ -305,7 +314,7 @@ class PWMDACSynth { | ||
305 | 314 | EACH_VOICE(v) v->update(modulation_offset); |
306 | 315 | } |
307 | 316 | #define ALL_CHANNEL for( int i=0; i<NumberOf(channels); i++ ) channels[i] |
308 | - static void setEnvelope(struct _EnvelopeParam ep) { ALL_CHANNEL.env_param = ep; } | |
317 | + static void setEnvelope(EnvelopeParam ep) { ALL_CHANNEL.env_param = ep; } | |
309 | 318 | static void setWave(PROGMEM byte *wt) { ALL_CHANNEL.wavetable = wt; } |
310 | 319 | #undef ALL_CHANNEL |
311 | 320 | static void noteOff(byte channel, byte pitch, byte velocity) { |
@@ -319,7 +328,7 @@ class PWMDACSynth { | ||
319 | 328 | static void pitchBend(byte channel, int bend) { |
320 | 329 | MidiChannel *cp = getChannel(channel); |
321 | 330 | if ( ! cp->pitchBendChange(bend) ) return; |
322 | - EACH_VOICE(v) if( v->isOn() && v->getChannel() == cp ) v->updatePitch(); | |
331 | + EACH_VOICE(v) if( v->isSoundOn() && v->getChannel() == cp ) v->updatePitch(); | |
323 | 332 | } |
324 | 333 | static void controlChange(byte channel, byte number, byte value) { |
325 | 334 | getChannel(channel)->controlChange(number, value); |
@@ -345,10 +354,10 @@ class PWMDACSynth { | ||
345 | 354 | static VoiceStatus *selectVoiceToAttack(MidiChannel *channel, byte note) { |
346 | 355 | EACH_VOICE(v) if( v->isNoteOn(note) && v->getChannel() == channel ) return v; |
347 | 356 | struct { |
348 | - unsigned long temperature = ULONG_MAX; | |
357 | + unsigned int temperature = UINT_MAX; | |
349 | 358 | VoiceStatus *voice = NULL; |
350 | 359 | } coldest; |
351 | - unsigned long temperature; | |
360 | + unsigned int temperature; | |
352 | 361 | EACH_VOICE(v) { |
353 | 362 | if( (temperature = v->getTemperature()) > coldest.temperature ) continue; |
354 | 363 | coldest.temperature = temperature; |
@@ -1,7 +1,7 @@ | ||
1 | 1 | |
2 | 2 | [PWMDAC_Synth - PWM DAC synthesizer library for Arduino] |
3 | 3 | |
4 | -ver.20150927 | |
4 | +ver.20151003 | |
5 | 5 | |
6 | 6 | Arduinoで動作する簡易シンセサイザライブラリです。 |
7 | 7 |
@@ -151,7 +151,8 @@ PWMDACSynth::update() | ||
151 | 151 | 波形とエンベロープパラメータ(ADSR)を MidiChannel クラスの |
152 | 152 | wavetable と env_param に指定することで、音色を変更できます。 |
153 | 153 | |
154 | - ADSRの設定は EnvelopeParam 構造体を介して行います。 | |
154 | + ADSRの設定は EnvelopeParam クラスを介して行います。 | |
155 | + コンストラクタで次の値を指定して初期化できます。 | |
155 | 156 | |
156 | 157 | ・attack_time - アタック時間(大きいほどノートオン直後の立ち上がりがゆっくり) |
157 | 158 | ・decay_time - ディケイ時間(大きいほどノートオン後の減衰がゆっくり) |
@@ -163,6 +164,8 @@ PWMDACSynth::update() | ||
163 | 164 | |
164 | 165 | サスティンレベルは 0〜255 の範囲です。 |
165 | 166 | |
167 | + 各ADSRパラメータ値へのポインタは getParam() メソッドで取得できます。 | |
168 | + | |
166 | 169 | 波形テーブルは、要素数256のbyte型PROGMEM配列として生成します。 |
167 | 170 | それを wavetable に指定することで、波形を切り替えることができます。 |
168 | 171 |