• R/O
  • HTTP
  • SSH
  • HTTPS

コミット

よく使われているワード(クリックで追加)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

ATMEGA328を搭載した Arduino Duemilanove 互換機で音をPWM D/A変換出力するシンセサイザーライブラリです。


コミットメタ情報

リビジョン870b0852c22fced83c388aa4e6ad99315ddd2dcc (tree)
日時2020-04-06 05:18:26
作者Akiyoshi Kamide <kamide@yk.r...>
コミッターAkiyoshi Kamide

ログメッセージ

Supported the compilation on Arduino IDE on Linux

- Refectored, and "const" added
- Update against performance-down when compiled on Linux

変更サマリ

差分

--- a/PWMDAC_Synth.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
1-//
2-// PWM DAC Synthesizer ver.20151003
3-// by Akiyoshi Kamide (Twitter: @akiyoshi_kamide)
4-// http://kamide.b.osdn.me/pwmdac_synth_lib/
5-// https://osdn.jp/users/kamide/pf/PWMDAC_Synth/
6-//
7-#include "PWMDAC_Synth.h"
8-
9-byte PWMDACSynth::musicalMod7(char x) {
10- while( x & 0xF8 ) x = (x >> 3) + (x & 7);
11- if(x==7) return 0;
12- return x;
13-}
14-byte PWMDACSynth::musicalMod12(char x) {
15- char n = x >> 2;
16- while( n & 0xFC ) n = (n >> 2) + (n & 3);
17- x &= 3;
18- if(n==3||n==0) return x;
19- return x + (n << 2);
20-}
21-byte PWMDACSynth::log2(unsigned int x) {
22- byte index = 0;
23- if (x & 0xFF00) { index += 8; x &= 0xFF00; }
24- if (x & 0xF0F0) { index += 4; x &= 0xF0F0; }
25- if (x & 0xCCCC) { index += 2; x &= 0xCCCC; }
26- if (x & 0xAAAA) { index += 1; x &= 0xAAAA; }
27- return index;
28-}
29-
--- a/PWMDAC_Synth.h
+++ b/PWMDAC_Synth.h
@@ -1,5 +1,5 @@
11 //
2-// PWM DAC Synthesizer ver.20190330
2+// PWM DAC Synthesizer ver.20200405
33 // by Akiyoshi Kamide (Twitter: @akiyoshi_kamide)
44 // http://kamide.b.osdn.me/pwmdac_synth_lib/
55 // https://osdn.jp/users/kamide/pf/PWMDAC_Synth/
@@ -36,60 +36,43 @@
3636 #define PWMDAC_POLYPHONY 6
3737 #endif
3838
39-
4039 // Built-in wavetable generator
4140 // x = Phase angle : 0x00...0x80(PI_radian)...0xFF
4241 // f(x) = Wave voltage at the x : 0x00(min)...0x80(center)...0xFF(max)
4342 #define PWMDAC_SQUARE_WAVE(x) (((x) < 0x80 ? 0x00 : 0xFF) / PWMDAC_POLYPHONY)
4443 #define PWMDAC_SAWTOOTH_WAVE(x) ((x) / PWMDAC_POLYPHONY)
4544 #define PWMDAC_TRIANGLE_WAVE(x) (((x) < 0x80 ? (x) : 0x100 - (x)) * 2 / PWMDAC_POLYPHONY)
46-
47-#define SINPI(x, t) sin(PI * (x) / (t))
48-#define PWMDAC_MAX_VOLUME_SINE_WAVE(x) ( 0x80 * ( 1 + SINPI(x,0x80) ) )
49-#define PWMDAC_SINE_WAVE(x) (PWMDAC_MAX_VOLUME_SINE_WAVE(x) / PWMDAC_POLYPHONY)
50-#define PWMDAC_SHEPARD_TONE(x) ( 0x80 * ( 8 \
51- +SINPI(x,0x80) \
52- +SINPI(x,0x40) \
53- +SINPI(x,0x20) \
54- +SINPI(x,0x10) \
55- +SINPI(x,0x08) \
56- +SINPI(x,0x04) \
57- +SINPI(x,0x02) \
58- +SINPI(x,0x01) \
59- ) / ( 8 * PWMDAC_POLYPHONY ) )
45+#define PWMDAC_MAX_VOLUME_SINE_WAVE(x) ((byte)( 0x80 * (1 + sin(PI * (x) / 0x80)) ))
46+#define PWMDAC_SINE_WAVE(x) ((byte)( 0x80 * (1 + sin(PI * (x) / 0x80)) / PWMDAC_POLYPHONY ))
47+#define PWMDAC_SHEPARD_TONE(x) ((byte)( 0x80 * (8 \
48+ +sin(PI * (x) / 0x80) \
49+ +sin(PI * (x) / 0x40) \
50+ +sin(PI * (x) / 0x20) \
51+ +sin(PI * (x) / 0x10) \
52+ +sin(PI * (x) / 0x08) \
53+ +sin(PI * (x) / 0x04) \
54+ +sin(PI * (x) / 0x02) \
55+ +sin(PI * (x) / 0x01) \
56+ ) / (8 * PWMDAC_POLYPHONY) ))
6057
6158 #define PWMDAC_CREATE_WAVETABLE(table, function) PROGMEM const byte table[] = ARRAY256(function)
6259
63-enum AdsrStatus : byte {ADSR_OFF, ADSR_RELEASE, ADSR_SUSTAIN, ADSR_DECAY, ADSR_ATTACK};
60+enum AdsrParam : byte {
61+ ADSR_RELEASE_VALUE, // 0..15
62+ ADSR_SUSTAIN_VALUE, // 0..255
63+ ADSR_DECAY_VALUE, // 0..15
64+ ADSR_ATTACK_VALUE, // 0..15
65+ NUMBER_OF_ADSR
66+};
6467
6568 typedef struct _Instrument {
66- PROGMEM const byte *wavetable;
67- PROGMEM const byte envelope[ADSR_ATTACK];
69+ const byte *wavetable;
70+ const byte envelope[NUMBER_OF_ADSR];
6871 } Instrument;
6972
70-class EnvelopeParam {
71- public:
72- EnvelopeParam() { }
73- EnvelopeParam(PROGMEM const byte *envelope) {
74- setParam(envelope);
75- }
76- EnvelopeParam(byte attack_time, byte decay_time, byte sustain_level, byte release_time) {
77- *getParam(ADSR_ATTACK) = attack_time; // 0..15
78- *getParam(ADSR_DECAY) = decay_time; // 0..15
79- *getParam(ADSR_SUSTAIN) = sustain_level; // 0..255
80- *getParam(ADSR_RELEASE) = release_time; // 0..15
81- }
82- byte *getParam(AdsrStatus adsr) {
83- return param + (byte)adsr - 1;
84- }
85- void setParam(PROGMEM const byte *envelope) {
86- memcpy_P(this->param, envelope, sizeof(this->param));
87- }
88- protected:
89- byte param[ADSR_ATTACK];
90-};
91-
9273 class MidiChannel {
74+ public:
75+ static const byte MAX_NUMBER = 16;
9376 protected:
9477 // RPN (Registered Parameter Number) or NRPN(Non-Registered Parameter Number)
9578 typedef struct { byte LSB; byte MSB; } ParameterNumber;
@@ -113,28 +96,30 @@ class MidiChannel {
11396 byte priority_volume_threshold;
11497 #endif
11598 byte modulation; // 0 .. 127 (Unsigned 7bit)
116- PROGMEM const byte *wavetable;
117- EnvelopeParam envelope;
118- MidiChannel(PROGMEM const Instrument *instrument) {
99+ const byte *wavetable;
100+ byte envelope[NUMBER_OF_ADSR];
101+ MidiChannel(const Instrument * const instrument) {
119102 #if defined(PWMDAC_CHANNEL_PRIORITY_SUPPORT)
120103 setPriority(0);
121104 #endif
122105 reset(instrument);
123106 }
124- void reset(PROGMEM const Instrument *instrument) {
107+ void reset(const Instrument * const instrument) {
125108 resetAllControllers();
126109 rpn.LSB = rpn.MSB = RPN_Null;
127110 data_entry_source = NULL;
128111 programChange(instrument);
129112 }
130113 #if defined(PWMDAC_CHANNEL_PRIORITY_SUPPORT)
131- void setPriority(byte priority) { this->priority_volume_threshold = ~priority; }
114+ void setPriority(const byte priority) {
115+ this->priority_volume_threshold = ~priority;
116+ }
132117 #endif
133118 int getPitchBend() const { return pitch_bend; }
134- unsigned long bendedPitchOf(unsigned long original_pitch) {
119+ unsigned long bendedPitchOf(const unsigned long original_pitch) const {
135120 return pitch_bend ? original_pitch * pitch_rate : original_pitch;
136121 }
137- boolean pitchBendChange(int new_pitch_bend) {
122+ boolean pitchBendChange(const int new_pitch_bend) {
138123 int diff = new_pitch_bend - pitch_bend;
139124 if( diff < 16 && diff > -16 ) return false;
140125 pitch_bend = new_pitch_bend;
@@ -142,15 +127,15 @@ class MidiChannel {
142127 return true;
143128 }
144129 byte getPitchBendSensitivity() const { return pitch_bend_sensitivity; }
145- boolean pitchBendSensitivityChange(byte value) {
130+ boolean pitchBendSensitivityChange(const byte value) {
146131 if ( pitch_bend_sensitivity == value ) return false;
147132 pitch_bend_sensitivity = value;
148133 updatePitchRate();
149134 return true;
150135 }
151- void programChange(PROGMEM const Instrument *instrument) {
152- this->wavetable = (PROGMEM const byte *)pgm_read_word(&(instrument->wavetable));
153- this->envelope.setParam(instrument->envelope);
136+ void programChange(const Instrument * const instrument) {
137+ this->wavetable = (const byte *)pgm_read_word(&(instrument->wavetable));
138+ memcpy_P(this->envelope, instrument->envelope, sizeof(this->envelope));
154139 }
155140 void resetAllControllers() {
156141 modulation = 0;
@@ -158,7 +143,7 @@ class MidiChannel {
158143 pitch_rate = 1.0;
159144 pitch_bend_sensitivity = 2;
160145 }
161- void controlChange(byte number, byte value) {
146+ void controlChange(const byte number, const byte value) {
162147 switch(number) {
163148 case 1: modulation = value; break;
164149 case 6: // RPN/NRPN Data Entry
@@ -179,136 +164,6 @@ class MidiChannel {
179164 }
180165 };
181166
182-// [Phase-correct PWM dual-slope]
183-// TCNTn =
184-// 00(BOTTOM) 01 02 03 ... FC FD FE
185-// FF(TOP) FE FD FC ... 03 02 01
186-// -> 0xFF * 2 = 510 values (NOT 512)
187-//
188-// ISR()-call interval = (0xFF * 2) / F_CPU(16MHz) = 31.875us
189-//
190-// [MIDI Tuning Standard]
191-// http://en.wikipedia.org/wiki/MIDI_Tuning_Standard
192-// fn(d) = 440 Hz * 2^( (d - 69) / 12 ) MIDI note # d = 0..127
193-//
194-#define PHASE_SPEED_OF(note_number) ( \
195- pow( 2, (double)(note_number - 69)/12 + (sizeof(unsigned long) * 8 + 1) ) \
196- * PWMDAC_NOTE_A_FREQUENCY * 0xFF / F_CPU )
197-
198-class VoiceStatus {
199- public:
200- boolean isNoteOn(byte note, MidiChannel *cp) {
201- return this->note == note && adsr > ADSR_RELEASE && channel == cp;
202- }
203- boolean isSoundOn(MidiChannel *cp) {
204- return adsr > ADSR_OFF && channel == cp;
205- }
206- inline unsigned int nextPulseWidth() {
207- // To generate waveform rapidly in ISR(), this method must be inline
208- phase.v32 += dphase32.real;
209- return HighestElementOf(volume.v8) * pgm_read_byte( wavetable + HighestElementOf(phase.v8) );
210- }
211- static const byte HIGHEST_PRIORITY = UCHAR_MAX;
212- static const byte LOWEST_PRIORITY = 0;
213- byte getPriority() {
214- byte t = HighestElementOf(volume.v8) >> 1;
215- if( adsr == ADSR_ATTACK ) t = HIGHEST_PRIORITY - t;
216-#if defined(PWMDAC_CHANNEL_PRIORITY_SUPPORT)
217- if( channel != NULL && t > channel->priority_volume_threshold ) {
218- t >>= 1; t |= 0x80;
219- } else {
220- t >>= 1;
221- }
222-#endif
223- return t;
224- }
225- VoiceStatus() { reset(); }
226- void setChannel(MidiChannel *cp) { if( channel != cp ) reset(cp); }
227- void noteOn(byte note) {
228- this->wavetable = channel->wavetable;
229- this->note = note;
230- updatePitch();
231- adsr = ADSR_ATTACK;
232- }
233- void noteOff(byte note, MidiChannel *cp) {
234- if( isNoteOn(note, cp) ) adsr = ADSR_RELEASE;
235- }
236- void allNotesOff(MidiChannel *cp) {
237- if( channel == cp ) adsr = ADSR_RELEASE;
238- }
239- void allSoundOff(MidiChannel *cp) { if( isSoundOn(cp) ) reset(); }
240- void reset(MidiChannel *cp = NULL) {
241- adsr = ADSR_OFF; volume.v16 = 0; note = UCHAR_MAX;
242- phase.v32 = dphase32.real = dphase32.moffset = dphase32.bended = 0L;
243- channel = cp;
244- }
245- void update(int modulation_offset) {
246- updateModulationStatus(modulation_offset);
247- updateEnvelopeStatus();
248- }
249- void updatePitch(MidiChannel *cp) { if( isSoundOn(cp) ) updatePitch(); }
250-protected:
251- PROGMEM const byte *wavetable;
252- struct {
253- unsigned long real; // Real phase speed
254- unsigned long bended; // Pitch-bended phase speed
255- long moffset; // Modulation pitch offset
256- } dphase32;
257- union { unsigned long v32; byte v8[4]; } phase;
258- union { unsigned int v16; byte v8[2]; } volume;
259- MidiChannel *channel;
260- byte note; // 0..127
261- AdsrStatus adsr;
262- void updatePitch() {
263- static PROGMEM const unsigned long phase_speed_table[] = ARRAY128(PHASE_SPEED_OF);
264- dphase32.bended = channel->bendedPitchOf(pgm_read_dword(phase_speed_table + note));
265- dphase32.real = dphase32.bended + dphase32.moffset;
266- }
267- void updateModulationStatus(char modulation_offset) {
268- if( channel->modulation <= 0x10 ) {
269- if( dphase32.moffset == 0 ) return;
270- dphase32.moffset = 0;
271- dphase32.real = dphase32.bended;
272- return;
273- }
274- dphase32.moffset = (dphase32.real >> 19) * channel->modulation * modulation_offset;
275- dphase32.real = dphase32.bended + dphase32.moffset;
276- }
277- EnvelopeParam *getEnvelope() { return &(channel->envelope); }
278- byte *getEnvelope(AdsrStatus adsr) { return getEnvelope()->getParam(adsr); }
279- void updateEnvelopeStatus() {
280- switch(adsr) {
281- case ADSR_ATTACK: {
282- unsigned long v = volume.v16;
283- if( (v += (UINT_MAX >> *getEnvelope(ADSR_ATTACK))) > UINT_MAX ) {
284- volume.v16 = UINT_MAX; adsr = ADSR_DECAY; break;
285- }
286- volume.v16 = v; break;
287- }
288- case ADSR_DECAY: {
289- EnvelopeParam *e = getEnvelope();
290- unsigned int dv = volume.v16 >> *(e->getParam(ADSR_DECAY));
291- if( dv == 0 ) dv = 1;
292- long v = volume.v16;
293- unsigned int sustain16 = (unsigned int)(*(e->getParam(ADSR_SUSTAIN))) << 8;
294- if( (v -= dv) <= sustain16 ) {
295- volume.v16 = sustain16; adsr = ADSR_SUSTAIN; break;
296- }
297- volume.v16 = v; break;
298- }
299- case ADSR_RELEASE: {
300- unsigned int dv = volume.v16 >> *getEnvelope(ADSR_RELEASE);
301- if( dv == 0 ) dv = 1;
302- long v = volume.v16;
303- if( (v -= dv) < 0x100 ) {
304- reset(); break;
305- }
306- volume.v16 = v; break;
307- }
308- }
309- }
310-};
311-
312167 #if PWMDAC_OUTPUT_PIN == 6 || PWMDAC_OUTPUT_PIN == 5
313168 // In Arduino, TIMER0 has been reserved by wiring.c in Arduino core,
314169 // so defining PWMDAC_OUTPUT_PIN = 5 or 6 causes compile error
@@ -338,9 +193,170 @@ protected:
338193 #endif
339194 #endif
340195
196+// [Phase-correct PWM dual-slope]
197+// TCNTn value changes to:
198+// 00(BOTTOM) 01 02 03 ... FC FD FE
199+// FF(TOP) FE FD FC ... 03 02 01
200+// -> # of values = 0xFF * 2 = 510 (NOT 512)
201+//
202+// ISR() call interval = (# of values) / F_CPU(16MHz) = 31.875us
203+//
204+// [MIDI Tuning Standard]
205+// http://en.wikipedia.org/wiki/MIDI_Tuning_Standard
206+// fn(d) = 440 Hz * 2^( (d - 69) / 12 ) MIDI note # d = 0..127
207+//
208+#define PHASE_SPEED_OF(note_number) (static_cast<unsigned long>( \
209+ pow( 2, static_cast<double>(note_number - 69)/12 + (sizeof(unsigned long) * 8 + 1) ) \
210+ * PWMDAC_NOTE_A_FREQUENCY * 0xFF / F_CPU ))
211+
341212 class PWMDACSynth {
213+ protected:
214+ static PROGMEM const byte maxVolumeSineWavetable[];
215+ static MidiChannel channels[MidiChannel::MAX_NUMBER];
216+ static PROGMEM const Instrument * const defaultInstrument;
217+ class Voice {
218+ protected:
219+ enum AdsrStatus : byte {
220+ ADSR_OFF,
221+ ADSR_RELEASE,
222+ ADSR_SUSTAIN,
223+ ADSR_DECAY,
224+ ADSR_ATTACK
225+ };
226+ union { unsigned long v32; byte v8[4]; } phase;
227+ unsigned long dphase32_real; // Real phase speed
228+ unsigned long dphase32_bended; // Pitch-bended phase speed
229+ unsigned long dphase32_original; // Original phase speed
230+ long dphase32_moffset; // Modulation pitch offset
231+ union { unsigned int v16; byte v8[2]; } volume;
232+ unsigned int dv; // Diff of volume
233+ unsigned int sustain; // Volume when ADSR Sustain
234+ byte note; // 0..127
235+ AdsrStatus adsr_status;
236+ const byte *wavetable;
237+ const MidiChannel *channel;
238+ const byte *envelope;
239+ const byte *modulation; // 0 .. 127 (Unsigned 7bit)
240+ public:
241+ static const byte HIGHEST_PRIORITY = UCHAR_MAX;
242+ static const byte LOWEST_PRIORITY = 0;
243+ Voice() { allSoundOff(); }
244+ byte getPriority() const {
245+ byte t = HighestElementOf(volume.v8) >> 1;
246+ if( adsr_status == ADSR_ATTACK ) t = HIGHEST_PRIORITY - t;
247+#if defined(PWMDAC_CHANNEL_PRIORITY_SUPPORT)
248+ if( channel != nullptr && t > channel->priority_volume_threshold ) {
249+ t >>= 1; t |= 0x80;
250+ } else {
251+ t >>= 1;
252+ }
253+#endif
254+ return t;
255+ }
256+ void noteOn(const byte note, const MidiChannel * const new_channel = nullptr) {
257+ if( new_channel != nullptr && this->channel != new_channel ) {
258+ volume.v16 = 0;
259+ this->channel = new_channel;
260+ }
261+ wavetable = this->channel->wavetable;
262+ envelope = this->channel->envelope;
263+ modulation = &(this->channel->modulation);
264+ static PROGMEM const unsigned long phase_speed_table[] = ARRAY128(PHASE_SPEED_OF);
265+ dphase32_original = pgm_read_dword(phase_speed_table + (this->note = note));
266+ dphase32_bended = this->channel->bendedPitchOf(dphase32_original);
267+ dphase32_real = dphase32_bended + dphase32_moffset;
268+ adsr_status = ADSR_ATTACK;
269+ dv = UINT_MAX >> envelope[ADSR_ATTACK_VALUE];
270+ }
271+ boolean reAttackIfAssigned(const byte note, const MidiChannel * const cp) {
272+ if( adsr_status > ADSR_RELEASE && this->note == note && channel == cp ) {
273+ adsr_status = ADSR_ATTACK;
274+ dv = UINT_MAX >> envelope[ADSR_ATTACK_VALUE];
275+ return true;
276+ }
277+ return false;
278+ }
279+ void noteOff(const byte note, const MidiChannel * const cp) {
280+ if( adsr_status > ADSR_RELEASE && this->note == note && channel == cp )
281+ adsr_status = ADSR_RELEASE;
282+ }
283+ void allNotesOff(const MidiChannel * const cp) {
284+ if( adsr_status > ADSR_RELEASE && channel == cp ) adsr_status = ADSR_RELEASE;
285+ }
286+ void allSoundOff(const MidiChannel * const cp) {
287+ if( adsr_status > ADSR_OFF && channel == cp ) allSoundOff();
288+ }
289+ void allSoundOff() {
290+ volume.v16 = 0;
291+ adsr_status = ADSR_OFF;
292+ note = UCHAR_MAX;
293+ phase.v32 = dphase32_real = dphase32_moffset = dphase32_bended = dphase32_original = 0L;
294+ channel = nullptr;
295+ }
296+ unsigned int nextPulseWidth() {
297+ phase.v32 += dphase32_real;
298+ return HighestElementOf(volume.v8) *
299+ pgm_read_byte(wavetable + HighestElementOf(phase.v8));
300+ }
301+ void update(const byte modulation_offset) {
302+ // Update volume by ADSR envelope
303+ switch(adsr_status) {
304+ case ADSR_ATTACK:
305+ if( volume.v16 < UINT_MAX - dv ) volume.v16 += dv;
306+ else {
307+ volume.v16 = UINT_MAX;
308+ adsr_status = ADSR_DECAY;
309+ sustain = static_cast<unsigned int>(envelope[ADSR_SUSTAIN_VALUE]) << 8;
310+ }
311+ break;
312+ case ADSR_DECAY:
313+ dv = volume.v16 >> envelope[ADSR_DECAY_VALUE];
314+ if( dv == 0 ) dv = 1;
315+ if( volume.v16 > sustain + dv ) volume.v16 -= dv;
316+ else { volume.v16 = sustain; adsr_status = ADSR_SUSTAIN; }
317+ break;
318+ case ADSR_SUSTAIN: break;
319+ case ADSR_RELEASE:
320+ dv = volume.v16 >> envelope[ADSR_RELEASE_VALUE];
321+ if( dv == 0 ) dv = 1;
322+ if( volume.v16 > 0x100 + dv ) volume.v16 -= dv;
323+ else allSoundOff();
324+ break;
325+ case ADSR_OFF: break;
326+ }
327+ // Update frequency by modulation
328+ if( *modulation <= 0x10 ) {
329+ // When Moduletion OFF
330+ if( dphase32_moffset == 0 ) return;
331+ dphase32_moffset = 0;
332+ dphase32_real = dphase32_bended;
333+ return;
334+ }
335+ dphase32_moffset = (dphase32_real >> 19) * (*modulation) * modulation_offset;
336+ dphase32_real = dphase32_bended + dphase32_moffset;
337+ }
338+ void updatePitch(const MidiChannel * const cp) {
339+ // Must be called at pitch-bend change on the channel
340+ if( adsr_status > ADSR_OFF && channel == cp ) {
341+ dphase32_bended = channel->bendedPitchOf(dphase32_original);
342+ dphase32_real = dphase32_bended + dphase32_moffset;
343+ }
344+ }
345+ };
346+ static Voice voices[PWMDAC_POLYPHONY];
342347 public:
343- static void setup() { // must be called from setup() once
348+ static void __updatePulseWidth() { // Only for ISR()
349+ union { unsigned int v16; byte v8[2]; } pw = {0};
350+ for( byte i = 0; i < NumberOf(voices); ++i ) pw.v16 += voices[i].nextPulseWidth();
351+ PWMDAC_OCR = HighestElementOf(pw.v8);
352+ }
353+ static void update() { // must be called from your loop() repeatedly
354+ static byte modulation_phase = 0;
355+ const byte *addr = maxVolumeSineWavetable + modulation_phase++;
356+ const byte modulation_offset = pgm_read_byte(addr) - 0x7F;
357+ for( byte i = 0; i < NumberOf(voices); ++i ) voices[i].update(modulation_offset);
358+ }
359+ static void setup() { // must be called from your setup() once
344360 pinMode(PWMDAC_OUTPUT_PIN,OUTPUT);
345361 #ifdef PWMDAC_USE_TIMER1
346362 // No prescaling
@@ -384,86 +400,80 @@ class PWMDACSynth {
384400 sbi(TIMSK2,TOIE2); // Enable interrupt
385401 #endif
386402 }
387-#define EACH_VOICE(p) for(VoiceStatus *(p)=voices; (p)<= voices + (PWMDAC_POLYPHONY - 1); (p)++)
388-#define EACH_CHANNEL(c) for(MidiChannel *(c)=channels; (c)<= &HighestElementOf(channels); (c)++)
389- inline static void updatePulseWidth() {
390- // To generate waveform rapidly in ISR(), this method must be inline
391- unsigned int pw = 0;
392- EACH_VOICE(v) pw += v->nextPulseWidth();
393- PWMDAC_OCR = pw >> 8;
403+ static MidiChannel *getChannel(const char channel) {
404+ return channels + (channel - 1);
394405 }
395- static void update() { // must be called from loop() repeatedly
396- static byte modulation_phase = 0;
397- int modulation_offset = pgm_read_byte(maxVolumeSineWavetable + (++modulation_phase)) - 0x7F;
398- EACH_VOICE(v) v->update(modulation_offset);
406+ static char getChannel(const MidiChannel *cp) {
407+ return (cp - channels) + 1;
399408 }
400- static void noteOff(byte channel, byte pitch, byte velocity) {
409+ static void noteOff(const byte channel, const byte pitch, const byte velocity) {
410+ (void)velocity;
401411 MidiChannel *cp = getChannel(channel);
402- EACH_VOICE(v) v->noteOff(pitch, cp);
403- }
404- static void noteOn(byte channel, byte pitch, byte velocity) {
405- assignVoice(getChannel(channel),pitch)->noteOn(pitch);
412+ for( byte i = 0; i < NumberOf(voices); ++i ) voices[i].noteOff(pitch, cp);
413+ }
414+ static void noteOn(const byte channel, const byte pitch, const byte velocity) {
415+ (void)velocity;
416+ const MidiChannel * const cp = getChannel(channel);
417+ for( byte i = 0; i < NumberOf(voices); ++i )
418+ if( voices[i].reAttackIfAssigned(pitch, cp) ) return;
419+ // Search voice to assign
420+ byte lowest_priority = Voice::HIGHEST_PRIORITY;
421+ byte lowest_priority_index = 0;
422+ for( byte i = 0; i < NumberOf(voices); ++i ) {
423+ const byte priority = voices[i].getPriority();
424+ if( priority > lowest_priority ) continue;
425+ lowest_priority = priority;
426+ lowest_priority_index = i;
427+ }
428+ voices[lowest_priority_index].noteOn(pitch, cp);
406429 }
407- static void pitchBend(byte channel, int bend) {
430+ static void pitchBend(const byte channel, const int bend) {
408431 MidiChannel *cp = getChannel(channel);
409432 if ( ! cp->pitchBendChange(bend) ) return;
410- EACH_VOICE(v) v->updatePitch(cp);
433+ for( byte i = 0; i < NumberOf(voices); ++i ) voices[i].updatePitch(cp);
411434 }
412- static void controlChange(byte channel, byte number, byte value) {
435+ static void controlChange(const byte channel, const byte number, const byte value) {
413436 MidiChannel *cp = getChannel(channel);
414437 cp->controlChange(number, value);
415438 switch(number) {
416439 case 120: // All sound off
417- EACH_VOICE(v) v->allSoundOff(cp);
440+ for( byte i = 0; i < NumberOf(voices); ++i ) voices[i].allSoundOff(cp);
418441 break;
419442 case 123: // All notes off
420- EACH_VOICE(v) v->allNotesOff(cp);
443+ for( byte i = 0; i < NumberOf(voices); ++i ) voices[i].allNotesOff(cp);
421444 break;
422445 }
423446 }
424447 static void systemReset() {
425- EACH_VOICE(v) v->reset();
426- EACH_CHANNEL(c) c->reset(defaultInstrument);
427- }
428- static MidiChannel *getChannel(char channel) { return channels + (channel - 1); }
429- static char getChannel(MidiChannel *cp) { return (cp - channels) + 1; }
430- static byte musicalMod12(char);
431- static byte musicalMod7(char);
432- static byte log2(unsigned int);
433- static int musicalConstrain12(int note, int min_note, int max_note) {
434- if( max_note < note ) {
435- note = max_note - musicalMod12(max_note - note);
436- }
437- else if( min_note > note ) {
438- note = min_note + musicalMod12(note - min_note);
439- }
440- return note;
441- }
442- protected:
443- static PROGMEM const Instrument * const defaultInstrument;
444- static MidiChannel channels[16];
445- static VoiceStatus voices[PWMDAC_POLYPHONY];
446- static PROGMEM const byte maxVolumeSineWavetable[];
447- static VoiceStatus *assignVoice(MidiChannel *channel, byte note) {
448- EACH_VOICE(v) if( v->isNoteOn(note, channel) ) return v;
449- VoiceStatus *lowest_priority_voice = voices;
450- byte lowest_priority = VoiceStatus::HIGHEST_PRIORITY;
451- EACH_VOICE(v) {
452- byte priority = v->getPriority();
453- if( priority > lowest_priority ) continue;
454- lowest_priority = priority;
455- lowest_priority_voice = v;
456- }
457- lowest_priority_voice->setChannel(channel);
458- return lowest_priority_voice;
448+ for( byte i = 0; i < NumberOf(voices); ++i ) voices[i].allSoundOff();
449+ for( byte i = 0; i < NumberOf(channels); i++ ) channels[i].reset(defaultInstrument);
459450 }
460-#undef EACH_VOICE
461451 };
462452
463453 #define PWMDAC_CREATE_INSTANCE(instrument) \
464- ISR(PWMDAC_OVF_vect) { PWMDACSynth::updatePulseWidth(); } \
465- VoiceStatus PWMDACSynth::voices[PWMDAC_POLYPHONY]; \
466454 PWMDAC_CREATE_WAVETABLE(PWMDACSynth::maxVolumeSineWavetable, PWMDAC_MAX_VOLUME_SINE_WAVE); \
467- PROGMEM const Instrument * const PWMDACSynth::defaultInstrument = instrument; \
468- MidiChannel PWMDACSynth::channels[16] = MidiChannel(instrument);
455+ MidiChannel PWMDACSynth::channels[] = { \
456+ MidiChannel(instrument), \
457+ MidiChannel(instrument), \
458+ MidiChannel(instrument), \
459+ MidiChannel(instrument), \
460+\
461+ MidiChannel(instrument), \
462+ MidiChannel(instrument), \
463+ MidiChannel(instrument), \
464+ MidiChannel(instrument), \
465+\
466+ MidiChannel(instrument), \
467+ MidiChannel(instrument), \
468+ MidiChannel(instrument), \
469+ MidiChannel(instrument), \
470+\
471+ MidiChannel(instrument), \
472+ MidiChannel(instrument), \
473+ MidiChannel(instrument), \
474+ MidiChannel(instrument), \
475+ }; \
476+ const Instrument * const PWMDACSynth::defaultInstrument PROGMEM = instrument; \
477+ PWMDACSynth::Voice PWMDACSynth::voices[]; \
478+ ISR(PWMDAC_OVF_vect) { PWMDACSynth::__updatePulseWidth(); }
469479
--- a/README.txt
+++ b/README.txt
@@ -1,7 +1,7 @@
11
22 [PWMDAC_Synth - PWM DAC synthesizer library for Arduino]
33
4-ver.20190330
4+ver.20200405
55
66 https://osdn.jp/users/kamide/pf/PWMDAC_Synth/wiki/FrontPage
77
@@ -99,18 +99,13 @@ Arduino
9999 ●同時発音数(ボイス数)
100100
101101 デフォルトは6重和音です。
102+ PWMDAC_POLYPHONY に数値を設定してコンパイルし直すことで増減可能です。
102103
103- PWMDAC_POLYPHONY に数値を設定してコンパイルし直すことで、
104- 同時発音数を増減させることができます。
104+ 同時発音数を増やすと、その分、割り込み処理 ISR() の実行に時間がかかります。
105+ 割り込み以外の処理を行うための時間的余裕がなくなった時点で動作しなくなります。
106+ そうなる寸前が、事実上の同時発音数の上限です。
105107
106- 同時発音数を増やしすぎると、その分、割り込み処理に時間がかかって
107- 割り込み以外の処理を行う余裕がなくなり、正常に動作しなくなります。
108-
109- 同時発音数の1つの発音は「ボイス」という単位で管理され、ボイスの状態を
110- 保持するクラスとして VoiceStatus クラスを定義しています。
111- VoiceStatus クラスの配列の要素数が、そのまま同時発音数となります。
112-
113- 特定のMIDIチャンネルについてボイスアサインの優先度を上げる方法については、
108+ 特定のMIDIチャンネルに対しボイスアサインの優先度を上げることもできます。
114109 後述の「●チャンネル優先度指定」を参照。
115110
116111
@@ -222,26 +217,6 @@ Arduino
222217 多重和音感が失われることがありますので、適宜調整してください。
223218
224219
225-●ユーティリティ
226- byte musicalMod7(char x)
227- byte musicalMod12(char x)
228- それぞれ7で割った余り、12で割った余りを返します。
229- % 演算子と違い、負数を与えても正数で返します。
230- 内部的にはビット演算と加算だけで高速に計算します。
231- 音階の計算に便利です。
232-
233- byte log2(unsigned int x)
234- 2を底とする対数を、小数点以下を切捨てた整数で返します。
235- y = log2(x) と x = 1 << y は逆関数の関係になります。
236-
237- int musicalConstrain12(int note, int min_note, int max_note)
238- Arduino の constrain() を音階用に実装し直した関数。
239- note が min_note 〜 max_note の音域にあれば
240- note をそのまま返します。音域をはみ出していた場合、
241- 音階を変えずにオクターブ位置を変えることにより、
242- 範囲に収まるよう調整されます。
243-
244-
245220 ●その他の関数
246221
247222 その他、利用できる関数についてはソースコードのヘッダ PWMDAC_Synth.h を参照。
@@ -249,3 +224,4 @@ Arduino
249224
250225 作者:@きよし - Akiyoshi Kamide
251226 http://www.yk.rim.or.jp/~kamide/
227+
--- a/keywords.txt
+++ b/keywords.txt
@@ -25,12 +25,7 @@ controlChange KEYWORD2
2525 systemReset KEYWORD2
2626 getChannel KEYWORD2
2727 setEnvelope KEYWORD2
28-setWave KEYWORD2
2928 setPriority KEYWORD2
30-musicalMod12 KEYWORD2
31-musicalMod7 KEYWORD2
32-log2 KEYWORD2
33-musicalConstrain12 KEYWORD2
3429
3530 #######################################
3631 # Constants (LITERAL1)