• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Frequently used words (click to add to your profile)

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

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


Commit MetaInfo

Revision817c85c7ad84d568cfb2a92c5d75bbf19ce0efb0 (tree)
Zeit2020-04-14 01:14:12
AutorAkiyoshi Kamide <kamide@yk.r...>
CommiterAkiyoshi Kamide

Log Message

Performance dramatically revived by specifying #pragma GCC optimize ("-O3")

Ändern Zusammenfassung

Diff

--- a/PWMDAC_Synth.h
+++ b/PWMDAC_Synth.h
@@ -1,17 +1,18 @@
11 //
2-// PWM DAC Synthesizer ver.20200405
2+// PWM DAC Synthesizer ver.20200413
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/
66 //
77 #pragma once
8+#pragma GCC push_options
9+#pragma GCC optimize ("-O3")
810
911 #include <Arduino.h>
1012 #include <wiring_private.h>
1113 #include <limits.h>
1214
1315 #define NumberOf(array) (sizeof(array)/sizeof(*(array)))
14-#define HighestElementOf(array) ((array)[NumberOf(array)-1])
1516 #define cbi16(sfr, bit) (_SFR_WORD(sfr) &= ~_BV(bit))
1617 #define sbi16(sfr, bit) (_SFR_WORD(sfr) |= _BV(bit))
1718
@@ -57,6 +58,52 @@
5758
5859 #define PWMDAC_CREATE_WAVETABLE(table, function) PROGMEM const byte table[] = ARRAY256(function)
5960
61+// [Phase-correct PWM dual-slope]
62+// TCNTn value changes to:
63+// 00(BOTTOM) 01 02 03 ... FC FD FE
64+// FF(TOP) FE FD FC ... 03 02 01
65+// -> # of values = 0xFF * 2 = 510 (NOT 512)
66+//
67+// ISR() call interval = (# of values) / F_CPU(16MHz) = 31.875us
68+//
69+// [MIDI Tuning Standard]
70+// http://en.wikipedia.org/wiki/MIDI_Tuning_Standard
71+// fn(d) = 440 Hz * 2^( (d - 69) / 12 ) MIDI note # d = 0..127
72+//
73+#define PHASE_SPEED_OF(note_number) (static_cast<unsigned long>( \
74+ pow( 2, static_cast<double>(note_number - 69)/12 + (sizeof(unsigned long) * 8 + 1) ) \
75+ * PWMDAC_NOTE_A_FREQUENCY * 0xFF / F_CPU ))
76+
77+// PWM Port macros
78+#if PWMDAC_OUTPUT_PIN == 6 || PWMDAC_OUTPUT_PIN == 5
79+// In Arduino, TIMER0 has been reserved by wiring.c in Arduino core,
80+// so defining PWMDAC_OUTPUT_PIN = 5 or 6 causes compile error
81+// (multiple definition of `__vector_16')
82+#define PWMDAC_USE_TIMER0
83+#define PWMDAC_OVF_vect TIMER0_OVF_vect
84+#if PWMDAC_OUTPUT_PIN == 6
85+#define PWMDAC_OCR OCR0A
86+#else
87+#define PWMDAC_OCR OCR0B
88+#endif
89+#elif PWMDAC_OUTPUT_PIN == 9 || PWMDAC_OUTPUT_PIN == 10
90+#define PWMDAC_USE_TIMER1
91+#define PWMDAC_OVF_vect TIMER1_OVF_vect
92+#if PWMDAC_OUTPUT_PIN == 9
93+#define PWMDAC_OCR OCR1A // OC1A maybe used as MOSI for SPI
94+#else
95+#define PWMDAC_OCR OCR1B // OC1B maybe used as SS for SPI
96+#endif
97+#elif PWMDAC_OUTPUT_PIN == 11 || PWMDAC_OUTPUT_PIN == 3
98+#define PWMDAC_USE_TIMER2
99+#define PWMDAC_OVF_vect TIMER2_OVF_vect
100+#if PWMDAC_OUTPUT_PIN == 11
101+#define PWMDAC_OCR OCR2A
102+#else
103+#define PWMDAC_OCR OCR2B // OC2B maybe used as INT1
104+#endif
105+#endif
106+
60107 enum AdsrParam : byte {
61108 ADSR_RELEASE_VALUE, // 0..15
62109 ADSR_SUSTAIN_VALUE, // 0..255
@@ -164,51 +211,6 @@ class MidiChannel {
164211 }
165212 };
166213
167-#if PWMDAC_OUTPUT_PIN == 6 || PWMDAC_OUTPUT_PIN == 5
168-// In Arduino, TIMER0 has been reserved by wiring.c in Arduino core,
169-// so defining PWMDAC_OUTPUT_PIN = 5 or 6 causes compile error
170-// (multiple definition of `__vector_16')
171-#define PWMDAC_USE_TIMER0
172-#define PWMDAC_OVF_vect TIMER0_OVF_vect
173-#if PWMDAC_OUTPUT_PIN == 6
174-#define PWMDAC_OCR OCR0A
175-#else
176-#define PWMDAC_OCR OCR0B
177-#endif
178-#elif PWMDAC_OUTPUT_PIN == 9 || PWMDAC_OUTPUT_PIN == 10
179-#define PWMDAC_USE_TIMER1
180-#define PWMDAC_OVF_vect TIMER1_OVF_vect
181-#if PWMDAC_OUTPUT_PIN == 9
182-#define PWMDAC_OCR OCR1A // OC1A maybe used as MOSI for SPI
183-#else
184-#define PWMDAC_OCR OCR1B // OC1B maybe used as SS for SPI
185-#endif
186-#elif PWMDAC_OUTPUT_PIN == 11 || PWMDAC_OUTPUT_PIN == 3
187-#define PWMDAC_USE_TIMER2
188-#define PWMDAC_OVF_vect TIMER2_OVF_vect
189-#if PWMDAC_OUTPUT_PIN == 11
190-#define PWMDAC_OCR OCR2A
191-#else
192-#define PWMDAC_OCR OCR2B // OC2B maybe used as INT1
193-#endif
194-#endif
195-
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-
212214 class PWMDACSynth {
213215 protected:
214216 static PROGMEM const byte maxVolumeSineWavetable[];
@@ -223,12 +225,26 @@ class PWMDACSynth {
223225 ADSR_DECAY,
224226 ADSR_ATTACK
225227 };
226- union { unsigned long v32; byte v8[4]; } phase;
228+ union {
229+ unsigned long v32;
230+ struct {
231+ byte lowest;
232+ byte lowest2;
233+ byte highest2;
234+ byte highest;
235+ } v8;
236+ } phase;
227237 unsigned long dphase32_real; // Real phase speed
228238 unsigned long dphase32_bended; // Pitch-bended phase speed
229239 unsigned long dphase32_original; // Original phase speed
230240 long dphase32_moffset; // Modulation pitch offset
231- union { unsigned int v16; byte v8[2]; } volume;
241+ union {
242+ unsigned int v16;
243+ struct {
244+ byte low;
245+ byte high;
246+ } v8;
247+ } volume;
232248 unsigned int dv; // Diff of volume
233249 unsigned int sustain; // Volume when ADSR Sustain
234250 byte note; // 0..127
@@ -242,7 +258,7 @@ class PWMDACSynth {
242258 static const byte LOWEST_PRIORITY = 0;
243259 Voice() { allSoundOff(); }
244260 byte getPriority() const {
245- byte t = HighestElementOf(volume.v8) >> 1;
261+ byte t = volume.v8.high >> 1;
246262 if( adsr_status == ADSR_ATTACK ) t = HIGHEST_PRIORITY - t;
247263 #if defined(PWMDAC_CHANNEL_PRIORITY_SUPPORT)
248264 if( channel != nullptr && t > channel->priority_volume_threshold ) {
@@ -295,8 +311,7 @@ class PWMDACSynth {
295311 }
296312 unsigned int nextPulseWidth() {
297313 phase.v32 += dphase32_real;
298- return HighestElementOf(volume.v8) *
299- pgm_read_byte(wavetable + HighestElementOf(phase.v8));
314+ return volume.v8.high * pgm_read_byte(wavetable + phase.v8.highest);
300315 }
301316 void update(const byte modulation_offset) {
302317 // Update volume by ADSR envelope
@@ -345,18 +360,8 @@ class PWMDACSynth {
345360 };
346361 static Voice voices[PWMDAC_POLYPHONY];
347362 public:
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
363+ static void setup() {
364+ // must be called from your setup() once
360365 pinMode(PWMDAC_OUTPUT_PIN,OUTPUT);
361366 #ifdef PWMDAC_USE_TIMER1
362367 // No prescaling
@@ -409,18 +414,19 @@ class PWMDACSynth {
409414 static void noteOff(const byte channel, const byte pitch, const byte velocity) {
410415 (void)velocity;
411416 MidiChannel *cp = getChannel(channel);
412- for( byte i = 0; i < NumberOf(voices); ++i ) voices[i].noteOff(pitch, cp);
417+ byte i = NumberOf(voices); while( i > 0 ) voices[--i].noteOff(pitch, cp);
413418 }
414419 static void noteOn(const byte channel, const byte pitch, const byte velocity) {
415420 (void)velocity;
416421 const MidiChannel * const cp = getChannel(channel);
417- for( byte i = 0; i < NumberOf(voices); ++i )
418- if( voices[i].reAttackIfAssigned(pitch, cp) ) return;
422+ byte i = NumberOf(voices);
423+ while(i) if( voices[--i].reAttackIfAssigned(pitch, cp) ) return;
419424 // Search voice to assign
420425 byte lowest_priority = Voice::HIGHEST_PRIORITY;
421426 byte lowest_priority_index = 0;
422- for( byte i = 0; i < NumberOf(voices); ++i ) {
423- const byte priority = voices[i].getPriority();
427+ i = NumberOf(voices);
428+ while(i) {
429+ const byte priority = voices[--i].getPriority();
424430 if( priority > lowest_priority ) continue;
425431 lowest_priority = priority;
426432 lowest_priority_index = i;
@@ -430,23 +436,38 @@ class PWMDACSynth {
430436 static void pitchBend(const byte channel, const int bend) {
431437 MidiChannel *cp = getChannel(channel);
432438 if ( ! cp->pitchBendChange(bend) ) return;
433- for( byte i = 0; i < NumberOf(voices); ++i ) voices[i].updatePitch(cp);
439+ byte i = NumberOf(voices); while(i) voices[--i].updatePitch(cp);
434440 }
435441 static void controlChange(const byte channel, const byte number, const byte value) {
436442 MidiChannel *cp = getChannel(channel);
437443 cp->controlChange(number, value);
444+ byte i;
438445 switch(number) {
439446 case 120: // All sound off
440- for( byte i = 0; i < NumberOf(voices); ++i ) voices[i].allSoundOff(cp);
447+ i = NumberOf(voices); while(i) voices[--i].allSoundOff(cp);
441448 break;
442449 case 123: // All notes off
443- for( byte i = 0; i < NumberOf(voices); ++i ) voices[i].allNotesOff(cp);
450+ i = NumberOf(voices); while(i) voices[--i].allNotesOff(cp);
444451 break;
445452 }
446453 }
447454 static void systemReset() {
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);
455+ byte i = NumberOf(voices); while(i) voices[--i].allSoundOff();
456+ i = NumberOf(channels); while(i) channels[--i].reset(defaultInstrument);
457+ }
458+ static byte __nextPulseWidth() {
459+ union { unsigned int v16; struct {byte low; byte high;} v8; } pw = {0};
460+ byte i = NumberOf(voices);
461+ while(i) pw.v16 += voices[--i].nextPulseWidth();
462+ return pw.v8.high;
463+ }
464+ static void update() {
465+ // must be called from your loop() repeatedly
466+ static byte modulation_phase = 0;
467+ const byte *addr = maxVolumeSineWavetable + modulation_phase++;
468+ const byte modulation_offset = pgm_read_byte(addr) - 0x7F;
469+ byte i = NumberOf(voices);
470+ while(i) voices[--i].update(modulation_offset);
450471 }
451472 };
452473
@@ -475,5 +496,7 @@ class PWMDACSynth {
475496 }; \
476497 const Instrument * const PWMDACSynth::defaultInstrument PROGMEM = instrument; \
477498 PWMDACSynth::Voice PWMDACSynth::voices[]; \
478- ISR(PWMDAC_OVF_vect) { PWMDACSynth::__updatePulseWidth(); }
499+ ISR(PWMDAC_OVF_vect) { PWMDAC_OCR = PWMDACSynth::__nextPulseWidth(); }
500+
501+#pragma GCC pop_options
479502