├── pra32-u-sample-midi-stream.bin ├── pra32-u-pwm-audio-bread-board.png ├── pra32-u-with-panel-breadboard.jpg ├── pra32-u-with-panel-grove-system.jpg ├── pra32-u-pwm-audio-circuit-diagram.png ├── bin ├── Digital-Synth-PRA32-U-PWM-Audio.uf2 └── Digital-Synth-PRA32-U-Pimoroni-Pico-Audio-Pack.uf2 ├── pra32-u-with-panel-circuit-diagram.png ├── pra32-u-make-sample-wav-file-cc.bat ├── Digital-Synth-PRA32-U ├── pra32-u-generate-constants-rb-from-h.rb ├── pra32-u-noise-gen.h ├── pra32-u-generate-eg-table.rb ├── pra32-u-generate-lfo-table.rb ├── pra32-u-common.h ├── pra32-u-amp.h ├── pra32-u-generate-filter-table.rb ├── pra32-u-lfo-table.h ├── pra32-u-eg-table.h ├── pra32-u-eg.h ├── pra32-u-delay-fx.h ├── pra32-u-lfo.h ├── pra32-u-chorus-fx.h ├── pra32-u-generate-osc-table.rb ├── pra32-u-program-table.h ├── pra32-u-constants.rb ├── pra32-u-filter.h ├── pra32-u-constants.h ├── pra32-u-control-panel-page-table.h ├── pra32-u-control-panel-font-table.h ├── Digital-Synth-PRA32-U.ino └── pra32-u-osc.h ├── .gitignore ├── pra32-u-make-sample-wav-file.cc ├── pra32-u-wav-file-out.h ├── pra32-u-generate-sample-midi-stream.rb ├── pra32-u-prog-preset.json ├── pra32-u-prog-sample-saw-pulse.json ├── pra32-u-prog-sample-sine-pm-piano.json ├── pra32-u-midi-in.h ├── README-PRA32-U-with-Panel.md ├── LICENSE ├── PRA32-U-Parameter-Guide.md ├── PRA32-U-Change-History.md ├── README.md └── PRA32-U-MIDI-Implementation-Chart.md /pra32-u-sample-midi-stream.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/risgk/digital-synth-pra32-u/HEAD/pra32-u-sample-midi-stream.bin -------------------------------------------------------------------------------- /pra32-u-pwm-audio-bread-board.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/risgk/digital-synth-pra32-u/HEAD/pra32-u-pwm-audio-bread-board.png -------------------------------------------------------------------------------- /pra32-u-with-panel-breadboard.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/risgk/digital-synth-pra32-u/HEAD/pra32-u-with-panel-breadboard.jpg -------------------------------------------------------------------------------- /pra32-u-with-panel-grove-system.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/risgk/digital-synth-pra32-u/HEAD/pra32-u-with-panel-grove-system.jpg -------------------------------------------------------------------------------- /pra32-u-pwm-audio-circuit-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/risgk/digital-synth-pra32-u/HEAD/pra32-u-pwm-audio-circuit-diagram.png -------------------------------------------------------------------------------- /bin/Digital-Synth-PRA32-U-PWM-Audio.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/risgk/digital-synth-pra32-u/HEAD/bin/Digital-Synth-PRA32-U-PWM-Audio.uf2 -------------------------------------------------------------------------------- /pra32-u-with-panel-circuit-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/risgk/digital-synth-pra32-u/HEAD/pra32-u-with-panel-circuit-diagram.png -------------------------------------------------------------------------------- /bin/Digital-Synth-PRA32-U-Pimoroni-Pico-Audio-Pack.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/risgk/digital-synth-pra32-u/HEAD/bin/Digital-Synth-PRA32-U-Pimoroni-Pico-Audio-Pack.uf2 -------------------------------------------------------------------------------- /pra32-u-make-sample-wav-file-cc.bat: -------------------------------------------------------------------------------- 1 | g++ .\pra32-u-make-sample-wav-file.cc -o .\pra32-u-make-sample-wav-file.exe 2 | .\pra32-u-make-sample-wav-file.exe .\pra32-u-sample-midi-stream.bin .\pra32-u-sample-wav-file-cc.wav 3 | del .\pra32-u-make-sample-wav-file.exe 4 | -------------------------------------------------------------------------------- /Digital-Synth-PRA32-U/pra32-u-generate-constants-rb-from-h.rb: -------------------------------------------------------------------------------- 1 | File.open("../Digital-Synth-PRA32-U/pra32-u-constants.h", "r") do |input| 2 | File.open("./pra32-u-constants.rb", "w") do |output| 3 | input.each_line do |line| 4 | output.puts line.chomp[16..-2] 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | 30 | # Others 31 | *.wav 32 | DigitalSynthVRA8U/build/* 33 | -------------------------------------------------------------------------------- /Digital-Synth-PRA32-U/pra32-u-noise-gen.h: -------------------------------------------------------------------------------- 1 | // refs https://en.wikipedia.org/wiki/Xorshift 2 | 3 | #pragma once 4 | 5 | #include "pra32-u-common.h" 6 | 7 | class PRA32_U_NoiseGen { 8 | uint32_t m_state_a; 9 | uint32_t m_state_a_prev; 10 | 11 | public: 12 | PRA32_U_NoiseGen() 13 | : m_state_a(1) 14 | , m_state_a_prev(0) 15 | { 16 | } 17 | 18 | INLINE int16_t process() { 19 | m_state_a_prev = m_state_a; 20 | 21 | uint32_t x = m_state_a; 22 | x ^= x << 13; 23 | x ^= x >> 17; 24 | x ^= x << 5; 25 | m_state_a = x; 26 | 27 | int16_t noise_int15 = (m_state_a >> 17) - 16384; 28 | return noise_int15; 29 | } 30 | 31 | INLINE void get_rand_uint8_array(uint8_t array[8]) { 32 | array[0] = (m_state_a_prev >> 0) & 0xFFu; 33 | array[1] = (m_state_a_prev >> 8) & 0xFFu; 34 | array[2] = (m_state_a_prev >> 16) & 0xFFu; 35 | array[3] = (m_state_a_prev >> 24) & 0xFFu; 36 | array[4] = (m_state_a >> 0) & 0xFFu; 37 | array[5] = (m_state_a >> 8) & 0xFFu; 38 | array[6] = (m_state_a >> 16) & 0xFFu; 39 | array[7] = (m_state_a >> 24) & 0xFFu; 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /Digital-Synth-PRA32-U/pra32-u-generate-eg-table.rb: -------------------------------------------------------------------------------- 1 | require_relative 'pra32-u-constants' 2 | 3 | $file = File.open("pra32-u-eg-table.h", "w") 4 | 5 | $file.printf("#pragma once\n\n") 6 | 7 | $file.printf("int32_t g_eg_attack_release_coef_table[] = {\n ") 8 | (0..127 + 16).each do |i| 9 | time = i 10 | eg_coef = (0.5 ** (1.0 / ((0.2 / 10.0) * (SAMPLING_RATE / 4) * (10.0 ** ((time - 64.0) / 32.0)))) * 0x40000000).round 11 | 12 | $file.printf("%10d,", eg_coef) 13 | if i == 127 + 16 14 | $file.printf("\n") 15 | elsif i % 8 == (8 - 1) 16 | $file.printf("\n ") 17 | else 18 | $file.printf(" ") 19 | end 20 | end 21 | $file.printf("};\n\n") 22 | 23 | $file.printf("int32_t g_eg_decay_coef_table[] = {\n ") 24 | (0..127).each do |i| 25 | time = i 26 | eg_coef = (0.5 ** (1.0 / ((0.2 / 10.0) * (SAMPLING_RATE / 4) * (10.0 ** ((time - 64.0) / 32.0)))) * 0x40000000).round 27 | eg_coef = 0x40000000 if time == 127 28 | 29 | $file.printf("%10d,", eg_coef) 30 | if i == 127 31 | $file.printf("\n") 32 | elsif i % 8 == (8 - 1) 33 | $file.printf("\n ") 34 | else 35 | $file.printf(" ") 36 | end 37 | end 38 | $file.printf("};\n\n") 39 | 40 | $file.close 41 | -------------------------------------------------------------------------------- /pra32-u-make-sample-wav-file.cc: -------------------------------------------------------------------------------- 1 | #define PRA32_U_MIDI_CH (0) // 0-based 2 | 3 | 4 | #include 5 | #include 6 | 7 | typedef signed char boolean; 8 | 9 | #define __not_in_flash_func(func) (func) 10 | 11 | uint8_t g_midi_ch = PRA32_U_MIDI_CH; 12 | 13 | #include "./Digital-Synth-PRA32-U/pra32-u-common.h" 14 | #include "./Digital-Synth-PRA32-U/pra32-u-synth.h" 15 | #include "./pra32-u-midi-in.h" 16 | #include "./pra32-u-wav-file-out.h" 17 | 18 | PRA32_U_Synth g_synth; 19 | PRA32_U_MIDIIn g_midi_in; 20 | PRA32_U_WAVFileOut g_wav_file_out; 21 | 22 | const uint16_t RECORDING_SEC = 60; 23 | const uint16_t SERIAL_SPEED_38400 = 38400; 24 | 25 | int main(int argc, char *argv[]) { 26 | // setup 27 | g_midi_in.open(g_synth); 28 | FILE* bin_file = ::fopen(argv[1], "rb"); 29 | g_wav_file_out.open(argv[2], RECORDING_SEC); 30 | g_synth.initialize(); 31 | 32 | // loop 33 | int c; 34 | while ((c = ::fgetc(bin_file)) != EOF) { 35 | g_midi_in.receive_midi_byte(c); 36 | uint16_t r = SAMPLING_RATE / (SERIAL_SPEED_38400 / 10); 37 | for (uint16_t i = 0; i < r; i++) { 38 | int16_t right_level; 39 | int16_t left_level = g_synth.process(right_level); 40 | g_wav_file_out.write(left_level, right_level); 41 | } 42 | } 43 | 44 | // teardown 45 | g_wav_file_out.close(); 46 | ::fclose(bin_file); 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /Digital-Synth-PRA32-U/pra32-u-generate-lfo-table.rb: -------------------------------------------------------------------------------- 1 | require_relative 'pra32-u-constants' 2 | 3 | $file = File.open("pra32-u-lfo-table.h", "w") 4 | 5 | $file.printf("#pragma once\n\n") 6 | 7 | $file.printf("uint32_t g_lfo_rate_table[] = {\n ") 8 | (0..127).each do |i| 9 | lfo_rate = ((2.0 ** ((i - 64) / 12.0)) * 10 | (A4_FREQ * (2.0 ** ((-19 - 69) / 12.0))) * (1 << 24) / (SAMPLING_RATE / 4.0)).floor 11 | 12 | $file.printf("%6d,", lfo_rate) 13 | if i == DATA_BYTE_MAX 14 | $file.printf("\n") 15 | elsif i % 16 == (16 - 1) 16 | $file.printf("\n ") 17 | else 18 | $file.printf(" ") 19 | end 20 | end 21 | $file.printf("};\n\n") 22 | 23 | $file.printf("uint16_t g_lfo_fade_coef_table[] = {\n ") 24 | (0..127).each do |i| 25 | fade_coef = (10.0 * (10.0 ** ((i - 128.0) / 64.0)) * (SAMPLING_RATE / 4.0) / 128.0).floor 26 | fade_coef = 1 if i == 0 27 | 28 | $file.printf("%6d,", fade_coef) 29 | if i == DATA_BYTE_MAX 30 | $file.printf("\n") 31 | elsif i % 16 == (16 - 1) 32 | $file.printf("\n ") 33 | else 34 | $file.printf(" ") 35 | end 36 | end 37 | $file.printf("};\n\n") 38 | 39 | $file.printf("uint32_t g_chorus_rate_table[] = {\n ") 40 | (0..127).each do |i| 41 | lfo_rate = ((2.0 ** ((i - 64) / 12.0)) * 42 | (A4_FREQ * (2.0 ** ((-49 - 69) / 12.0))) * (1 << 24) / (SAMPLING_RATE / 4.0)).floor 43 | 44 | $file.printf("%6d,", lfo_rate) 45 | if i == DATA_BYTE_MAX 46 | $file.printf("\n") 47 | elsif i % 16 == (16 - 1) 48 | $file.printf("\n ") 49 | else 50 | $file.printf(" ") 51 | end 52 | end 53 | $file.printf("};\n\n") 54 | 55 | $file.close 56 | -------------------------------------------------------------------------------- /Digital-Synth-PRA32-U/pra32-u-common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pra32-u-constants.h" 4 | 5 | #define INLINE inline __attribute__((always_inline)) 6 | 7 | #if !defined(ARDUINO_ARCH_AVR) 8 | typedef int32_t __int24; 9 | typedef uint32_t __uint24; 10 | #endif 11 | 12 | static INLINE uint8_t low_byte(uint16_t x) { 13 | return x & 0xFF; 14 | } 15 | 16 | static INLINE uint8_t high_byte(uint16_t x) { 17 | return (x >> 8) & 0xFF; 18 | } 19 | 20 | static INLINE int8_t high_sbyte(int16_t x) { 21 | return (x >> 8) & 0xFF; 22 | } 23 | 24 | static INLINE uint8_t hhigh_byte(__uint24 x) { 25 | return x >> 16; 26 | } 27 | 28 | static INLINE int8_t hhigh_sbyte(__int24 x) { 29 | return x >> 16; 30 | } 31 | 32 | static INLINE int32_t mul_s32_s16_h32(int32_t x, int16_t y) { 33 | int32_t x1 = x >> 16; 34 | uint32_t x0 = x & 0xFFFF; 35 | return (static_cast(x0 * y) >> 16) + (x1 * y); 36 | } 37 | 38 | static INLINE int32_t mul_s32_u16_h32(int32_t x, uint16_t y) { 39 | int32_t x1 = x >> 16; 40 | uint32_t x0 = x & 0xFFFF; 41 | return (static_cast(x0 * y) >> 16) + (x1 * y); 42 | } 43 | 44 | static INLINE int32_t mul_u32_s16_h32(uint32_t x, int16_t y) { 45 | uint32_t x1 = x >> 16; 46 | uint32_t x0 = x & 0xFFFF; 47 | return (static_cast(x0 * y) >> 16) + (x1 * y); 48 | } 49 | 50 | static INLINE uint32_t mul_u32_u16_h32(uint32_t x, uint16_t y) { 51 | uint32_t x1 = x >> 16; 52 | uint32_t x0 = x & 0xFFFF; 53 | return (static_cast(x0 * y) >> 16) + (x1 * y); 54 | } 55 | 56 | // refs https://web.archive.org/web/20190109010921/http://www.hackersdelight.org/hdcodetxt/mulhs.c.txt 57 | 58 | static INLINE int32_t mul_s32_s32_h32(int32_t x, int32_t y) { 59 | int32_t x1 = x >> 16; 60 | uint32_t x0 = x & 0xFFFF; 61 | int32_t y1 = y >> 16; 62 | uint32_t y0 = y & 0xFFFF; 63 | int32_t x0_y1 = x0 * y1; 64 | int32_t z = ((x0 * y0) >> 16) + 65 | (x1 * y0) + (x0_y1 & 0xFFFF); 66 | return (z >> 16) + (x0_y1 >> 16) + (x1 * y1); 67 | } 68 | -------------------------------------------------------------------------------- /pra32-u-wav-file-out.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "./Digital-Synth-PRA32-U/pra32-u-common.h" 5 | 6 | class PRA32_U_WAVFileOut { 7 | FILE* m_file; 8 | uint32_t m_max_size; 9 | uint32_t m_data_size; 10 | boolean m_closed; 11 | 12 | public: 13 | PRA32_U_WAVFileOut() 14 | : m_file() 15 | , m_max_size() 16 | , m_data_size() 17 | , m_closed() 18 | {} 19 | 20 | INLINE void open(const char* path, uint16_t sec) { 21 | m_file = fopen(path, "wb"); 22 | fwrite("RIFF", 1, 4, m_file); 23 | fwrite("\x00\x00\x00\x00", 1, 4, m_file); 24 | fwrite("WAVE", 1, 4, m_file); 25 | fwrite("fmt ", 1, 4, m_file); 26 | fwrite("\x10\x00\x00\x00", 1, 4, m_file); 27 | fwrite("\x01\x00\x02\x00", 1, 4, m_file); 28 | uint32_t a[1] = {SAMPLING_RATE}; 29 | fwrite(a, 4, 1, m_file); 30 | a[0] = {SAMPLING_RATE * 4}; 31 | fwrite(a, 4, 1, m_file); 32 | fwrite("\x04\x00\x10\x00", 1, 4, m_file); 33 | fwrite("data", 1, 4, m_file); 34 | fwrite("\x00\x00\x00\x00", 1, 4, m_file); 35 | m_max_size = (SAMPLING_RATE) * 2 * sec; 36 | m_data_size = 0; 37 | m_closed = false; 38 | } 39 | 40 | INLINE void write(int16_t left, int16_t right) { 41 | if (m_data_size < m_max_size) { 42 | int16_t l[1] = {left}; 43 | int16_t r[1] = {right}; 44 | fwrite(l, 2, 1, m_file); 45 | fwrite(r, 2, 1, m_file); 46 | m_data_size += 2; 47 | m_data_size += 2; 48 | } else { 49 | close(); 50 | m_closed = true; 51 | } 52 | } 53 | 54 | INLINE void close() { 55 | if (!m_closed) { 56 | fseek(m_file, 0, SEEK_END); 57 | long file_size = ftell(m_file); 58 | fseek(m_file, 4, SEEK_SET); 59 | uint32_t a[1] = {static_cast(file_size) - 8}; 60 | fwrite(a, 4, 1, m_file); 61 | fseek(m_file, 40, SEEK_SET); 62 | uint32_t a2[1] = {static_cast(file_size) - 36}; 63 | fwrite(a2, 4, 1, m_file); 64 | fclose(m_file); 65 | printf("End Of Recording\n"); 66 | } 67 | } 68 | }; 69 | -------------------------------------------------------------------------------- /Digital-Synth-PRA32-U/pra32-u-amp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pra32-u-common.h" 4 | 5 | class PRA32_U_Amp { 6 | int16_t m_gain_control; 7 | int16_t m_gain_control_effective; 8 | int16_t m_gain_linear; 9 | int16_t m_gain_mod_input; 10 | uint8_t m_breath_mod; 11 | uint8_t m_breath_controller; 12 | int16_t m_breath_gain_linear; 13 | 14 | public: 15 | PRA32_U_Amp() 16 | : m_gain_control(127) 17 | , m_gain_control_effective(0) 18 | , m_gain_linear() 19 | , m_gain_mod_input(0) 20 | , m_breath_mod() 21 | , m_breath_controller() 22 | , m_breath_gain_linear() 23 | { 24 | } 25 | 26 | INLINE void set_gain(uint8_t controller_value) { 27 | m_gain_control = controller_value; 28 | } 29 | 30 | INLINE void set_breath_mod(uint8_t controller_value) { 31 | #if 1 32 | if (controller_value == 2 || controller_value >= 96) { 33 | #else 34 | if (controller_value >= 96) { 35 | #endif 36 | m_breath_mod = 2; 37 | #if 1 38 | } else if (controller_value == 1 || controller_value >= 32) { 39 | #else 40 | } else if (controller_value >= 32) { 41 | #endif 42 | m_breath_mod = 1; 43 | } else { 44 | m_breath_mod = 0; 45 | } 46 | } 47 | 48 | INLINE void set_breath_controller(uint8_t controller_value) { 49 | m_breath_controller = controller_value; 50 | } 51 | 52 | INLINE void process_at_low_rate(int16_t gain_mod_input) { 53 | update_gain_effective(); 54 | m_gain_mod_input = gain_mod_input; 55 | update_breath_controller_effective(); 56 | } 57 | 58 | INLINE int16_t process(int16_t audio_input) { 59 | int16_t audio_output = (audio_input * m_gain_mod_input) >> 14; 60 | audio_output = (audio_output * m_gain_linear) >> 14; 61 | audio_output = (audio_output * m_breath_gain_linear) >> 14; 62 | return audio_output; 63 | } 64 | 65 | private: 66 | INLINE void update_gain_effective() { 67 | m_gain_control_effective += (m_gain_control_effective < m_gain_control); 68 | m_gain_control_effective -= (m_gain_control_effective > m_gain_control); 69 | 70 | m_gain_linear = ((m_gain_control_effective * m_gain_control_effective) * 16384) / 16129; 71 | } 72 | 73 | INLINE void update_breath_controller_effective() { 74 | if (m_breath_mod == 2) { 75 | m_breath_gain_linear = (m_breath_controller * 16384) / 127; 76 | } else if (m_breath_mod == 1) { 77 | m_breath_gain_linear = ((m_breath_controller * m_breath_controller) * 16384) / 16129; 78 | } else { 79 | m_breath_gain_linear = 16384; 80 | } 81 | } 82 | }; 83 | -------------------------------------------------------------------------------- /Digital-Synth-PRA32-U/pra32-u-generate-filter-table.rb: -------------------------------------------------------------------------------- 1 | require_relative 'pra32-u-constants' 2 | 3 | $file = File.open("pra32-u-filter-table.h", "w") 4 | 5 | $file.printf("#pragma once\n\n") 6 | 7 | OCTAVES = 10 8 | 9 | def generate_filter_lpf_table(res_id, name, q) 10 | $file.printf("int32_t g_filter_lpf_table_%s[] = {\n ", name) 11 | (0..DATA_BYTE_MAX * 2 + 1).each do |i| 12 | f_idx = [[-2, i - 1 * 2].max, 252].min 13 | f_0 = (2.0 ** ((f_idx / 2.0) / (120.0 / OCTAVES))) * ((A4_FREQ * 2.0) * 16.0) * 2.0 / (2.0 ** (OCTAVES.to_f + 1.0)) 14 | f_0_over_f_s = f_0 / SAMPLING_RATE 15 | 16 | w_0 = 2.0 * Math::PI * f_0_over_f_s 17 | alpha = Math.sin(w_0) / (2.0 * q) 18 | 19 | lpf_b_2 = (1.0 - Math.cos(w_0)) / 2.0 20 | hpf_b_2 = (1.0 + Math.cos(w_0)) / 2.0 21 | a_0 = 1.0 + alpha 22 | a_1 = (-2.0) * Math.cos(w_0) 23 | a_2 = 1.0 - alpha 24 | 25 | input_gain = 1.0 / (2.0 ** (res_id / 12.0)) 26 | b_2_over_a_0_gain = (input_gain * (lpf_b_2 / a_0) * (1 << FILTER_TABLE_FRACTION_BITS)).floor.to_i 27 | a_1_over_a_0 = ((a_1 / a_0) * (1 << FILTER_TABLE_FRACTION_BITS)).floor.to_i 28 | a_2_over_a_0 = ((a_2 / a_0) * (1 << FILTER_TABLE_FRACTION_BITS)).floor.to_i 29 | 30 | printf("i: %d, f_idx: %d, f_0_over_f_s: %f, f_0: %f, res_id: %d, q: %f, g: %f, q_mul_g: %f\n", i, f_idx, f_0_over_f_s, f_0, res_id, q, input_gain, q * input_gain) 31 | 32 | $file.printf("%+11d, %+11d, %+11d,", b_2_over_a_0_gain, a_1_over_a_0, a_2_over_a_0) 33 | if i == DATA_BYTE_MAX * 2 + 1 34 | $file.printf("\n") 35 | elsif i % 2 == (2 - 1) 36 | $file.printf("\n ") 37 | else 38 | $file.printf(" ") 39 | end 40 | end 41 | $file.printf("};\n\n") 42 | end 43 | 44 | MAX_RES_ID = 14 45 | 46 | (0..MAX_RES_ID).each do |res_id| 47 | generate_filter_lpf_table(res_id, res_id.to_s, Math.sqrt(2.0) ** ((res_id - 2.0) / 2.0)) 48 | end 49 | 50 | $file.printf("int32_t* g_filter_tables[] = {\n ") 51 | (0..16).each do |res_index| 52 | res_id = [[res_index - 1, 0].max, MAX_RES_ID].min 53 | $file.printf("g_filter_lpf_table_%-2d,", res_id) 54 | if res_index == DATA_BYTE_MAX 55 | $file.printf("\n") 56 | elsif res_index % 4 == 3 57 | $file.printf("\n ") 58 | else 59 | $file.printf(" ") 60 | end 61 | end 62 | $file.printf("};\n\n") 63 | 64 | $file.printf("int16_t g_filter_gain_tables[] = {\n ") 65 | (0..16).each do |res_index| 66 | res_id = [[res_index - 1, 0].max, MAX_RES_ID].min 67 | gain = ((1 << (FILTER_TABLE_FRACTION_BITS - 16)) * 1.0 / (2.0 ** (res_id / 12.0))).floor 68 | 69 | $file.printf("%+6d,", gain) 70 | if res_index == DATA_BYTE_MAX 71 | $file.printf("\n") 72 | elsif res_index % 4 == 3 73 | $file.printf("\n ") 74 | else 75 | $file.printf(" ") 76 | end 77 | end 78 | $file.printf("};\n\n") 79 | 80 | $file.close 81 | -------------------------------------------------------------------------------- /pra32-u-generate-sample-midi-stream.rb: -------------------------------------------------------------------------------- 1 | require_relative 'Digital-Synth-PRA32-U/pra32-u-constants' 2 | 3 | PRA32_U_MIDI_CH = 0 # 0-based 4 | 5 | $file = File.open("pra32-u-sample-midi-stream.bin", "wb") 6 | 7 | def control_change(control_number, value) 8 | $file.write([(CONTROL_CHANGE | PRA32_U_MIDI_CH), control_number, value].pack("C*")) 9 | wait(10) 10 | end 11 | 12 | def program_change(program_number) 13 | $file.write([(PROGRAM_CHANGE | PRA32_U_MIDI_CH), program_number].pack("C*")) 14 | wait(800) 15 | end 16 | 17 | def note_on(note_number, velocity) 18 | $file.write([(NOTE_ON | PRA32_U_MIDI_CH), note_number, velocity].pack("C*")) 19 | end 20 | 21 | def note_off(note_number) 22 | $file.write([(NOTE_OFF | PRA32_U_MIDI_CH), note_number, 64].pack("C*")) 23 | end 24 | 25 | def wait(length) 26 | length.times { $file.write([ACTIVE_SENSING].pack("C")) } 27 | end 28 | 29 | def play_mono_a(oct) 30 | play_mono(12, oct, 64) 31 | play_mono(16, oct, 64) 32 | play_mono(14, oct, 64) 33 | play_mono(17, oct, 64) 34 | end 35 | 36 | def play_a(oct) 37 | play_chord_a(12, 16, 19, 23, oct, 64) 38 | play_chord_a(16, 19, 23, 26, oct, 64) 39 | play_chord_a(14, 17, 21, 24, oct, 64) 40 | play_chord_a(17, 21, 24, 28, oct, 64) 41 | end 42 | 43 | def play_b(oct) 44 | play_chord_b(12, 16, 19, 23, oct, 64) 45 | play_chord_b(16, 19, 23, 26, oct, 64) 46 | play_chord_b(14, 17, 21, 24, oct, 64) 47 | play_chord_b(17, 21, 24, 28, oct, 64) 48 | end 49 | 50 | def play_mono(x, oct, velocity) 51 | note_on(x + (oct * 12), velocity) 52 | wait(3200) 53 | note_off(x + (oct * 12)) 54 | wait(800) 55 | end 56 | 57 | def play_chord_a(x, y, z, w, oct, velocity) 58 | note_on(x + (oct * 12), velocity) 59 | note_on(y + (oct * 12), velocity) 60 | note_on(z + (oct * 12), velocity) 61 | note_on(w + (oct * 12), velocity) 62 | wait(3200) 63 | note_off(x + (oct * 12)) 64 | note_off(y + (oct * 12)) 65 | note_off(z + (oct * 12)) 66 | note_off(w + (oct * 12)) 67 | wait(800) 68 | end 69 | 70 | def play_chord_b(x, y, z, w, oct, velocity) 71 | note_on(x + (oct * 12), velocity) 72 | wait(800) 73 | note_on(y + (oct * 12), velocity) 74 | wait(800) 75 | note_on(z + (oct * 12), velocity) 76 | wait(800) 77 | note_on(w + (oct * 12), velocity) 78 | wait(3200) 79 | note_off(w + (oct * 12)) 80 | wait(800) 81 | note_off(z + (oct * 12)) 82 | wait(800) 83 | note_off(y + (oct * 12)) 84 | wait(800) 85 | note_off(x + (oct * 12)) 86 | wait(800) 87 | end 88 | 89 | def sound_off 90 | control_change(ALL_SOUND_OFF, 0 ) 91 | wait(800) 92 | end 93 | 94 | sound_off 95 | 96 | program_change(0) 97 | play_mono_a(3) 98 | sound_off 99 | 100 | program_change(6) 101 | play_b(3) 102 | sound_off 103 | 104 | $file.close 105 | -------------------------------------------------------------------------------- /Digital-Synth-PRA32-U/pra32-u-lfo-table.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | uint32_t g_lfo_rate_table[] = { 4 | 94, 100, 106, 112, 119, 126, 133, 141, 150, 159, 168, 178, 189, 200, 212, 225, 5 | 238, 252, 267, 283, 300, 318, 337, 357, 378, 400, 424, 450, 476, 505, 535, 567, 6 | 600, 636, 674, 714, 756, 801, 849, 900, 953, 1010, 1070, 1134, 1201, 1272, 1348, 1428, 7 | 1513, 1603, 1699, 1800, 1907, 2020, 2140, 2268, 2402, 2545, 2697, 2857, 3027, 3207, 3398, 3600, 8 | 3814, 4041, 4281, 4536, 4805, 5091, 5394, 5715, 6055, 6415, 6796, 7200, 7629, 8082, 8563, 9072, 9 | 9611, 10183, 10789, 11430, 12110, 12830, 13593, 14401, 15258, 16165, 17126, 18144, 19223, 20367, 21578, 22861, 10 | 24220, 25660, 27186, 28803, 30516, 32330, 34253, 36289, 38447, 40734, 43156, 45722, 48441, 51321, 54373, 57606, 11 | 61032, 64661, 68506, 72579, 76895, 81468, 86312, 91444, 96882, 102643, 108746, 115213, 122064, 129322, 137012, 145159, 12 | }; 13 | 14 | uint16_t g_lfo_fade_coef_table[] = { 15 | 1, 9, 10, 10, 10, 11, 11, 12, 12, 12, 13, 13, 14, 14, 15, 16, 16 | 16, 17, 17, 18, 19, 19, 20, 21, 22, 23, 23, 24, 25, 26, 27, 28, 17 | 29, 30, 31, 33, 34, 35, 36, 38, 39, 40, 42, 44, 45, 47, 49, 50, 18 | 52, 54, 56, 58, 60, 63, 65, 67, 70, 72, 75, 78, 81, 84, 87, 90, 19 | 93, 97, 100, 104, 108, 112, 116, 120, 125, 129, 134, 139, 144, 149, 155, 160, 20 | 166, 172, 179, 185, 192, 199, 206, 214, 222, 230, 238, 247, 256, 266, 275, 285, 21 | 296, 307, 318, 330, 342, 354, 367, 381, 395, 409, 424, 440, 456, 473, 490, 508, 22 | 527, 546, 566, 587, 608, 631, 654, 678, 703, 728, 755, 783, 811, 841, 872, 904, 23 | }; 24 | 25 | uint32_t g_chorus_rate_table[] = { 26 | 16, 17, 18, 19, 21, 22, 23, 25, 26, 28, 29, 31, 33, 35, 37, 39, 27 | 42, 44, 47, 50, 53, 56, 59, 63, 66, 70, 75, 79, 84, 89, 94, 100, 28 | 106, 112, 119, 126, 133, 141, 150, 159, 168, 178, 189, 200, 212, 225, 238, 252, 29 | 267, 283, 300, 318, 337, 357, 378, 400, 424, 450, 476, 505, 535, 567, 600, 636, 30 | 674, 714, 756, 801, 849, 900, 953, 1010, 1070, 1134, 1201, 1272, 1348, 1428, 1513, 1603, 31 | 1699, 1800, 1907, 2020, 2140, 2268, 2402, 2545, 2697, 2857, 3027, 3207, 3398, 3600, 3814, 4041, 32 | 4281, 4536, 4805, 5091, 5394, 5715, 6055, 6415, 6796, 7200, 7629, 8082, 8563, 9072, 9611, 10183, 33 | 10789, 11430, 12110, 12830, 13593, 14401, 15258, 16165, 17126, 18144, 19223, 20367, 21578, 22861, 24220, 25660, 34 | }; 35 | 36 | -------------------------------------------------------------------------------- /Digital-Synth-PRA32-U/pra32-u-eg-table.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | int32_t g_eg_attack_release_coef_table[] = { 4 | 804397487, 820689725, 836147100, 850792705, 864651831, 877751464, 890119826, 901785979, 5 | 912779478, 923130072, 932867450, 942021023, 950619743, 958691961, 966265301, 973366570, 6 | 980021685, 986255615, 992092351, 997554878, 1002665167, 1007444172, 1011911839, 1016087120, 7 | 1019987994, 1023631489, 1027033714, 1030209886, 1033174365, 1035940689, 1038521605, 1040929109, 8 | 1043174479, 1045268307, 1047220540, 1049040508, 1050736961, 1052318097, 1053791595, 1055164644, 9 | 1056443973, 1057635873, 1058746230, 1059780544, 1060743956, 1061641267, 1062476961, 1063255225, 10 | 1063979968, 1064654838, 1065283237, 1065868341, 1066413111, 1066920308, 1067392509, 1067832114, 11 | 1068241360, 1068622334, 1068976980, 1069307109, 1069614410, 1069900455, 1070166709, 1070414537, 12 | 1070645210, 1070859913, 1071059748, 1071245742, 1071418853, 1071579969, 1071729922, 1071869482, 13 | 1071999369, 1072120253, 1072232756, 1072337459, 1072434902, 1072525588, 1072609984, 1072688527, 14 | 1072761622, 1072829646, 1072892952, 1072951865, 1073006692, 1073057714, 1073105197, 1073149384, 15 | 1073190505, 1073228773, 1073264385, 1073297526, 1073328367, 1073357067, 1073383776, 1073408630, 16 | 1073431760, 1073453284, 1073473314, 1073491954, 1073509300, 1073525442, 1073540464, 1073554443, 17 | 1073567451, 1073579557, 1073590822, 1073601305, 1073611060, 1073620138, 1073628586, 1073636448, 18 | 1073643763, 1073650571, 1073656907, 1073662802, 1073668288, 1073673393, 1073678144, 1073682565, 19 | 1073686679, 1073690508, 1073694071, 1073697386, 1073700471, 1073703342, 1073706014, 1073708500, 20 | 1073710814, 1073712967, 1073714970, 1073716834, 1073718569, 1073720184, 1073721686, 1073723084, 21 | 1073724385, 1073725596, 1073726723, 1073727771, 1073728747, 1073729655, 1073730500, 1073731286, 22 | }; 23 | 24 | int32_t g_eg_decay_coef_table[] = { 25 | 804397487, 820689725, 836147100, 850792705, 864651831, 877751464, 890119826, 901785979, 26 | 912779478, 923130072, 932867450, 942021023, 950619743, 958691961, 966265301, 973366570, 27 | 980021685, 986255615, 992092351, 997554878, 1002665167, 1007444172, 1011911839, 1016087120, 28 | 1019987994, 1023631489, 1027033714, 1030209886, 1033174365, 1035940689, 1038521605, 1040929109, 29 | 1043174479, 1045268307, 1047220540, 1049040508, 1050736961, 1052318097, 1053791595, 1055164644, 30 | 1056443973, 1057635873, 1058746230, 1059780544, 1060743956, 1061641267, 1062476961, 1063255225, 31 | 1063979968, 1064654838, 1065283237, 1065868341, 1066413111, 1066920308, 1067392509, 1067832114, 32 | 1068241360, 1068622334, 1068976980, 1069307109, 1069614410, 1069900455, 1070166709, 1070414537, 33 | 1070645210, 1070859913, 1071059748, 1071245742, 1071418853, 1071579969, 1071729922, 1071869482, 34 | 1071999369, 1072120253, 1072232756, 1072337459, 1072434902, 1072525588, 1072609984, 1072688527, 35 | 1072761622, 1072829646, 1072892952, 1072951865, 1073006692, 1073057714, 1073105197, 1073149384, 36 | 1073190505, 1073228773, 1073264385, 1073297526, 1073328367, 1073357067, 1073383776, 1073408630, 37 | 1073431760, 1073453284, 1073473314, 1073491954, 1073509300, 1073525442, 1073540464, 1073554443, 38 | 1073567451, 1073579557, 1073590822, 1073601305, 1073611060, 1073620138, 1073628586, 1073636448, 39 | 1073643763, 1073650571, 1073656907, 1073662802, 1073668288, 1073673393, 1073678144, 1073682565, 40 | 1073686679, 1073690508, 1073694071, 1073697386, 1073700471, 1073703342, 1073706014, 1073741824, 41 | }; 42 | 43 | -------------------------------------------------------------------------------- /Digital-Synth-PRA32-U/pra32-u-eg.h: -------------------------------------------------------------------------------- 1 | // refs https://www.g200kg.com/archives/2012/10/adsr.html 2 | // refs https://www.g200kg.com/archives/2012/10/adsr2.html 3 | // refs https://www.g200kg.com/archives/2020/07/adsr-1.html 4 | 5 | #pragma once 6 | 7 | #include "pra32-u-common.h" 8 | #include "pra32-u-eg-table.h" 9 | 10 | class PRA32_U_EG { 11 | static const uint8_t STATE_ATTACK = 0; 12 | static const uint8_t STATE_SUSTAIN = 1; 13 | static const uint8_t STATE_IDLE = 2; 14 | 15 | uint8_t m_state; 16 | int32_t m_level; 17 | int16_t m_level_out; 18 | int32_t m_attack_coef; 19 | int32_t m_decay_coef; 20 | int32_t m_sustain; 21 | int32_t m_release_coef; 22 | uint8_t m_velocity_sensitivity; 23 | uint8_t m_note_on_velocity; 24 | int32_t m_attack_level; 25 | int32_t m_sustain_level; 26 | 27 | public: 28 | PRA32_U_EG() 29 | : m_state() 30 | , m_level() 31 | , m_level_out() 32 | , m_attack_coef() 33 | , m_decay_coef() 34 | , m_sustain() 35 | , m_release_coef() 36 | , m_velocity_sensitivity() 37 | , m_note_on_velocity() 38 | , m_attack_level() 39 | , m_sustain_level() 40 | { 41 | m_state = STATE_IDLE; 42 | set_attack(0); 43 | set_decay(0); 44 | set_sustain(127); 45 | set_release(0); 46 | } 47 | 48 | INLINE void set_attack(uint8_t controller_value) { 49 | m_attack_coef = g_eg_attack_release_coef_table[controller_value + 16]; 50 | } 51 | 52 | INLINE void set_decay(uint8_t controller_value) { 53 | m_decay_coef = g_eg_decay_coef_table[controller_value]; 54 | } 55 | 56 | INLINE void set_sustain(uint8_t controller_value) { 57 | m_sustain = (controller_value + 1) >> 1; 58 | } 59 | 60 | INLINE void set_release(uint8_t controller_value) { 61 | m_release_coef = g_eg_attack_release_coef_table[controller_value]; 62 | } 63 | 64 | INLINE void set_velocity_sensitivity(uint8_t controller_value) { 65 | m_velocity_sensitivity = (controller_value + 1) >> 1; 66 | } 67 | 68 | INLINE void note_on(uint8_t velocity) { 69 | if (velocity <= 127) { 70 | m_note_on_velocity = velocity; 71 | } 72 | 73 | m_attack_level = ((((m_note_on_velocity * m_velocity_sensitivity) + (127 * (64 - m_velocity_sensitivity))) * 16384) / 127) << 10; 74 | m_sustain_level = (m_attack_level >> 6) * m_sustain; 75 | m_state = STATE_ATTACK; 76 | } 77 | 78 | INLINE void note_off() { 79 | m_state = STATE_IDLE; 80 | m_note_on_velocity = 0; 81 | } 82 | 83 | INLINE int16_t get_output() { 84 | return m_level_out; 85 | } 86 | 87 | INLINE void process_at_low_rate() { 88 | #if 1 89 | switch (m_state) { 90 | case STATE_ATTACK: 91 | if (m_level >= EG_LEVEL_MAX) { 92 | m_level = EG_LEVEL_MAX; 93 | m_state = STATE_SUSTAIN; 94 | } else if (m_level >= m_attack_level) { 95 | m_state = STATE_SUSTAIN; 96 | } else { 97 | m_level = ((m_attack_level - 1) << 1) - (mul_s32_s32_h32((((m_attack_level - 1) << 1) - m_level), m_attack_coef) << 2); 98 | } 99 | break; 100 | 101 | case STATE_SUSTAIN: 102 | { 103 | // effective_sustain = min(m_sustain_level, m_level) 104 | int32_t effective_sustain = m_sustain_level - m_level; 105 | effective_sustain = (effective_sustain < 0) * effective_sustain + m_level; 106 | 107 | m_level = effective_sustain + (mul_s32_s32_h32((m_level - effective_sustain), m_decay_coef) << 2); 108 | } 109 | break; 110 | 111 | case STATE_IDLE: 112 | m_level = mul_s32_s32_h32(m_level, m_release_coef) << 2; 113 | break; 114 | } 115 | 116 | m_level_out = m_level >> 16; 117 | #endif 118 | } 119 | }; 120 | -------------------------------------------------------------------------------- /pra32-u-prog-preset.json: -------------------------------------------------------------------------------- 1 | { 2 | "_version " : "PRA32-U Editor v3.2.0", 3 | "_comment " : " Current #8 #9 #10 #11 #12 #13 #14 #15 ", 4 | "OSC_1_WAVE " : [ [127], [127, 127, 127, 127, 127, 127, 127, 0 ] ], 5 | "OSC_1_SHAPE " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 6 | "OSC_1_MORPH " : [ [64 ], [64 , 64 , 64 , 64 , 64 , 0 , 0 , 0 ] ], 7 | "MIXER_SUB_OSC " : [ [127], [127, 64 , 64 , 64 , 127, 127, 64 , 64 ] ], 8 | 9 | "OSC_2_WAVE " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 10 | "OSC_2_COARSE " : [ [64 ], [64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 ] ], 11 | "OSC_2_PITCH " : [ [109], [109, 72 , 72 , 72 , 72 , 72 , 72 , 72 ] ], 12 | "MIXER_OSC_MIX " : [ [64 ], [64 , 64 , 64 , 64 , 64 , 64 , 64 , 0 ] ], 13 | 14 | "FILTER_CUTOFF " : [ [115], [115, 115, 91 , 103, 55 , 115, 67 , 127] ], 15 | "FILTER_RESO " : [ [64 ], [64 , 0 , 32 , 32 , 64 , 64 , 32 , 0 ] ], 16 | "FILTER_EG_AMT " : [ [64 ], [64 , 64 , 88 , 16 , 112, 64 , 64 , 64 ] ], 17 | "FILTER_KEY_TRK " : [ [64 ], [64 , 64 , 64 , 64 , 64 , 64 , 64 , 0 ] ], 18 | 19 | "EG_ATTACK " : [ [0 ], [0 , 64 , 48 , 96 , 0 , 0 , 0 , 0 ] ], 20 | "EG_DECAY " : [ [0 ], [0 , 0 , 80 , 96 , 96 , 0 , 100, 0 ] ], 21 | "EG_SUSTAIN " : [ [127], [127, 127, 0 , 0 , 0 , 127, 0 , 127] ], 22 | "EG_RELEASE " : [ [0 ], [0 , 64 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 23 | 24 | "EG_OSC_AMT " : [ [64 ], [64 , 64 , 72 , 64 , 64 , 64 , 96 , 64 ] ], 25 | "EG_OSC_DST " : [ [0 ], [0 , 0 , 64 , 0 , 0 , 0 , 127, 0 ] ], 26 | "VOICE_MODE " : [ [127], [127, 0 , 0 , 0 , 75 , 127, 0 , 0 ] ], 27 | "PORTAMENTO " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 28 | 29 | "LFO_WAVE " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 30 | "LFO_RATE " : [ [80 ], [80 , 80 , 80 , 80 , 80 , 80 , 80 , 80 ] ], 31 | "LFO_DEPTH " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 127, 0 , 0 ] ], 32 | "LFO_FADE_TIME " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 33 | 34 | "LFO_OSC_AMT " : [ [96 ], [96 , 96 , 64 , 64 , 64 , 96 , 64 , 64 ] ], 35 | "LFO_OSC_DST " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 127, 0 , 0 ] ], 36 | "LFO_FILTER_AMT " : [ [64 ], [64 , 64 , 88 , 88 , 88 , 64 , 88 , 64 ] ], 37 | "AMP_GAIN " : [ [90 ], [90 , 90 , 90 , 90 , 90 , 90 , 127, 90 ] ], 38 | 39 | "AMP_ATTACK " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 40 | "AMP_DECAY " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 41 | "AMP_SUSTAIN " : [ [127], [127, 127, 127, 127, 127, 127, 127, 127] ], 42 | "AMP_RELEASE " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 43 | 44 | "FILTER_MODE " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 45 | "EG_AMP_MOD " : [ [127], [127, 127, 0 , 0 , 127, 127, 127, 0 ] ], 46 | "REL_EQ_DECAY " : [ [0 ], [0 , 0 , 0 , 0 , 127, 0 , 0 , 0 ] ], 47 | "P_BEND_RANGE " : [ [12 ], [12 , 12 , 12 , 12 , 12 , 12 , 12 , 12 ] ], 48 | 49 | "BTH_FILTER_AMT " : [ [64 ], [64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 ] ], 50 | "BTH_AMP_MOD " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 51 | "EG_VEL_SENS " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 52 | "AMP_VEL_SENS " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 53 | 54 | "VOICE_ASGN_MODE" : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 55 | 56 | 57 | 58 | 59 | "CHORUS_MIX " : [ [64 ], [64 , 127, 127, 127, 64 , 64 , 127, 0 ] ], 60 | "CHORUS_RATE " : [ [64 ], [64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 ] ], 61 | "CHORUS_DEPTH " : [ [64 ], [64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 ] ], 62 | 63 | 64 | "DELAY_FEEDBACK " : [ [64 ], [64 , 64 , 64 , 64 , 64 , 64 , 64 , 0 ] ], 65 | "DELAY_TIME " : [ [93 ], [93 , 93 , 93 , 93 , 93 , 93 , 93 , 93 ] ], 66 | "DELAY_MODE " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 67 | 68 | "_end " : "" 69 | } 70 | -------------------------------------------------------------------------------- /pra32-u-prog-sample-saw-pulse.json: -------------------------------------------------------------------------------- 1 | { 2 | "_version " : "PRA32-U Editor v3.2.0", 3 | "_comment " : " Current #8 #9 #10 #11 #12 #13 #14 #15 ", 4 | "OSC_1_WAVE " : [ [127], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 5 | "OSC_1_SHAPE " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 6 | "OSC_1_MORPH " : [ [64 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 7 | "MIXER_SUB_OSC " : [ [64 ], [64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 ] ], 8 | 9 | "OSC_2_WAVE " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 10 | "OSC_2_COARSE " : [ [64 ], [64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 ] ], 11 | "OSC_2_PITCH " : [ [72 ], [72 , 72 , 72 , 72 , 72 , 72 , 72 , 72 ] ], 12 | "MIXER_OSC_MIX " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 13 | 14 | "FILTER_CUTOFF " : [ [127], [127, 127, 127, 127, 127, 127, 127, 127] ], 15 | "FILTER_RESO " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 16 | "FILTER_EG_AMT " : [ [64 ], [64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 ] ], 17 | "FILTER_KEY_TRK " : [ [64 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 18 | 19 | "EG_ATTACK " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 20 | "EG_DECAY " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 21 | "EG_SUSTAIN " : [ [127], [127, 127, 127, 127, 127, 127, 127, 127] ], 22 | "EG_RELEASE " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 23 | 24 | "EG_OSC_AMT " : [ [64 ], [64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 ] ], 25 | "EG_OSC_DST " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 26 | "VOICE_MODE " : [ [127], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 27 | "PORTAMENTO " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 28 | 29 | "LFO_WAVE " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 30 | "LFO_RATE " : [ [80 ], [80 , 80 , 80 , 80 , 80 , 80 , 80 , 80 ] ], 31 | "LFO_DEPTH " : [ [127], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 32 | "LFO_FADE_TIME " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 33 | 34 | "LFO_OSC_AMT " : [ [64 ], [64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 ] ], 35 | "LFO_OSC_DST " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 36 | "LFO_FILTER_AMT " : [ [64 ], [64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 ] ], 37 | "AMP_GAIN " : [ [90 ], [90 , 90 , 90 , 90 , 90 , 90 , 90 , 90 ] ], 38 | 39 | "AMP_ATTACK " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 40 | "AMP_DECAY " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 41 | "AMP_SUSTAIN " : [ [127], [127, 127, 127, 127, 127, 127, 127, 127] ], 42 | "AMP_RELEASE " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 43 | 44 | "FILTER_MODE " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 45 | "EG_AMP_MOD " : [ [127], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 46 | "REL_EQ_DECAY " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 47 | "P_BEND_RANGE " : [ [12 ], [12 , 12 , 12 , 12 , 12 , 12 , 12 , 12 ] ], 48 | 49 | "BTH_FILTER_AMT " : [ [64 ], [64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 ] ], 50 | "BTH_AMP_MOD " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 51 | "EG_VEL_SENS " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 52 | "AMP_VEL_SENS " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 53 | 54 | "VOICE_ASGN_MODE" : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 55 | 56 | 57 | 58 | 59 | "CHORUS_MIX " : [ [64 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 60 | "CHORUS_RATE " : [ [64 ], [64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 ] ], 61 | "CHORUS_DEPTH " : [ [64 ], [64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 ] ], 62 | 63 | 64 | "DELAY_FEEDBACK " : [ [64 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 65 | "DELAY_TIME " : [ [93 ], [93 , 93 , 93 , 93 , 93 , 93 , 93 , 93 ] ], 66 | "DELAY_MODE " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 67 | 68 | "_end " : "" 69 | } 70 | -------------------------------------------------------------------------------- /pra32-u-prog-sample-sine-pm-piano.json: -------------------------------------------------------------------------------- 1 | { 2 | "_version " : "PRA32-U Editor v3.2.0", 3 | "_comment " : " Current #8 #9 #10 #11 #12 #13 #14 #15 ", 4 | "OSC_1_WAVE " : [ [25 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 5 | "OSC_1_SHAPE " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 6 | "OSC_1_MORPH " : [ [108], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 7 | "MIXER_SUB_OSC " : [ [64 ], [64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 ] ], 8 | 9 | "OSC_2_WAVE " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 10 | "OSC_2_COARSE " : [ [64 ], [64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 ] ], 11 | "OSC_2_PITCH " : [ [72 ], [72 , 72 , 72 , 72 , 72 , 72 , 72 , 72 ] ], 12 | "MIXER_OSC_MIX " : [ [64 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 13 | 14 | "FILTER_CUTOFF " : [ [79 ], [127, 127, 127, 127, 127, 127, 127, 127] ], 15 | "FILTER_RESO " : [ [32 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 16 | "FILTER_EG_AMT " : [ [64 ], [64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 ] ], 17 | "FILTER_KEY_TRK " : [ [64 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 18 | 19 | "EG_ATTACK " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 20 | "EG_DECAY " : [ [100], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 21 | "EG_SUSTAIN " : [ [0 ], [127, 127, 127, 127, 127, 127, 127, 127] ], 22 | "EG_RELEASE " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 23 | 24 | "EG_OSC_AMT " : [ [72 ], [64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 ] ], 25 | "EG_OSC_DST " : [ [127], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 26 | "VOICE_MODE " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 27 | "PORTAMENTO " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 28 | 29 | "LFO_WAVE " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 30 | "LFO_RATE " : [ [80 ], [80 , 80 , 80 , 80 , 80 , 80 , 80 , 80 ] ], 31 | "LFO_DEPTH " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 32 | "LFO_FADE_TIME " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 33 | 34 | "LFO_OSC_AMT " : [ [64 ], [64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 ] ], 35 | "LFO_OSC_DST " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 36 | "LFO_FILTER_AMT " : [ [88 ], [64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 ] ], 37 | "AMP_GAIN " : [ [127], [90 , 90 , 90 , 90 , 90 , 90 , 90 , 90 ] ], 38 | 39 | "AMP_ATTACK " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 40 | "AMP_DECAY " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 41 | "AMP_SUSTAIN " : [ [127], [127, 127, 127, 127, 127, 127, 127, 127] ], 42 | "AMP_RELEASE " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 43 | 44 | "FILTER_MODE " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 45 | "EG_AMP_MOD " : [ [127], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 46 | "REL_EQ_DECAY " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 47 | "P_BEND_RANGE " : [ [12 ], [12 , 12 , 12 , 12 , 12 , 12 , 12 , 12 ] ], 48 | 49 | "BTH_FILTER_AMT " : [ [64 ], [64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 ] ], 50 | "BTH_AMP_MOD " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 51 | "EG_VEL_SENS " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 52 | "AMP_VEL_SENS " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 53 | 54 | "VOICE_ASGN_MODE" : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 55 | 56 | 57 | 58 | 59 | "CHORUS_MIX " : [ [127], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 60 | "CHORUS_RATE " : [ [64 ], [64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 ] ], 61 | "CHORUS_DEPTH " : [ [64 ], [64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 ] ], 62 | 63 | 64 | "DELAY_FEEDBACK " : [ [64 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 65 | "DELAY_TIME " : [ [93 ], [93 , 93 , 93 , 93 , 93 , 93 , 93 , 93 ] ], 66 | "DELAY_MODE " : [ [0 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ], 67 | 68 | "_end " : "" 69 | } 70 | -------------------------------------------------------------------------------- /pra32-u-midi-in.h: -------------------------------------------------------------------------------- 1 | // refs https://www.midi.org/specifications/item/the-midi-1-0-specification 2 | // refs http://amei.or.jp/midistandardcommittee/MIDIspcj.html 3 | 4 | #pragma once 5 | 6 | #include "./Digital-Synth-PRA32-U/pra32-u-common.h" 7 | #include "./Digital-Synth-PRA32-U/pra32-u-synth.h" 8 | 9 | class PRA32_U_MIDIIn { 10 | PRA32_U_Synth* m_synth; 11 | 12 | uint8_t m_system_exclusive; 13 | uint8_t m_system_data_remaining; 14 | uint8_t m_running_status; 15 | uint8_t m_first_data; 16 | 17 | public: 18 | PRA32_U_MIDIIn() 19 | : m_synth() 20 | , m_system_exclusive() 21 | , m_system_data_remaining() 22 | , m_running_status() 23 | , m_first_data() 24 | {} 25 | 26 | INLINE void open(PRA32_U_Synth& synth) { 27 | m_synth = &synth; 28 | m_running_status = STATUS_BYTE_INVALID; 29 | m_first_data = DATA_BYTE_INVALID; 30 | } 31 | 32 | INLINE void receive_midi_byte(uint8_t b) { 33 | if (is_data_byte(b)) { 34 | if (m_system_exclusive) { 35 | // do nothing 36 | } else if (m_system_data_remaining != 0) { 37 | --m_system_data_remaining; 38 | } else if (m_running_status == (NOTE_ON | PRA32_U_MIDI_CH)) { 39 | if (!is_data_byte(m_first_data)) { 40 | m_first_data = b; 41 | } else if (b == 0) { 42 | m_synth->note_off(m_first_data); 43 | m_first_data = DATA_BYTE_INVALID; 44 | } else { 45 | m_synth->note_on(m_first_data, b); 46 | m_first_data = DATA_BYTE_INVALID; 47 | } 48 | } else if (m_running_status == (NOTE_OFF | PRA32_U_MIDI_CH)) { 49 | if (!is_data_byte(m_first_data)) { 50 | m_first_data = b; 51 | } else { 52 | m_synth->note_off(m_first_data); 53 | m_first_data = DATA_BYTE_INVALID; 54 | } 55 | } else if (m_running_status == (CONTROL_CHANGE | PRA32_U_MIDI_CH)) { 56 | if (!is_data_byte(m_first_data)) { 57 | m_first_data = b; 58 | } else { 59 | m_synth->control_change(m_first_data, b); 60 | m_first_data = DATA_BYTE_INVALID; 61 | } 62 | } else if (m_running_status == (PITCH_BEND | PRA32_U_MIDI_CH)) { 63 | if (!is_data_byte(m_first_data)) { 64 | m_first_data = b; 65 | } else { 66 | m_synth->pitch_bend(m_first_data, b); 67 | m_first_data = DATA_BYTE_INVALID; 68 | } 69 | } else if (m_running_status == (PROGRAM_CHANGE | PRA32_U_MIDI_CH)) { 70 | m_synth->program_change(b); 71 | } 72 | } else if (is_system_message(b)) { 73 | switch (b) { 74 | case SYSTEM_EXCLUSIVE: 75 | m_system_exclusive = true; 76 | m_running_status = STATUS_BYTE_INVALID; 77 | break; 78 | case EOX: 79 | case TUNE_REQUEST: 80 | case 0xF4: 81 | case 0xF5: 82 | m_system_exclusive = false; 83 | m_system_data_remaining = 0; 84 | m_running_status = STATUS_BYTE_INVALID; 85 | break; 86 | case TIME_CODE: 87 | case SONG_SELECT: 88 | m_system_exclusive = false; 89 | m_system_data_remaining = 1; 90 | m_running_status = STATUS_BYTE_INVALID; 91 | break; 92 | case SONG_POSITION: 93 | m_system_exclusive = false; 94 | m_system_data_remaining = 2; 95 | m_running_status = STATUS_BYTE_INVALID; 96 | break; 97 | } 98 | } else if (is_status_byte(b)) { 99 | m_system_exclusive = false; 100 | m_running_status = b; 101 | m_first_data = DATA_BYTE_INVALID; 102 | } 103 | } 104 | 105 | private: 106 | INLINE boolean is_real_time_message(uint8_t b) { 107 | return b >= REAL_TIME_MESSAGE_MIN; 108 | } 109 | 110 | INLINE boolean is_system_message(uint8_t b) { 111 | return b >= SYSTEM_MESSAGE_MIN; 112 | } 113 | 114 | INLINE boolean is_status_byte(uint8_t b) { 115 | return b >= STATUS_BYTE_MIN; 116 | } 117 | 118 | INLINE boolean is_data_byte(uint8_t b) { 119 | return b <= DATA_BYTE_MAX; 120 | } 121 | }; 122 | -------------------------------------------------------------------------------- /Digital-Synth-PRA32-U/pra32-u-delay-fx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pra32-u-common.h" 4 | 5 | class PRA32_U_DelayFx { 6 | static const uint16_t DELAY_BUFF_SIZE = 16384; 7 | 8 | int16_t m_delay_buff[2][DELAY_BUFF_SIZE]; 9 | uint16_t m_delay_wp[2]; 10 | 11 | uint8_t m_delay_feedback; 12 | uint8_t m_delay_feedback_effective; 13 | uint16_t m_delay_time; 14 | uint16_t m_delay_time_effective; 15 | uint8_t m_delay_mode; 16 | 17 | public: 18 | PRA32_U_DelayFx() 19 | : m_delay_buff() 20 | , m_delay_wp() 21 | 22 | , m_delay_feedback() 23 | , m_delay_feedback_effective() 24 | , m_delay_time() 25 | , m_delay_time_effective() 26 | , m_delay_mode() 27 | { 28 | m_delay_wp[0] = DELAY_BUFF_SIZE - 1; 29 | m_delay_wp[1] = DELAY_BUFF_SIZE - 1; 30 | 31 | set_delay_feedback(32 ); 32 | set_delay_time (93 ); 33 | 34 | m_delay_time_effective = m_delay_time; 35 | } 36 | 37 | INLINE void set_delay_feedback(uint8_t controller_value) { 38 | m_delay_feedback = controller_value; 39 | } 40 | 41 | INLINE void set_delay_time(uint8_t controller_value) { 42 | static uint16_t delay_time_table[128] = { 43 | 48, 96, 144, 192, 240, 288, 384, 480, 44 | 576, 672, 768, 864, 960, 1056, 1152, 1248, 45 | 1344, 1440, 1536, 1632, 1728, 1824, 1920, 2016, 46 | 2112, 2208, 2304, 2400, 2560, 2720, 2880, 3040, 47 | 3200, 3360, 3520, 3680, 3840, 4000, 4160, 4320, 48 | 4480, 4640, 4800, 4960, 5120, 5280, 5440, 5600, 49 | 5760, 5920, 6080, 6240, 6400, 6560, 6720, 6880, 50 | 7040, 7200, 7360, 7520, 7680, 7840, 8000, 8160, 51 | 8320, 8480, 8640, 8800, 8960, 9120, 9280, 9440, 52 | 9600, 9760, 9920, 10080, 10240, 10400, 10560, 10720, 53 | 10880, 11040, 11200, 11360, 11520, 11680, 11840, 12000, 54 | 12160, 12320, 12480, 12640, 12800, 12960, 13120, 13280, 55 | 13440, 13600, 13760, 13920, 14080, 14240, 14400, 14560, 56 | 14720, 14880, 15040, 15200, 15360, 15520, 15680, 15840, 57 | 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 58 | 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 59 | }; 60 | 61 | m_delay_time = delay_time_table[controller_value]; 62 | } 63 | 64 | INLINE void set_delay_mode(uint8_t controller_value) { 65 | m_delay_mode = controller_value; 66 | } 67 | 68 | INLINE void process_at_low_rate(uint8_t count) { 69 | static_cast(count); 70 | 71 | m_delay_feedback_effective += (m_delay_feedback_effective < m_delay_feedback); 72 | m_delay_feedback_effective -= (m_delay_feedback_effective > m_delay_feedback); 73 | 74 | m_delay_time_effective += (m_delay_time_effective < m_delay_time); 75 | m_delay_time_effective -= (m_delay_time_effective > m_delay_time); 76 | } 77 | 78 | INLINE int16_t process(int16_t left_input, int16_t right_input, int16_t& right_level) { 79 | int16_t left_delay = delay_buff_get<0>(m_delay_time_effective); 80 | int16_t right_delay = delay_buff_get<1>(m_delay_time_effective); 81 | 82 | int16_t left_feedback; 83 | int16_t right_feedback; 84 | 85 | #if 1 86 | if (m_delay_mode == 1 || m_delay_mode >= 64) { 87 | #else 88 | if (m_delay_mode >= 64) { 89 | #endif 90 | // Ping Pong Delay 91 | left_feedback = ((((left_input + right_input) >> 2) + right_delay) * m_delay_feedback_effective) / 256; 92 | right_feedback = (( left_delay ) * m_delay_feedback_effective) / 256; 93 | } else { 94 | // Stereo Delay 95 | left_feedback = ((((left_input >> 1) + left_delay ) * m_delay_feedback_effective) / 256); 96 | right_feedback = ((((right_input >> 1) + right_delay) * m_delay_feedback_effective) / 256); 97 | } 98 | 99 | delay_buff_push<0>(left_feedback); 100 | delay_buff_push<1>(right_feedback); 101 | 102 | int16_t left_output = (left_input >> 1) + left_delay; 103 | int16_t right_output = (right_input >> 1) + right_delay; 104 | 105 | right_level = right_output; 106 | return left_output; 107 | } 108 | 109 | private: 110 | template 111 | INLINE void delay_buff_push(int16_t audio_input) { 112 | m_delay_wp[N] = (m_delay_wp[N] + 1) & (DELAY_BUFF_SIZE - 1); 113 | m_delay_buff[N][m_delay_wp[N]] = audio_input; 114 | } 115 | 116 | template 117 | INLINE int16_t delay_buff_get(uint16_t sample_delay) { 118 | uint16_t delay_rp = (m_delay_wp[N] - sample_delay) & (DELAY_BUFF_SIZE - 1); 119 | return m_delay_buff[N][delay_rp]; 120 | } 121 | }; 122 | -------------------------------------------------------------------------------- /README-PRA32-U-with-Panel.md: -------------------------------------------------------------------------------- 1 | # Digital Synth PRA32-U with Panel v3.2.0 2 | 3 | - 2025-05-11 ISGK Instruments 4 | - 5 | 6 | 7 | ## PRA32-U with Panel (Optional) 8 | 9 | ![PRA32-U with Panel (Grove System)](./pra32-u-with-panel-grove-system.jpg) 10 | 11 | - Features 12 | - Edit and display parameters by panel operation 13 | - Playing by panel operation 14 | - Built-in monophonic 8-step sequencer 15 | - Panel and Step Sequencer Parameters 16 | - This option requires 3 SWs (tactile switches), 3 VRs (ADCs), and a monochrome 128x64 OLED Display based on SSD1306 series drivers 17 | - Tested with Pimoroni Pico Audio Pack, necobit電子 MIDI Unit for GROVE (optional), Long Leg 2x20 Pin Socket * 2, Seeed Studio's Grove Shield for Pi Pico, Buttons * 3, Rotary Angle Sensors * 3, and an OLED Display 0.96 inch 18 | - Uncomment out `//#define PRA32_U_USE_CONTROL_PANEL` in "Digital-Synth-PRA32-U.ino" and modify the constants 19 | - Inputs 20 | - SW0: Prev Key (Push to the previous page, Long press to the previous group) 21 | - SW1: Next Key (Push to the next page, Long press to the next group) 22 | - SW2: Play Key (Normal Mode: press to play, Sequencer Mode: push to start/stop) 23 | - VR0 (ADC0): Parameter A 24 | - VR1 (ADC1): Parameter B 25 | - VR2 (ADC2): Parameter C 26 | - Group A 27 | - Synth Parameters 28 | - Group B 29 | - Panel Parameters 30 | - Panel Play Mode [Nrm|Seq]: Normal Mode, Step Sequencer Mode 31 | - Panel MIDI Ch: Basic Channel 1-16 32 | - Panel Play Pitch: min 4 to max 124 33 | - Panel Play Velo (Velocity) 34 | - Panel Scale [Maj|Min|Mel|MaP|MiP|Chr]: Major, Natural Minor, Ascending Melodic Minor (Jazz Minor), Major Pentatonic, Minor Pentatonic, Chromatic (2 octaves) 35 | - Panel Pitch Ofst (Pitch Offset) [-|+]: Offset Panel Play Pitch and Seq Pitch 0-7 (min -60 to max +60) 36 | - For example, if Panel Scale is Maj and Panel Pitch Ofst is -25, the scale is G Mixolydian 37 | - For example, if Panel Scale is Maj and Panel Pitch Ofst is -15, the scale is A Aeolian (Natural Minor) 38 | - For example, if Panel Scale is Maj and Panel Pitch Ofst is +10, the scale is D Dorian 39 | - For example, if Panel Scale is Maj and Panel Pitch Ofst is +20, the scale is E Phrygian 40 | - For example, if Panel Scale is Maj and Panel Pitch Ofst is +25, the scale is F Lydian 41 | - Panel Transpose [-|+] 42 | - For example, if Panel Scale is Mel and Panel Transpose is -3, the scale is A Ascending Melodic Minor (Jazz Minor) 43 | - For example, if Panel Scale is Min and Panel Transpose is -3, the scale is A Natural Minor 44 | - For example, if Panel Scale is MiP and Panel Transpose is -3, the scale is A Minor Pentatonic 45 | - Step Sequencer Parameters 46 | - Seq Mode [Fwd|Rvs|Bnc]: Forward, Reverse, Bounce 47 | - Seq Num Steps (Number of Steps): 1-32 (current step mod 8 is used as the index for Seq Pitch and Seq Velo) 48 | - Seq On Steps: bit 0 is Step 1 On, ..., bit 6 is Step 7 On (Step 0 is always On) 49 | - Seq Act Steps (Active Steps): bit 0 is Step 1 Active, ..., bit 6 is Step 7 Active (Step 0 is always Active) 50 | - Seq Step Note [4|8|16]: Quarter Note, Eighth Note, Sixteenth Note 51 | - Seq Tempo: BPM 30-300 52 | - Seq Gate Time [1/6|2/6|3/6|4/6|5/6|6/6] 53 | - Seq Transpose [-|+] 54 | - Seq Clock Src [Int|Ext]: Internal, External (Rx MIDI Clock) 55 | - Seq Pitch 0-7: min 4 to max 124 56 | - Seq Velo 0-7 (Velocity 0-7) 57 | - Step Sequencer Function 58 | - Seq Rand Pitch (Randomize Pitch 0-7): Change the value from 0-63 [Rdy] to 64-127 [Exe] 59 | - Seq Rand Velo (Randomize Velo 0-7): Change the value from 0-63 [Rdy] to 64-127 [Exe] 60 | - Control Parameters 61 | - Modulation 62 | - Breath Controller 63 | - Sustain Pedal 64 | - Group C 65 | - Write Operations 66 | - Write Program 8-15, Write Panel Prms (Write Panel and Step Sequencer Parameters) 67 | - Change the value from 0-63 [Rdy] to 64-127 [Exe] to write to the flash 68 | - Group D 69 | - Read Operations 70 | - Read Program 1-7, 8-15, Read Panel Prms, Init Panel Prms 71 | - Change the value from 0-63 [Rdy] to 64-127 [Exe] to read from the flash 72 | - NOTE: UART MIDI control is recommended to prevent ADCs from being affected by USB MIDI communication noise 73 | 74 | 75 | ### Circuit Diagram 76 | 77 | ![Circuit Diagram](./pra32-u-with-panel-circuit-diagram.png) 78 | 79 | - This image was created with Fritzing. 80 | 81 | 82 | ### An Example of Construction by a Breadboard 83 | 84 | - Tested with a breadboard, GY-PCM5102 (PCM5102A I2S DAC Module), 3 SWs, 3 VRs, and a OLED Display 85 | - An connection between Raspberry Pico's Mute Off Pin and GY-PCM5102's XSMT is omitted 86 | 87 | ![PRA32-U with Panel (Bread Board)](./pra32-u-with-panel-breadboard.jpg) 88 | -------------------------------------------------------------------------------- /Digital-Synth-PRA32-U/pra32-u-lfo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pra32-u-common.h" 4 | #include "pra32-u-lfo-table.h" 5 | #include "pra32-u-osc-table.h" 6 | 7 | class PRA32_U_LFO { 8 | static const uint8_t LFO_WAVEFORM_TRIANGLE = 0; 9 | static const uint8_t LFO_WAVEFORM_SINE = 1; 10 | static const uint8_t LFO_WAVEFORM_SAW_DOWN = 2; 11 | static const uint8_t LFO_WAVEFORM_RANDOM = 3; 12 | static const uint8_t LFO_WAVEFORM_SQUARE = 4; 13 | 14 | static const uint8_t LFO_FADE_COEF_OFF = 1; 15 | 16 | static const uint8_t LFO_FADE_LEVEL_MAX = 128; 17 | 18 | uint32_t m_lfo_phase; 19 | int16_t m_lfo_wave_level; 20 | int16_t m_lfo_level; 21 | uint32_t m_lfo_rate; 22 | uint8_t m_lfo_depth[2]; 23 | uint8_t m_lfo_waveform; 24 | uint16_t m_lfo_fade_coef; 25 | uint16_t m_lfo_fade_cnt; 26 | uint8_t m_lfo_fade_level; 27 | int16_t m_noise_int15; 28 | int16_t m_sampled_noise_int15; 29 | 30 | public: 31 | PRA32_U_LFO() 32 | : m_lfo_phase() 33 | , m_lfo_wave_level() 34 | , m_lfo_level() 35 | , m_lfo_rate() 36 | , m_lfo_depth() 37 | , m_lfo_waveform() 38 | , m_lfo_fade_coef() 39 | , m_lfo_fade_cnt() 40 | , m_lfo_fade_level() 41 | , m_noise_int15() 42 | , m_sampled_noise_int15() 43 | { 44 | m_lfo_waveform = LFO_WAVEFORM_TRIANGLE; 45 | m_lfo_fade_coef = LFO_FADE_COEF_OFF; 46 | m_lfo_fade_cnt = m_lfo_fade_coef; 47 | m_lfo_fade_level = LFO_FADE_LEVEL_MAX; 48 | m_noise_int15 = 0; 49 | m_sampled_noise_int15 = m_noise_int15; 50 | } 51 | 52 | INLINE void set_lfo_waveform(uint8_t controller_value) { 53 | static uint8_t lfo_waveform_table[6] = { 54 | LFO_WAVEFORM_TRIANGLE, 55 | LFO_WAVEFORM_SINE, 56 | LFO_WAVEFORM_SAW_DOWN, 57 | LFO_WAVEFORM_SAW_DOWN, 58 | LFO_WAVEFORM_RANDOM, 59 | LFO_WAVEFORM_SQUARE, 60 | }; 61 | 62 | volatile int32_t index = ((controller_value * 10) + 127) / 254; 63 | 64 | // index = min(index, 5) 65 | index = index - 5; 66 | index = (index < 0) * index + 5; 67 | 68 | m_lfo_waveform = lfo_waveform_table[index]; 69 | #if 1 70 | if (controller_value < 6) { 71 | m_lfo_waveform = lfo_waveform_table[controller_value]; 72 | } 73 | #endif 74 | } 75 | 76 | INLINE void set_lfo_rate(uint8_t controller_value) { 77 | m_lfo_rate = g_lfo_rate_table[controller_value]; 78 | } 79 | 80 | template 81 | INLINE void set_lfo_depth(uint8_t controller_value) { 82 | m_lfo_depth[N] = (controller_value + 1) >> 1; 83 | } 84 | 85 | INLINE void set_lfo_fade_time(uint8_t controller_value) { 86 | m_lfo_fade_coef = g_lfo_fade_coef_table[controller_value]; 87 | } 88 | 89 | INLINE void trigger_lfo() { 90 | if ( (m_lfo_waveform == LFO_WAVEFORM_SAW_DOWN) 91 | || (m_lfo_waveform == LFO_WAVEFORM_RANDOM) 92 | || (m_lfo_waveform == LFO_WAVEFORM_SQUARE)) { 93 | m_lfo_phase = 0x00FFFFFF; 94 | } 95 | 96 | if (m_lfo_fade_coef > LFO_FADE_COEF_OFF) { 97 | m_lfo_fade_level = 0; 98 | } 99 | } 100 | 101 | INLINE int16_t get_output() { 102 | return m_lfo_level; 103 | } 104 | 105 | INLINE void process_at_low_rate(uint8_t count, int16_t noise_int15) { 106 | static_cast(count); 107 | 108 | m_noise_int15 = noise_int15; 109 | update_lfo_level(); 110 | } 111 | 112 | private: 113 | INLINE int16_t get_lfo_wave_level(uint32_t phase) { 114 | int16_t level = 0; 115 | 116 | switch (m_lfo_waveform) { 117 | case LFO_WAVEFORM_TRIANGLE: 118 | level = static_cast(phase >> 8) >> 1; 119 | if (level < -(64 << 7)) { 120 | level = -(64 << 7) - (level + (64 << 7)); 121 | } else if (level < (64 << 7)) { 122 | // do nothing 123 | } else { 124 | level = (64 << 7) - (level - (64 << 7)); 125 | } 126 | break; 127 | case LFO_WAVEFORM_SINE: 128 | { 129 | uint16_t phase16 = phase >> 8; 130 | uint16_t curr_index = phase16 >> (16 - OSC_WAVE_TABLE_SAMPLES_BITS); 131 | uint16_t next_weight = phase16 & ((1 << (16 - OSC_WAVE_TABLE_SAMPLES_BITS)) - 1); 132 | int16_t curr_data = g_osc_sine_wave_table_h1[curr_index + 0]; 133 | int16_t next_data = g_osc_sine_wave_table_h1[curr_index + 1]; 134 | level = curr_data + (((next_data - curr_data) * next_weight) >> (16 - OSC_WAVE_TABLE_SAMPLES_BITS)); // lerp 135 | } 136 | break; 137 | case LFO_WAVEFORM_SAW_DOWN: 138 | { 139 | level = (64 << 7) - ((phase >> 8) >> 2); 140 | } 141 | break; 142 | case LFO_WAVEFORM_RANDOM: 143 | if (phase < m_lfo_rate) { 144 | m_sampled_noise_int15 = m_noise_int15; 145 | } 146 | level = m_sampled_noise_int15 >> 1; 147 | break; 148 | case LFO_WAVEFORM_SQUARE: 149 | if (phase < 0x800000) { 150 | level = 128 << 7; 151 | } else { 152 | level = 0; 153 | } 154 | break; 155 | } 156 | 157 | return level; 158 | } 159 | 160 | INLINE void update_lfo_level() { 161 | --m_lfo_fade_cnt; 162 | if (m_lfo_fade_cnt == 0) { 163 | m_lfo_fade_cnt = m_lfo_fade_coef; 164 | if (m_lfo_fade_level < LFO_FADE_LEVEL_MAX) { 165 | m_lfo_fade_level += 1; 166 | } 167 | } 168 | 169 | m_lfo_phase += m_lfo_rate; 170 | m_lfo_phase &= 0x00FFFFFF; 171 | m_lfo_wave_level = get_lfo_wave_level(m_lfo_phase); 172 | 173 | 174 | uint8_t lfo_depth = high_byte((m_lfo_depth[0] << 1) * m_lfo_fade_level) + m_lfo_depth[1]; 175 | if (lfo_depth > 64) { 176 | lfo_depth = 64; 177 | } 178 | lfo_depth <<= 1; 179 | 180 | m_lfo_level = (lfo_depth * m_lfo_wave_level) >> 7; 181 | } 182 | }; 183 | -------------------------------------------------------------------------------- /Digital-Synth-PRA32-U/pra32-u-chorus-fx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pra32-u-common.h" 4 | 5 | class PRA32_U_ChorusFx { 6 | static const uint16_t DELAY_BUFF_SIZE = 512; 7 | 8 | int16_t m_delay_buff[DELAY_BUFF_SIZE]; 9 | uint16_t m_delay_wp; 10 | 11 | uint16_t m_chorus_mix_control; 12 | uint16_t m_chorus_mix_control_effective; 13 | uint16_t m_chorus_depth_control; 14 | uint16_t m_chorus_depth_control_effective; 15 | uint32_t m_chorus_rate_control; 16 | uint16_t m_chorus_delay_time_control; 17 | uint16_t m_chorus_delay_time_control_effective; 18 | uint8_t m_chorus_depth_control_actual; 19 | uint32_t m_chorus_lfo_phase; 20 | uint16_t m_chorus_delay_time[2]; 21 | 22 | public: 23 | PRA32_U_ChorusFx() 24 | : m_delay_buff() 25 | , m_delay_wp() 26 | 27 | , m_chorus_mix_control() 28 | , m_chorus_mix_control_effective() 29 | , m_chorus_depth_control() 30 | , m_chorus_depth_control_effective() 31 | , m_chorus_rate_control() 32 | , m_chorus_delay_time_control() 33 | , m_chorus_delay_time_control_effective() 34 | , m_chorus_lfo_phase() 35 | , m_chorus_delay_time() 36 | { 37 | m_delay_wp = DELAY_BUFF_SIZE - 1; 38 | 39 | set_chorus_depth (64 ); 40 | set_chorus_rate (64 ); 41 | set_chorus_delay_time(64 ); 42 | 43 | m_chorus_depth_control_effective = 64 << 6; 44 | m_chorus_delay_time_control_effective = 64 << 6; 45 | } 46 | 47 | INLINE void set_chorus_depth(uint8_t controller_value) { 48 | if (controller_value < 126) { 49 | m_chorus_depth_control = controller_value << 6; 50 | } else { 51 | m_chorus_depth_control = 126 << 6; 52 | } 53 | } 54 | 55 | INLINE void set_chorus_rate(uint8_t controller_value) { 56 | m_chorus_rate_control = g_chorus_rate_table[controller_value]; 57 | } 58 | 59 | INLINE void set_chorus_delay_time(uint8_t controller_value) { 60 | m_chorus_delay_time_control = controller_value << 6; 61 | } 62 | 63 | INLINE void set_chorus_mix(uint8_t controller_value) { 64 | m_chorus_mix_control = (controller_value + 1) >> 1; 65 | } 66 | 67 | template 68 | INLINE uint16_t get_chorus_delay_time() { 69 | return m_chorus_delay_time[N]; 70 | } 71 | 72 | INLINE void process_at_low_rate(uint8_t count) { 73 | #if 1 74 | static_cast(count); 75 | 76 | m_chorus_mix_control_effective += (m_chorus_mix_control_effective < m_chorus_mix_control); 77 | m_chorus_mix_control_effective -= (m_chorus_mix_control_effective > m_chorus_mix_control); 78 | 79 | m_chorus_depth_control_effective += (m_chorus_depth_control_effective < m_chorus_depth_control); 80 | m_chorus_depth_control_effective -= (m_chorus_depth_control_effective > m_chorus_depth_control); 81 | 82 | m_chorus_delay_time_control_effective += (m_chorus_delay_time_control_effective < m_chorus_delay_time_control); 83 | m_chorus_delay_time_control_effective -= (m_chorus_delay_time_control_effective > m_chorus_delay_time_control); 84 | 85 | uint16_t chorus_depth_control_effective_limited; 86 | if (m_chorus_delay_time_control_effective < (64 << 6)) { 87 | if (m_chorus_depth_control_effective > (m_chorus_delay_time_control_effective << 1)) { 88 | chorus_depth_control_effective_limited = (m_chorus_delay_time_control_effective << 1); 89 | } else { 90 | chorus_depth_control_effective_limited = m_chorus_depth_control_effective; 91 | } 92 | } else { 93 | if (m_chorus_depth_control_effective > (((127 << 6) - m_chorus_delay_time_control_effective) << 1)) { 94 | chorus_depth_control_effective_limited = (((127 << 6) - m_chorus_delay_time_control_effective) << 1); 95 | } else { 96 | chorus_depth_control_effective_limited = m_chorus_depth_control_effective; 97 | } 98 | } 99 | 100 | m_chorus_lfo_phase += m_chorus_rate_control; 101 | m_chorus_lfo_phase &= 0x00FFFFFF; 102 | 103 | int16_t chorus_lfo_wave_level = get_chorus_lfo_wave_level(m_chorus_lfo_phase); 104 | 105 | int16_t chorus_lfo_level = (chorus_lfo_wave_level * chorus_depth_control_effective_limited) >> 14; 106 | 107 | m_chorus_delay_time[0] = m_chorus_delay_time_control_effective - chorus_lfo_level; 108 | m_chorus_delay_time[1] = m_chorus_delay_time_control_effective + chorus_lfo_level; 109 | #endif 110 | } 111 | 112 | INLINE int16_t process(int16_t dir_sample, int16_t& right_level) { 113 | int16_t eff_sample_0 = delay_buff_get(get_chorus_delay_time<0>()); 114 | int16_t eff_sample_1 = delay_buff_get(get_chorus_delay_time<1>()); 115 | delay_buff_push(dir_sample); 116 | 117 | right_level = (((dir_sample * (128 - m_chorus_mix_control_effective)) + (eff_sample_1 * m_chorus_mix_control_effective))) >> 7; 118 | return (((dir_sample * (128 - m_chorus_mix_control_effective)) + (eff_sample_0 * m_chorus_mix_control_effective))) >> 7; 119 | } 120 | 121 | private: 122 | INLINE void delay_buff_push(int16_t audio_input) { 123 | m_delay_wp = (m_delay_wp + 1) & (DELAY_BUFF_SIZE - 1); 124 | m_delay_buff[m_delay_wp] = audio_input; 125 | } 126 | 127 | INLINE int16_t delay_buff_get(uint16_t sample_delay) { 128 | uint16_t curr_index = (m_delay_wp - (sample_delay >> 4)) & (DELAY_BUFF_SIZE - 1); 129 | uint16_t next_index = (curr_index - 1) & (DELAY_BUFF_SIZE - 1); 130 | uint16_t next_weight = (sample_delay & 0xF); 131 | int16_t curr_data = m_delay_buff[curr_index]; 132 | int16_t next_data = m_delay_buff[next_index]; 133 | 134 | // lerp 135 | int16_t result = curr_data + (((next_data - curr_data) * next_weight) >> 4); 136 | 137 | return result; 138 | } 139 | 140 | INLINE void delay_buff_attenuate() { 141 | for (uint16_t i = 0; i < DELAY_BUFF_SIZE; ++i) { 142 | m_delay_buff[i] = m_delay_buff[i] >> 1; 143 | } 144 | } 145 | 146 | INLINE int16_t get_chorus_lfo_wave_level(uint32_t phase) { 147 | int16_t triangle_wave_level = 0; 148 | phase = (phase >> 9); 149 | 150 | if (phase < 0x00004000) { 151 | triangle_wave_level = phase - (512 << 4); 152 | } else { 153 | triangle_wave_level = (1535 << 4) - phase; 154 | } 155 | 156 | return triangle_wave_level; 157 | } 158 | }; 159 | -------------------------------------------------------------------------------- /Digital-Synth-PRA32-U/pra32-u-generate-osc-table.rb: -------------------------------------------------------------------------------- 1 | require_relative 'pra32-u-constants' 2 | 3 | $file = File.open("pra32-u-osc-table.h", "w") 4 | 5 | $file.printf("#pragma once\n\n") 6 | 7 | def freq_from_note_number(note_number, pr = false) 8 | cent = (note_number * 100.0) - 6900.0 9 | hz = A4_FREQ * (2.0 ** (cent / 1200.0)) 10 | freq = (hz * (1 << OSC_PHASE_RESOLUTION_BITS) / SAMPLING_RATE).floor.to_i 11 | freq = freq + 1 if freq.even? 12 | if pr 13 | printf("[0]%3d, %+f, %d\n",note_number, 1.0 - freq.to_f * SAMPLING_RATE / (hz * (1 << OSC_PHASE_RESOLUTION_BITS)), freq) 14 | end 15 | return freq 16 | end 17 | 18 | $file.printf("uint32_t g_osc_freq_table[] = {\n ") 19 | (NOTE_NUMBER_MIN..NOTE_NUMBER_MAX).each do |note_number| 20 | freq = freq_from_note_number(note_number, true) 21 | 22 | $file.printf("0x%08X,", freq) 23 | if note_number == DATA_BYTE_MAX 24 | $file.printf("\n") 25 | elsif note_number % 12 == (12 - 1) 26 | $file.printf("\n ") 27 | else 28 | $file.printf(" ") 29 | end 30 | end 31 | $file.printf("};\n\n") 32 | 33 | max_tune_rate = -Float::INFINITY 34 | $file.printf("int16_t g_osc_tune_table[] = {\n ") 35 | (0..(1 << OSC_TUNE_TABLE_STEPS_BITS) - 1).each do |i| 36 | tune_rate = ((2.0 ** ((i - (1 << (OSC_TUNE_TABLE_STEPS_BITS - 1))) / (12.0 * (1 << OSC_TUNE_TABLE_STEPS_BITS)))) * 37 | (1 << OSC_TUNE_DENOMINATOR_BITS) / 1.0).round - 38 | (1 << OSC_TUNE_DENOMINATOR_BITS) / 1.0 39 | max_tune_rate = tune_rate if max_tune_rate < tune_rate 40 | 41 | $file.printf("%5d,", tune_rate) 42 | if i == (1 << OSC_TUNE_TABLE_STEPS_BITS) - 1 43 | $file.printf("\n") 44 | elsif i % 8 == 7 45 | $file.printf("\n ") 46 | else 47 | $file.printf(" ") 48 | end 49 | end 50 | $file.printf("};\n\n") 51 | 52 | def generate_osc_wave_table(name, last, amp) 53 | $file.printf("int16_t g_osc_#{name}_wave_table_h%d[] = {\n ", last) 54 | (0..(1 << OSC_WAVE_TABLE_SAMPLES_BITS)).each do |n| 55 | level = 0 56 | nn = n 57 | nn = 0 if n == (1 << OSC_WAVE_TABLE_SAMPLES_BITS) 58 | max = last 59 | (1..max).each do |k| 60 | level += yield(nn, k) 61 | end 62 | level *= amp 63 | level = (level * OSC_WAVE_TABLE_AMP).round.to_i 64 | $file.printf("%+6d,", level) 65 | if n == (1 << OSC_WAVE_TABLE_SAMPLES_BITS) 66 | $file.printf("\n") 67 | elsif n % 16 == 15 68 | $file.printf("\n ") 69 | else 70 | $file.printf(" ") 71 | end 72 | end 73 | $file.printf("};\n\n") 74 | end 75 | 76 | $osc_harmonics_restriction_table = [] 77 | 78 | (NOTE_NUMBER_MIN..NOTE_NUMBER_MAX).each do |note_number| 79 | correction = (max_tune_rate.to_f + (1 << OSC_TUNE_DENOMINATOR_BITS)) / (1 << OSC_TUNE_DENOMINATOR_BITS) 80 | freq = freq_from_note_number(((note_number + (3 - 1)) / 3) * 3) * correction 81 | freq = freq.floor 82 | bit = 1 83 | freq += bit 84 | $osc_harmonics_restriction_table << freq 85 | end 86 | 87 | OSC_DETUNE_CORRECRION = 1015 # Approx. 25 cents 88 | 89 | def last_harmonic(freq) 90 | last = (freq != 0) ? ((FREQUENCY_MAX * (1 << OSC_PHASE_RESOLUTION_BITS)) / 91 | (((freq * OSC_DETUNE_CORRECRION / 1000) + OSC_DETUNE_FREQ_MAX) * SAMPLING_RATE)) : 0 92 | last = 9 if last == 10 93 | last = 7 if last == 8 94 | last = 5 if last == 6 95 | last = 3 if last == 4 96 | last = [last, 127].min 97 | last 98 | end 99 | 100 | def generate_osc_wave_table_arrays 101 | $osc_harmonics_restriction_table. 102 | map { |freq| last_harmonic(freq) }.uniq.sort.reverse.each do |i| 103 | yield(i) 104 | end 105 | end 106 | 107 | generate_osc_wave_table_arrays do |last| 108 | generate_osc_wave_table("saw", last, 1.0) do |n, k| 109 | (2.0 / Math::PI) * Math.sin((2.0 * Math::PI) * 110 | (n.to_f / (1 << OSC_WAVE_TABLE_SAMPLES_BITS)) * k) / k 111 | end 112 | end 113 | 114 | generate_osc_wave_table_arrays do |last| 115 | generate_osc_wave_table("triangle", last, 1.0) do |n, k| 116 | if k % 4 == 1 117 | +(8.0 / (Math::PI * Math::PI)) * Math.sin((2.0 * Math::PI) * 118 | (n.to_f / (1 << OSC_WAVE_TABLE_SAMPLES_BITS)) * k) / (k * k) 119 | elsif k % 4 == 3 120 | -(8.0 / (Math::PI * Math::PI)) * Math.sin((2.0 * Math::PI) * 121 | (n.to_f / (1 << OSC_WAVE_TABLE_SAMPLES_BITS)) * k) / (k * k) 122 | else 123 | 0.0 124 | end 125 | end 126 | end 127 | 128 | generate_osc_wave_table_arrays do |last| 129 | generate_osc_wave_table("square", last, 1.0) do |n, k| 130 | if k % 2 == 1 131 | (4.0 / Math::PI) * Math.sin((2.0 * Math::PI) * 132 | (n.to_f / (1 << OSC_WAVE_TABLE_SAMPLES_BITS)) * k) / k 133 | else 134 | 0.0 135 | end 136 | end 137 | end 138 | 139 | generate_osc_wave_table("sine", 1, 1.0) do |n, k| 140 | Math.sin((2.0 * Math::PI) * (n.to_f / (1 << OSC_WAVE_TABLE_SAMPLES_BITS)) * k) 141 | end 142 | 143 | def generate_osc_wave_tables_array(name, last = 127) 144 | $file.printf("int16_t* g_osc_#{name}_wave_tables[] = {\n ") 145 | $osc_harmonics_restriction_table.each_with_index do |freq, idx| 146 | $file.printf("g_osc_#{name}_wave_table_h%-3d,", [last_harmonic(freq), last].min) 147 | if idx == DATA_BYTE_MAX 148 | $file.printf("\n") 149 | elsif idx % 3 == (3 - 1) 150 | $file.printf("\n ") 151 | else 152 | $file.printf(" ") 153 | end 154 | end 155 | $file.printf("};\n\n") 156 | end 157 | 158 | generate_osc_wave_tables_array("saw") 159 | generate_osc_wave_tables_array("triangle") 160 | generate_osc_wave_tables_array("square") 161 | generate_osc_wave_tables_array("sine", 1) 162 | 163 | $file.printf("int32_t g_portamento_coef_table[] = {\n ") 164 | (0..127).each do |i| 165 | time = i 166 | portamento_coef = (0.5 ** (1.0 / ((0.1 / 10.0) * (SAMPLING_RATE / 4) * (10.0 ** ((time - 64.0) / 32.0)))) * 0x40000000).round 167 | portamento_coef = 0 if time == 0 168 | 169 | $file.printf("%10d,", portamento_coef) 170 | if i == 127 171 | $file.printf("\n") 172 | elsif i % 8 == (8 - 1) 173 | $file.printf("\n ") 174 | else 175 | $file.printf(" ") 176 | end 177 | end 178 | $file.printf("};\n\n") 179 | 180 | $file.close 181 | -------------------------------------------------------------------------------- /Digital-Synth-PRA32-U/pra32-u-program-table.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | const uint8_t PROGRAM_NUMBER_DEFAULT = 8; 4 | 5 | // Preset #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 6 | const uint8_t g_preset_table_OSC_1_WAVE [] = {127, 127, 127, 127, 127, 127, 127, 0 , 127, 127, 127, 127, 127, 127, 127, 0 }; 7 | const uint8_t g_preset_table_OSC_1_SHAPE [] = {0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }; 8 | const uint8_t g_preset_table_OSC_1_MORPH [] = {64 , 64 , 64 , 64 , 64 , 0 , 0 , 0 , 64 , 64 , 64 , 64 , 64 , 0 , 0 , 0 }; 9 | const uint8_t g_preset_table_MIXER_SUB_OSC [] = {127, 64 , 64 , 64 , 127, 127, 64 , 64 , 127, 64 , 64 , 64 , 127, 127, 64 , 64 }; 10 | 11 | const uint8_t g_preset_table_OSC_2_WAVE [] = {0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }; 12 | const uint8_t g_preset_table_OSC_2_COARSE [] = {64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 }; 13 | const uint8_t g_preset_table_OSC_2_PITCH [] = {109, 72 , 72 , 72 , 72 , 72 , 72 , 72 , 109, 72 , 72 , 72 , 72 , 72 , 72 , 72 }; 14 | const uint8_t g_preset_table_MIXER_OSC_MIX [] = {64 , 64 , 64 , 64 , 64 , 64 , 64 , 0 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 0 }; 15 | 16 | const uint8_t g_preset_table_FILTER_CUTOFF [] = {115, 115, 91 , 103, 55 , 115, 67 , 127, 115, 115, 91 , 103, 55 , 115, 67 , 127}; 17 | const uint8_t g_preset_table_FILTER_RESO [] = {64 , 0 , 32 , 32 , 64 , 64 , 32 , 0 , 64 , 0 , 32 , 32 , 64 , 64 , 32 , 0 }; 18 | const uint8_t g_preset_table_FILTER_EG_AMT [] = {64 , 64 , 88 , 16 , 112, 64 , 64 , 64 , 64 , 64 , 88 , 16 , 112, 64 , 64 , 64 }; 19 | const uint8_t g_preset_table_FILTER_KEY_TRK [] = {64 , 64 , 64 , 64 , 64 , 64 , 64 , 0 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 0 }; 20 | 21 | const uint8_t g_preset_table_EG_ATTACK [] = {0 , 64 , 48 , 96 , 0 , 0 , 0 , 0 , 0 , 64 , 48 , 96 , 0 , 0 , 0 , 0 }; 22 | const uint8_t g_preset_table_EG_DECAY [] = {0 , 0 , 80 , 96 , 96 , 0 , 100, 0 , 0 , 0 , 80 , 96 , 96 , 0 , 100, 0 }; 23 | const uint8_t g_preset_table_EG_SUSTAIN [] = {127, 127, 0 , 0 , 0 , 127, 0 , 127, 127, 127, 0 , 0 , 0 , 127, 0 , 127}; 24 | const uint8_t g_preset_table_EG_RELEASE [] = {0 , 64 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 64 , 0 , 0 , 0 , 0 , 0 , 0 }; 25 | 26 | const uint8_t g_preset_table_EG_OSC_AMT [] = {64 , 64 , 72 , 64 , 64 , 64 , 96 , 64 , 64 , 64 , 72 , 64 , 64 , 64 , 96 , 64 }; 27 | const uint8_t g_preset_table_EG_OSC_DST [] = {0 , 0 , 64 , 0 , 0 , 0 , 127, 0 , 0 , 0 , 64 , 0 , 0 , 0 , 127, 0 }; 28 | const uint8_t g_preset_table_VOICE_MODE [] = {127, 0 , 0 , 0 , 75 , 127, 0 , 0 , 127, 0 , 0 , 0 , 75 , 127, 0 , 0 }; 29 | const uint8_t g_preset_table_PORTAMENTO [] = {0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }; 30 | 31 | const uint8_t g_preset_table_LFO_WAVE [] = {0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }; 32 | const uint8_t g_preset_table_LFO_RATE [] = {80 , 80 , 80 , 80 , 80 , 80 , 80 , 80 , 80 , 80 , 80 , 80 , 80 , 80 , 80 , 80 }; 33 | const uint8_t g_preset_table_LFO_DEPTH [] = {0 , 0 , 0 , 0 , 0 , 127, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 127, 0 , 0 }; 34 | const uint8_t g_preset_table_LFO_FADE_TIME [] = {0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }; 35 | 36 | const uint8_t g_preset_table_LFO_OSC_AMT [] = {96 , 96 , 64 , 64 , 64 , 96 , 64 , 64 , 96 , 96 , 64 , 64 , 64 , 96 , 64 , 64 }; 37 | const uint8_t g_preset_table_LFO_OSC_DST [] = {0 , 0 , 0 , 0 , 0 , 127, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 127, 0 , 0 }; 38 | const uint8_t g_preset_table_LFO_FILTER_AMT [] = {64 , 64 , 88 , 88 , 88 , 64 , 88 , 64 , 64 , 64 , 88 , 88 , 88 , 64 , 88 , 64 }; 39 | const uint8_t g_preset_table_AMP_GAIN [] = {90 , 90 , 90 , 90 , 90 , 90 , 127, 90 , 90 , 90 , 90 , 90 , 90 , 90 , 127, 90 }; 40 | 41 | const uint8_t g_preset_table_AMP_ATTACK [] = {0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }; 42 | const uint8_t g_preset_table_AMP_DECAY [] = {0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }; 43 | const uint8_t g_preset_table_AMP_SUSTAIN [] = {127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127}; 44 | const uint8_t g_preset_table_AMP_RELEASE [] = {0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }; 45 | 46 | const uint8_t g_preset_table_FILTER_MODE [] = {0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }; 47 | const uint8_t g_preset_table_EG_AMP_MOD [] = {127, 127, 0 , 0 , 127, 127, 127, 0 , 127, 127, 0 , 0 , 127, 127, 127, 0 }; 48 | const uint8_t g_preset_table_REL_EQ_DECAY [] = {0 , 0 , 0 , 0 , 127, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 127, 0 , 0 , 0 }; 49 | const uint8_t g_preset_table_P_BEND_RANGE [] = {12 , 12 , 12 , 12 , 12 , 12 , 12 , 12 , 12 , 12 , 12 , 12 , 12 , 12 , 12 , 12 }; 50 | 51 | const uint8_t g_preset_table_BTH_FILTER_AMT [] = {64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 }; 52 | const uint8_t g_preset_table_BTH_AMP_MOD [] = {0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }; 53 | const uint8_t g_preset_table_EG_VEL_SENS [] = {0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }; 54 | const uint8_t g_preset_table_AMP_VEL_SENS [] = {0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }; 55 | 56 | const uint8_t g_preset_table_VOICE_ASGN_MODE[] = {0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }; 57 | 58 | 59 | 60 | 61 | const uint8_t g_preset_table_CHORUS_MIX [] = {64 , 127, 127, 127, 64 , 64 , 127, 0 , 64 , 127, 127, 127, 64 , 64 , 127, 0 }; 62 | const uint8_t g_preset_table_CHORUS_RATE [] = {64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 }; 63 | const uint8_t g_preset_table_CHORUS_DEPTH [] = {64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 }; 64 | 65 | 66 | const uint8_t g_preset_table_DELAY_FEEDBACK [] = {64 , 64 , 64 , 64 , 64 , 64 , 64 , 0 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 0 }; 67 | const uint8_t g_preset_table_DELAY_TIME [] = {93 , 93 , 93 , 93 , 93 , 93 , 93 , 93 , 93 , 93 , 93 , 93 , 93 , 93 , 93 , 93 }; 68 | const uint8_t g_preset_table_DELAY_MODE [] = {0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }; 69 | 70 | -------------------------------------------------------------------------------- /Digital-Synth-PRA32-U/pra32-u-constants.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | A4_FREQ = 440.0 4 | 5 | SAMPLING_RATE = 48000 6 | FREQUENCY_MAX = 23000 7 | BIT_DEPTH = 8 8 | NOTE_NUMBER_MIN = 0 9 | NOTE_NUMBER_MAX = 127 10 | NOTE_NUMBER_INVALID = 255 11 | 12 | PRESET_PROGRAM_NUMBER_MAX = 7 13 | PROGRAM_NUMBER_MAX = 15 14 | 15 | OSC_CONTROL_INTERVAL_BITS = 1 16 | OSC_CONTROL_INTERVAL = 0x01 << OSC_CONTROL_INTERVAL_BITS 17 | OSC_PHASE_RESOLUTION_BITS = 24 18 | OSC_TUNE_TABLE_STEPS_BITS = 8 19 | OSC_TUNE_DENOMINATOR_BITS = 15 20 | OSC_WAVE_TABLE_AMP_BITS = 13 21 | OSC_WAVE_TABLE_AMP = 1 << OSC_WAVE_TABLE_AMP_BITS 22 | OSC_WAVE_TABLE_SAMPLES_BITS = 9 23 | OSC_DETUNE_MUL_NUM_BITS = 4 24 | OSC_DETUNE_FREQ_MAX = (20 * 2) * (1 << 24) / SAMPLING_RATE 25 | FILTER_CONTROL_INTERVAL_BITS = 3 26 | FILTER_CONTROL_INTERVAL = 0x01 << FILTER_CONTROL_INTERVAL_BITS 27 | FILTER_TABLE_FRACTION_BITS = 30 28 | EG_CONTROL_INTERVAL = 0x10 29 | EG_LEVEL_MAX = 0x40000000 30 | 31 | 32 | DATA_BYTE_MAX = 0x7F 33 | STATUS_BYTE_INVALID = 0x7F 34 | DATA_BYTE_INVALID = 0x80 35 | STATUS_BYTE_MIN = 0x80 36 | NOTE_OFF = 0x80 37 | NOTE_ON = 0x90 38 | CONTROL_CHANGE = 0xB0 39 | PROGRAM_CHANGE = 0xC0 40 | PITCH_BEND = 0xE0 41 | SYSTEM_MESSAGE_MIN = 0xF0 42 | SYSTEM_EXCLUSIVE = 0xF0 43 | TIME_CODE = 0xF1 44 | SONG_POSITION = 0xF2 45 | SONG_SELECT = 0xF3 46 | TUNE_REQUEST = 0xF6 47 | EOX = 0xF7 48 | REAL_TIME_MESSAGE_MIN = 0xF8 49 | ACTIVE_SENSING = 0xFE 50 | 51 | 52 | MODULATION = 1 53 | BTH_CONTROLLER = 2 54 | SUSTAIN_PEDAL = 64 55 | 56 | 57 | OSC_1_WAVE = 14 58 | OSC_1_SHAPE = 19 59 | OSC_1_MORPH = 20 60 | MIXER_SUB_OSC = 23 61 | 62 | OSC_2_WAVE = 104 63 | OSC_2_COARSE = 85 64 | OSC_2_PITCH = 76 65 | MIXER_OSC_MIX = 21 66 | 67 | FILTER_CUTOFF = 74 68 | FILTER_RESO = 71 69 | FILTER_EG_AMT = 24 70 | FILTER_KEY_TRK = 26 71 | 72 | EG_ATTACK = 73 73 | EG_DECAY = 75 74 | EG_SUSTAIN = 30 75 | EG_RELEASE = 72 76 | 77 | EG_OSC_AMT = 91 78 | EG_OSC_DST = 89 79 | VOICE_MODE = 102 80 | PORTAMENTO = 5 81 | 82 | LFO_WAVE = 12 83 | LFO_RATE = 3 84 | LFO_DEPTH = 17 85 | LFO_FADE_TIME = 56 86 | 87 | LFO_OSC_AMT = 13 88 | LFO_OSC_DST = 103 89 | LFO_FILTER_AMT = 25 90 | AMP_GAIN = 15 91 | 92 | AMP_ATTACK = 52 93 | AMP_DECAY = 53 94 | AMP_SUSTAIN = 54 95 | AMP_RELEASE = 55 96 | 97 | FILTER_MODE = 78 98 | EG_AMP_MOD = 28 99 | REL_EQ_DECAY = 29 100 | P_BEND_RANGE = 57 101 | 102 | BTH_FILTER_AMT = 60 103 | BTH_AMP_MOD = 61 104 | EG_VEL_SENS = 62 105 | AMP_VEL_SENS = 63 106 | 107 | VOICE_ASGN_MODE = 108 108 | 109 | 110 | 111 | 112 | CHORUS_MIX = 27 113 | CHORUS_RATE = 58 114 | CHORUS_DEPTH = 59 115 | 116 | 117 | DELAY_FEEDBACK = 92 118 | DELAY_TIME = 90 119 | DELAY_MODE = 35 120 | 121 | 122 | PROG_N_TO_W_TO = 87 123 | WRITE_P_TO_PROG = 106 124 | PC_BY_CC_8 = 112 125 | PC_BY_CC_9 = 113 126 | PC_BY_CC_10 = 114 127 | PC_BY_CC_11 = 115 128 | PC_BY_CC_12 = 116 129 | PC_BY_CC_13 = 117 130 | PC_BY_CC_14 = 118 131 | PC_BY_CC_15 = 119 132 | 133 | 134 | ALL_SOUND_OFF = 120 135 | RESET_ALL_CTRLS = 121 136 | ALL_NOTES_OFF = 123 137 | OMNI_MODE_OFF = 124 138 | OMNI_MODE_ON = 125 139 | MONO_MODE_ON = 126 140 | POLY_MODE_ON = 127 141 | 142 | OSC_WAVE_SAW = 0 143 | OSC_WAVE_SINE = 25 144 | OSC_WAVE_TRIANGLE = 75 145 | OSC_WAVE_2_NOISE = 100 146 | OSC_WAVE_1_PULSE = 127 147 | OSC_WAVE_2_SQUARE = 127 148 | 149 | OSC_DST_PITCH = 0 150 | OSC_DST_PITCH_2 = 64 151 | OSC_DST_SHAPE_1 = 127 152 | 153 | LFO_WAVE_SINE = 0 154 | LFO_WAVE_SAW_DOWN = 25 155 | LFO_WAVE_TRIANGLE = 75 156 | LFO_WAVE_S_AND_H = 100 157 | LFO_WAVE_SQUARE = 127 158 | 159 | VOICE_PARAPHONIC = 0 160 | VOICE_POLYPHONIC = 25 161 | VOICE_MONOPHONIC = 75 162 | VOICE_LEGATO = 100 163 | VOICE_LEGATO_PORTA = 127 164 | 165 | 166 | PANEL_SCALE = 128 + 8 167 | PANEL_TRANSPOSE = 128 + 9 168 | PANEL_PLAY_MODE = 128 + 10 169 | PANEL_MIDI_CH = 128 + 11 170 | 171 | PANEL_PLAY_PIT = 128 + 12 172 | PANEL_PLAY_VELO = 128 + 13 173 | PANEL_PIT_OFST = 128 + 14 174 | 175 | 176 | SEQ_PITCH_0 = 128 + 16 177 | SEQ_PITCH_1 = 128 + 17 178 | SEQ_PITCH_2 = 128 + 18 179 | SEQ_PITCH_3 = 128 + 19 180 | 181 | SEQ_PITCH_4 = 128 + 20 182 | SEQ_PITCH_5 = 128 + 21 183 | SEQ_PITCH_6 = 128 + 22 184 | SEQ_PITCH_7 = 128 + 23 185 | 186 | SEQ_PITCH_8 = 128 + 24 187 | SEQ_PITCH_9 = 128 + 25 188 | SEQ_PITCH_10 = 128 + 26 189 | SEQ_PITCH_11 = 128 + 27 190 | 191 | SEQ_PITCH_12 = 128 + 28 192 | SEQ_PITCH_13 = 128 + 29 193 | SEQ_PITCH_14 = 128 + 30 194 | SEQ_PITCH_15 = 128 + 31 195 | 196 | SEQ_VELO_0 = 128 + 32 197 | SEQ_VELO_1 = 128 + 33 198 | SEQ_VELO_2 = 128 + 34 199 | SEQ_VELO_3 = 128 + 35 200 | 201 | SEQ_VELO_4 = 128 + 36 202 | SEQ_VELO_5 = 128 + 37 203 | SEQ_VELO_6 = 128 + 38 204 | SEQ_VELO_7 = 128 + 39 205 | 206 | SEQ_VELO_8 = 128 + 40 207 | SEQ_VELO_9 = 128 + 41 208 | SEQ_VELO_10 = 128 + 42 209 | SEQ_VELO_11 = 128 + 43 210 | 211 | SEQ_VELO_12 = 128 + 44 212 | SEQ_VELO_13 = 128 + 45 213 | SEQ_VELO_14 = 128 + 46 214 | SEQ_VELO_15 = 128 + 47 215 | 216 | SEQ_TEMPO = 128 + 48 217 | SEQ_CLOCK_SRC = 128 + 49 218 | SEQ_GATE_TIME = 128 + 50 219 | SEQ_NUM_STEPS = 128 + 51 220 | 221 | SEQ_MODE = 128 + 52 222 | SEQ_ACT_STEPS = 128 + 53 223 | SEQ_TRANSPOSE = 128 + 54 224 | SEQ_STEP_NOTE = 128 + 55 225 | 226 | SEQ_ON_STEPS = 128 + 56 227 | 228 | 229 | 230 | 231 | RD_PROGRAM_0 = 128 + 64 232 | RD_PROGRAM_1 = 128 + 65 233 | RD_PROGRAM_2 = 128 + 66 234 | RD_PROGRAM_3 = 128 + 67 235 | 236 | RD_PROGRAM_4 = 128 + 68 237 | RD_PROGRAM_5 = 128 + 69 238 | RD_PROGRAM_6 = 128 + 70 239 | RD_PROGRAM_7 = 128 + 71 240 | 241 | RD_PROGRAM_8 = 128 + 72 242 | RD_PROGRAM_9 = 128 + 73 243 | RD_PROGRAM_10 = 128 + 74 244 | RD_PROGRAM_11 = 128 + 75 245 | 246 | RD_PROGRAM_12 = 128 + 76 247 | RD_PROGRAM_13 = 128 + 77 248 | RD_PROGRAM_14 = 128 + 78 249 | RD_PROGRAM_15 = 128 + 79 250 | 251 | WR_PROGRAM_0 = 128 + 80 252 | WR_PROGRAM_1 = 128 + 81 253 | WR_PROGRAM_2 = 128 + 82 254 | WR_PROGRAM_3 = 128 + 83 255 | 256 | WR_PROGRAM_4 = 128 + 84 257 | WR_PROGRAM_5 = 128 + 85 258 | WR_PROGRAM_6 = 128 + 86 259 | WR_PROGRAM_7 = 128 + 87 260 | 261 | WR_PROGRAM_8 = 128 + 88 262 | WR_PROGRAM_9 = 128 + 89 263 | WR_PROGRAM_10 = 128 + 90 264 | WR_PROGRAM_11 = 128 + 91 265 | 266 | WR_PROGRAM_12 = 128 + 92 267 | WR_PROGRAM_13 = 128 + 93 268 | WR_PROGRAM_14 = 128 + 94 269 | WR_PROGRAM_15 = 128 + 95 270 | 271 | RD_PANEL_PRMS = 128 + 96 272 | IN_PANEL_PRMS = 128 + 97 273 | 274 | WR_PANEL_PRMS = 128 + 100 275 | 276 | SEQ_RAND_PITCH = 128 + 104 277 | SEQ_RAND_VELO = 128 + 105 278 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /Digital-Synth-PRA32-U/pra32-u-filter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // refs https://webaudio.github.io/Audio-EQ-Cookbook/Audio-EQ-Cookbook.txt 4 | 5 | #include "pra32-u-common.h" 6 | #include "pra32-u-filter-table.h" 7 | 8 | class PRA32_U_Filter { 9 | int32_t m_b_2_over_a_0; 10 | int32_t m_a_1_over_a_0; 11 | int32_t m_a_2_over_a_0; 12 | int16_t m_filter_gain; 13 | int16_t m_x_1; 14 | int16_t m_x_2; 15 | int32_t m_y_1; 16 | int32_t m_y_2; 17 | int32_t m_resonance_index; 18 | int16_t m_cutoff_current; 19 | int16_t m_cutoff_candidate; 20 | uint8_t m_cutoff_control; 21 | uint8_t m_cutoff_control_effective; 22 | int8_t m_cutoff_eg_amt; 23 | int8_t m_cutoff_lfo_amt; 24 | int8_t m_cutoff_pitch_amt; 25 | int8_t m_cutoff_offset; 26 | uint8_t m_filter_mode; 27 | int8_t m_cutoff_breath_amt; 28 | int16_t m_breath_controller; 29 | 30 | const uint8_t AUDIO_FRACTION_BITS = 14; 31 | const int16_t MAX_ABS_OUTPUT = ((124 << (AUDIO_FRACTION_BITS - 8)) >> 8) << 8; 32 | 33 | public: 34 | PRA32_U_Filter() 35 | : m_b_2_over_a_0() 36 | , m_a_1_over_a_0() 37 | , m_a_2_over_a_0() 38 | , m_filter_gain() 39 | , m_x_1() 40 | , m_x_2() 41 | , m_y_1() 42 | , m_y_2() 43 | , m_resonance_index() 44 | , m_cutoff_current() 45 | , m_cutoff_candidate() 46 | , m_cutoff_control() 47 | , m_cutoff_control_effective() 48 | , m_cutoff_eg_amt() 49 | , m_cutoff_lfo_amt() 50 | , m_cutoff_pitch_amt() 51 | , m_cutoff_offset() 52 | , m_filter_mode() 53 | , m_cutoff_breath_amt() 54 | , m_breath_controller() 55 | { 56 | m_cutoff_current = 254; 57 | 58 | set_cutoff(127); 59 | set_resonance(0); 60 | set_cutoff_eg_amt(64); 61 | set_cutoff_lfo_amt(64); 62 | set_cutoff_pitch_amt(0); 63 | set_cutoff_offset(0); 64 | 65 | update_coefs(0, 0, 60 << 8); 66 | } 67 | 68 | INLINE void set_cutoff(uint8_t controller_value) { 69 | m_cutoff_control = controller_value << 1; 70 | } 71 | 72 | INLINE void set_resonance(uint8_t controller_value) { 73 | m_resonance_index = (controller_value + 2) >> 3; 74 | } 75 | 76 | INLINE int8_t get_cutoff_mod_amt(uint8_t controller_value) { 77 | static int8_t cutoff_mod_amt_table[128] = { 78 | -120, -120, -120, -120, -120, -118, -116, -114, 79 | -112, -110, -108, -106, -104, -102, -100, -98, 80 | -96, -94, -92, -90, -88, -86, -84, -82, 81 | -80, -78, -76, -74, -72, -70, -68, -66, 82 | -64, -62, -60, -58, -56, -54, -52, -50, 83 | -48, -46, -44, -42, -40, -38, -36, -34, 84 | -32, -30, -28, -26, -24, -22, -20, -18, 85 | -16, -14, -12, -10, -8, -6, -4, -2, 86 | +0, +2, +4, +6, +8, +10, +12, +14, 87 | +16, +18, +20, +22, +24, +26, +28, +30, 88 | +32, +34, +36, +38, +40, +42, +44, +46, 89 | +48, +50, +52, +54, +56, +58, +60, +62, 90 | +64, +66, +68, +70, +72, +74, +76, +78, 91 | +80, +82, +84, +86, +88, +90, +92, +94, 92 | +96, +98, +100, +102, +104, +106, +108, +110, 93 | +112, +114, +116, +118, +120, +120, +120, +120, 94 | }; 95 | 96 | return cutoff_mod_amt_table[controller_value]; 97 | } 98 | 99 | INLINE void set_cutoff_eg_amt(uint8_t controller_value) { 100 | m_cutoff_eg_amt = get_cutoff_mod_amt(controller_value); 101 | } 102 | 103 | INLINE void set_cutoff_lfo_amt(uint8_t controller_value) { 104 | m_cutoff_lfo_amt = get_cutoff_mod_amt(controller_value); 105 | } 106 | 107 | INLINE void set_cutoff_pitch_amt(uint8_t controller_value) { 108 | static int8_t cutoff_pitch_amt_table[4] = { 109 | +0, +1, +1, +2, 110 | }; 111 | 112 | m_cutoff_pitch_amt = cutoff_pitch_amt_table[controller_value >> 5]; 113 | } 114 | 115 | INLINE void set_filter_mode(uint8_t controller_value) { 116 | m_filter_mode = controller_value; 117 | } 118 | 119 | INLINE void set_cutoff_breath_amt(uint8_t controller_value) {; 120 | m_cutoff_breath_amt = get_cutoff_mod_amt(controller_value); 121 | } 122 | 123 | INLINE void set_breath_controller(uint8_t controller_value) { 124 | m_breath_controller = (controller_value * 16384) / 127; 125 | } 126 | 127 | INLINE void set_cutoff_offset(int8_t cutoff_offset) { 128 | m_cutoff_offset = cutoff_offset; 129 | } 130 | 131 | INLINE void process_at_low_rate(uint8_t count, int16_t eg_input, int16_t lfo_input, uint16_t osc_pitch) { 132 | switch (count & (0x04 - 1)) { 133 | case 0x00: 134 | case 0x02: 135 | update_coefs(eg_input, lfo_input, osc_pitch); 136 | break; 137 | case 0x03: 138 | update_cutoff_control_effective(); 139 | break; 140 | } 141 | } 142 | 143 | INLINE int16_t process(int16_t audio_input) { 144 | #if 1 145 | int16_t x_0 = audio_input >> (16 - AUDIO_FRACTION_BITS); 146 | int16_t x_3 = x_0 + (m_x_1 << 1) + m_x_2; 147 | int32_t y_0 = mul_s32_s16_h32(m_b_2_over_a_0, x_3) << (32 - FILTER_TABLE_FRACTION_BITS); 148 | 149 | y_0 -= mul_s32_s32_h32(m_a_1_over_a_0, m_y_1) << (32 - FILTER_TABLE_FRACTION_BITS); 150 | y_0 -= mul_s32_s32_h32(m_a_2_over_a_0, m_y_2) << (32 - FILTER_TABLE_FRACTION_BITS); 151 | 152 | m_x_2 = m_x_1; 153 | m_y_2 = m_y_1; 154 | m_x_1 = x_0; 155 | 156 | // y_0_clamped = clamp(y_0, (-MAX_ABS_OUTPUT << 16), (+MAX_ABS_OUTPUT << 16)) 157 | volatile int32_t y_0_clamped = y_0 - (+MAX_ABS_OUTPUT << 16); 158 | y_0_clamped = (y_0_clamped < 0) * y_0_clamped + (+MAX_ABS_OUTPUT << 16) - (-MAX_ABS_OUTPUT << 16); 159 | y_0_clamped = (y_0_clamped > 0) * y_0_clamped + (-MAX_ABS_OUTPUT << 16); 160 | 161 | m_y_1 = y_0_clamped; 162 | 163 | #if 1 164 | if (m_filter_mode == 1 || m_filter_mode >= 64) { 165 | #else 166 | if (m_filter_mode >= 64) { 167 | #endif 168 | // high pass 169 | y_0_clamped = (x_0 << 16) - y_0_clamped; 170 | } 171 | 172 | // y_0_clamped_2nd = clamp(y_0_clamped, (-MAX_ABS_OUTPUT << 16), (+MAX_ABS_OUTPUT << 16)) 173 | volatile int32_t y_0_clamped_2nd = y_0_clamped - (+MAX_ABS_OUTPUT << 16); 174 | y_0_clamped_2nd = (y_0_clamped_2nd < 0) * y_0_clamped_2nd + (+MAX_ABS_OUTPUT << 16) - (-MAX_ABS_OUTPUT << 16); 175 | y_0_clamped_2nd = (y_0_clamped_2nd > 0) * y_0_clamped_2nd + (-MAX_ABS_OUTPUT << 16); 176 | #else 177 | volatile int32_t y_0_clamped_2nd = x_0; 178 | #endif 179 | 180 | return (y_0_clamped_2nd << (16 - AUDIO_FRACTION_BITS)) >> 16; 181 | } 182 | 183 | private: 184 | INLINE void update_cutoff_control_effective() { 185 | m_cutoff_control_effective += (m_cutoff_control_effective < m_cutoff_control); 186 | m_cutoff_control_effective -= (m_cutoff_control_effective > m_cutoff_control); 187 | } 188 | 189 | INLINE void update_coefs(int16_t eg_input, int16_t lfo_input, uint16_t osc_pitch) { 190 | m_cutoff_candidate = m_cutoff_control_effective; 191 | m_cutoff_candidate += (m_cutoff_eg_amt * eg_input) >> 14; 192 | m_cutoff_candidate += m_cutoff_offset; 193 | 194 | m_cutoff_candidate += (lfo_input * m_cutoff_lfo_amt) >> 14; 195 | m_cutoff_candidate += (((osc_pitch - (60 << 8)) * m_cutoff_pitch_amt) + 128) >> 8; 196 | m_cutoff_candidate += (m_breath_controller * m_cutoff_breath_amt) >> 14; 197 | 198 | // cutoff_target = clamp(m_cutoff_candidate, 0, 255) 199 | volatile int16_t cutoff_target = m_cutoff_candidate - 255; 200 | cutoff_target = (cutoff_target < 0) * cutoff_target + 255; 201 | cutoff_target = (cutoff_target > 0) * cutoff_target; 202 | 203 | m_cutoff_current += (m_cutoff_current < cutoff_target); 204 | m_cutoff_current -= (m_cutoff_current > cutoff_target); 205 | 206 | const int32_t* filter_table = g_filter_tables[m_resonance_index]; 207 | size_t index = m_cutoff_current * 3; 208 | m_b_2_over_a_0 = filter_table[index + 0]; 209 | m_a_1_over_a_0 = filter_table[index + 1]; 210 | m_a_2_over_a_0 = filter_table[index + 2]; 211 | 212 | m_filter_gain = g_filter_gain_tables[m_resonance_index]; 213 | } 214 | }; 215 | -------------------------------------------------------------------------------- /PRA32-U-Parameter-Guide.md: -------------------------------------------------------------------------------- 1 | # Digital Synth PRA32-U Parameter Guide v3.2.0 2 | 3 | - 2025-05-11 ISGK Instruments 4 | - 5 | 6 | ## Control Change Parameters 7 | 8 | - Notes 9 | - $1 : Disabled in Paraphonic Mode 10 | - $2 : Disabled if Osc 1 Wave is not Sin or Pls 11 | - Osc 1 Wave [Saw|Sin|-|Tri|-|Pls] 12 | - 0, 6-12: Saw Wave 13 | - 1, 25 (13-38): Sine Wave (Shape adjustable) 14 | - 2, 50 (39-63): Triangle Wave 15 | - 3, 75 (64-88): Triangle Wave 16 | - 4, 100 (89-114): Pulse Wave (Shape adjustable) 17 | - 5, 127 (115-127): Pulse Wave (Shape adjustable) 18 | - Osc 1 Shape $2 19 | - Sine Wave (Phase Modulation): Modulation Depth 20 | - Pulse Wave (= 1st Saw + Phase Shifted 2nd Saw) 21 | - 0: Pulse Width 50%, or 2nd Saw Phase 50% (min) 22 | - 64: Pulse Width 25%, or 2nd Saw Phase 25% 23 | - 96: Pulse Width 12.5%, or 2nd Saw Phase 12.5% 24 | - 127: Pulse Width 0.4%, or 2nd Saw Phase 99.6% (max) 25 | - Osc 1 Morph $2 26 | - Sine Wave (Phase Modulation): Frequency Ratio of Modulator 27 | - 0 (0-1): Ratio 0.5 (min) 28 | - 4 (2-5): Ratio 1.0 29 | - 8 (6-9): Ratio 1.5 30 | - 12 (10-13): Ratio 2.0 31 | - 20 (18-21): Ratio 3.0 32 | - 28 (26-29): Ratio 3.5 33 | - 108 (106-109): Ratio 14.0 34 | - 124 (122-125): Ratio 16.0 35 | - 127 (126-127): Ratio 16.5 (max) 36 | - Pulse Wave 37 | - 0: Pulse 100% = Saw 100% + Reverse Saw 100% (min) 38 | - 32: Pulse 50% + Saw 50% = Saw 100% + Reverse Saw 50% 39 | - 64: Saw 100% 40 | - 96: Saw 100% + Saw 50% 41 | - 127: Saw 100% + Saw 100% (max) 42 | - Mixer Noise/Sub Osc [N|S] 43 | - -64 (0): Noise 100% 44 | - -63 (1): Noise 98.4% 45 | - -62 (2): Noise 96.9% 46 | - -1 (63): Noise 1.6% 47 | - +0 (64): 0% 48 | - +1 (65): Sub Osc 1.6% 49 | - +62 (126): Sub Osc 96.9% 50 | - +63 (127): Sub Osc 100% 51 | - Osc 2 Wave [Saw|Sin|-|Tri|Nos|Sqr] 52 | - 0, 6-12: Saw Wave 53 | - 1, 25 (13-38): Sine Wave 54 | - 2, 50 (39-63): Triangle Wave 55 | - 3, 75 (64-88): Triangle Wave 56 | - 4, 100 (89-114): White Noise 57 | - 5, 127 (115-127): Square Wave 58 | - Osc 2 Coarse [-|+] 59 | - -60 (4): -60 semitone (min) 60 | - +60 (124): +60 semitone (max) 61 | - Osc 2 Pitch [-|+] 62 | - -55 (9): -12 semitone (min) 63 | - -41 (23): -5 semitone 64 | - -33 (31): -1 semitone 65 | - -32 (32): -50 cent 66 | - +0 (64): +0 cent 67 | - +32 (96): +50 cent 68 | - +33 (97): +1 semitone 69 | - +45 (109): +7 semitone 70 | - +55 (119): +12 semitone (max) 71 | - Mixer Osc Mix [1|2] 72 | - Filter Cutoff 73 | - 0: f = 13.0 Hz (min) 74 | - 61: f = 440 Hz 75 | - 64: f = 523.3 Hz 76 | - 121: f = 14080 kHz 77 | - 127: f = 19912.1 Hz (max) 78 | - Filter Resonance 79 | - 16 (0-19): Q = 0.7 (min) 80 | - 24 (20-27): Q = 0.8 81 | - 32 (28-35): Q = 1.0 82 | - 40 (36-43): Q = 1.2 83 | - 48 (44-51): Q = 1.4 84 | - 56 (52-59): Q = 1.7 85 | - 64 (60-67): Q = 2.0 86 | - 72 (68-75): Q = 2.4 87 | - 80 (76-83): Q = 2.8 88 | - 88 (84-91): Q = 3.4 89 | - 96 (92-99): Q = 4.0 90 | - 104 (100-107): Q = 4.8 91 | - 112 (108-115): Q = 5.6 92 | - 120 (116-123): Q = 6.7 93 | - 127 (124-127): Q = 8.0 (max) 94 | - Filter EG Amt [-|+], LFO Filter Amt [-|+] 95 | - -60 (4): -60 (min) 96 | - +60 (124): +60 (max) 97 | - Filter Key Track [0.0|0.5|1.0] $1 98 | - 0 (0-31): 0.0 99 | - 64 (32-95): 0.5 100 | - 127 (96-127): 1.0 101 | - EG Attack, Amp Attack 102 | - 0: 0.7 ms 103 | - 64: 63.2 ms 104 | - 127: 5.9 s 105 | - EG Decay, Amp Decay 106 | - 0: 2 ms 107 | - 64: 200 ms 108 | - 126: 17.3 s 109 | - 127: No Decay 110 | - EG Release, Amp Release 111 | - 0: 2 ms 112 | - 64: 200 ms 113 | - 127: 18.6 s 114 | - EG Osc Amt [-|+], LFO Osc Amt [-|+] 115 | - Pitch 116 | - -61 (3): -30 semitone (min) 117 | - -55 (9): -24 semitone 118 | - -43 (21): -12 semitone 119 | - -33 (31): -2 semitone 120 | - -32 (32): -100 cent 121 | - +0 (64): +0 cent 122 | - +32 (96): +100 cent 123 | - +33 (97): +2 semitone 124 | - +43 (107): +12 semitone 125 | - +55 (119): +24 semitone 126 | - +61 (125): +30 semitone (max) 127 | - Shape 128 | - -63 (1): Shape -252 (min) 129 | - +63 (127): Shape +252 (max) 130 | - EG Osc Dst [P|2P|1S], LFO Osc Dst [P|2P|1S] 131 | - 0, 3-31: Osc 1 & 2 Pitch 132 | - 1, 64 (32-95): Osc 2 Pitch 133 | - 2, 127 (96-127): Osc 1 Shape 134 | - Voice Mode [Pol|Par|-|Mon|LP|Lgt] 135 | - 0, 6-12: Polyphonic (LFO Single Trigger) 136 | - 1, 25 (13-38): Paraphonic (LFO Single Trigger) 137 | - 2, 50 (39-63): Monophonic (EG & LFO Multi Trigger) 138 | - 3, 75 (64-88): Monophonic (EG & LFO Multi Trigger) 139 | - 4, 100 (89-114): Legato Portamento (Monophonic, EG & LFO Single Trigger, Auto Portamento) 140 | - 5, 127 (115-127): Legato (Monophonic, EG & LFO Single Trigger) 141 | - Portamento 142 | - 0: Portamento Time 0 ms 143 | - 1: Portamento Time 1.1 ms 144 | - 64: Portamento Time 100 ms 145 | - 127: Portamento Time 9.3 s 146 | - LFO Wave [Tri|Sin|-|Saw|SH|Sqr] 147 | - 0, 6-12: Triangle Wave (Key Trigger Off, -0.5 to +0.5) 148 | - 1, 25 (13-38): Sine Wave (Key Trigger Off, -0.5 to +0.5) 149 | - 2, 50 (39-63): Saw Wave (Key Trigger On, -0.5 to +0.5) 150 | - 3, 75 (64-88): Saw Wave (Key Trigger On, -0.5 to +0.5) 151 | - 4, 100 (89-114): Sample & Hold (Key Trigger On, -0.5 to +0.5) 152 | - 5, 127 (115-127): Square Wave (Key Trigger On, 0.0 to 1.0) 153 | - LFO Rate 154 | - 0: 0.068 Hz (min) 155 | - 64: 2.7 Hz 156 | - 80: 6.9 Hz 157 | - 127: 103.8Hz (max) 158 | - LFO Depth 159 | - The actual LFO depth is the "LFO Depth" value plus the "Modulation" value 160 | - LFO Fade Time 161 | - 0: 0 ms (min) 162 | - 1: 9.6 ms 163 | - 64: 1.0 s 164 | - 127: 9.6 s (max) 165 | - Filter Mode [LP|HP] 166 | - 0, 2-63: Low Pass 167 | - 1, 127 (64-127): High Pass 168 | - EG Amp Mod [Off|On] 169 | - 0, 2-63: Off 170 | - 1, 127 (64-127): On, Amp ADSR = EG ADSR 171 | - Release = Decay [Off|On] 172 | - 0, 2-63: Off 173 | - 1, 127 (64-127): On, EG Release = EG Decay and Amp Release = Amp Decay 174 | - **NOTE**: EG Velocity Sensitivity and Amp Velocity Sensitivity works independently 175 | - Pitch Bend Range 176 | - 0: 0 semitone (min) 177 | - 60: 60 semitone (max) 178 | - Breath Filter Amt [-|+] 179 | - -60 (4): -60 (min) 180 | - +60 (124): +60 (max) 181 | - Breath Amp Mod [Off|Qad|Lin] 182 | - 0, 3-31: Off 183 | - 1, 64 (32-95): Quadratic Curve 184 | - 2, 127 (96-127): Liniear Curve 185 | - Voice Assign Mode [1|2] 186 | - 0, 2-63: Mode 1, Free voice with next number has priority in Polyphonic/Paraphonic Mode, Release is effective 187 | - 1, 127 (64-127): Mode 2, Free voice with small number has priority in Polyphonic/Paraphonic Mode, Portamento is effective 188 | - Chorus Rate 189 | - 0: LFO Frequency 0.012 Hz (min) 190 | - 64: LFO Frequency 0.48 Hz 191 | - 127: LFO Frequency 18.4 Hz (max) 192 | - Chorus Depth 193 | - 0: Delay Time +/- 0 ms (min) 194 | - 32: Delay Time +/- 1.3 ms 195 | - 64: Delay Time +/- 2.7 ms 196 | - 126: Delay Time +/- 5.3 ms (max) 197 | - Delay Feedback 198 | - 0: Feedback 0% (min) 199 | - 64: Feedback 25% 200 | - 127: Feedback 49.6% (max) 201 | - Delay Time 202 | - 0: 1 ms (min) 203 | - 5: 6 ms 204 | - 6: 8 ms 205 | - 7: 10 ms 206 | - 12: 20 ms 207 | - 27: 50 ms 208 | - 42: 100 ms = eighth note time at 300 BPM 209 | - 57: 150 ms = eighth note time at 200 BPM 210 | - 62: 166.7 ms = eighth note time at 180 BPM 211 | - 64: 173.3 ms 212 | - 72: 200 ms = eighth note time at 150 BPM 213 | - 87: 250 ms = eighth note time at 120 BPM 214 | - 93: 270 ms 215 | - 102: 300 ms = eighth note time at 100 BPM 216 | - 112: 333.3 ms (max) 217 | - Delay Mode [S|P] 218 | - 0, 2-63: Stereo Delay 219 | - 1, 127 (64-127): Ping Pong Delay 220 | -------------------------------------------------------------------------------- /Digital-Synth-PRA32-U/pra32-u-constants.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | const double A4_FREQ = 440.0; 4 | 5 | const uint32_t SAMPLING_RATE = 48000; 6 | const uint32_t FREQUENCY_MAX = 23000; 7 | const uint8_t BIT_DEPTH = 8; 8 | const uint8_t NOTE_NUMBER_MIN = 0; 9 | const uint8_t NOTE_NUMBER_MAX = 127; 10 | const uint8_t NOTE_NUMBER_INVALID = 255; 11 | 12 | const uint8_t PRESET_PROGRAM_NUMBER_MAX = 7; 13 | const uint8_t PROGRAM_NUMBER_MAX = 15; 14 | 15 | const int8_t OSC_CONTROL_INTERVAL_BITS = 1; 16 | const uint8_t OSC_CONTROL_INTERVAL = 0x01 << OSC_CONTROL_INTERVAL_BITS; 17 | const int8_t OSC_PHASE_RESOLUTION_BITS = 24; 18 | const int8_t OSC_TUNE_TABLE_STEPS_BITS = 8; 19 | const int8_t OSC_TUNE_DENOMINATOR_BITS = 15; 20 | const uint8_t OSC_WAVE_TABLE_AMP_BITS = 13; 21 | const int16_t OSC_WAVE_TABLE_AMP = 1 << OSC_WAVE_TABLE_AMP_BITS; 22 | const int8_t OSC_WAVE_TABLE_SAMPLES_BITS = 9; 23 | const int8_t OSC_DETUNE_MUL_NUM_BITS = 4; 24 | const uint16_t OSC_DETUNE_FREQ_MAX = (20 * 2) * (1 << 24) / SAMPLING_RATE; 25 | const int8_t FILTER_CONTROL_INTERVAL_BITS = 3; 26 | const uint8_t FILTER_CONTROL_INTERVAL = 0x01 << FILTER_CONTROL_INTERVAL_BITS; 27 | const int8_t FILTER_TABLE_FRACTION_BITS = 30; 28 | const uint8_t EG_CONTROL_INTERVAL = 0x10; 29 | const int32_t EG_LEVEL_MAX = 0x40000000; 30 | 31 | 32 | const uint8_t DATA_BYTE_MAX = 0x7F; 33 | const uint8_t STATUS_BYTE_INVALID = 0x7F; 34 | const uint8_t DATA_BYTE_INVALID = 0x80; 35 | const uint8_t STATUS_BYTE_MIN = 0x80; 36 | const uint8_t NOTE_OFF = 0x80; 37 | const uint8_t NOTE_ON = 0x90; 38 | const uint8_t CONTROL_CHANGE = 0xB0; 39 | const uint8_t PROGRAM_CHANGE = 0xC0; 40 | const uint8_t PITCH_BEND = 0xE0; 41 | const uint8_t SYSTEM_MESSAGE_MIN = 0xF0; 42 | const uint8_t SYSTEM_EXCLUSIVE = 0xF0; 43 | const uint8_t TIME_CODE = 0xF1; 44 | const uint8_t SONG_POSITION = 0xF2; 45 | const uint8_t SONG_SELECT = 0xF3; 46 | const uint8_t TUNE_REQUEST = 0xF6; 47 | const uint8_t EOX = 0xF7; 48 | const uint8_t REAL_TIME_MESSAGE_MIN = 0xF8; 49 | const uint8_t ACTIVE_SENSING = 0xFE; 50 | 51 | 52 | const uint8_t MODULATION = 1; 53 | const uint8_t BTH_CONTROLLER = 2; 54 | const uint8_t SUSTAIN_PEDAL = 64; 55 | 56 | 57 | const uint8_t OSC_1_WAVE = 14; 58 | const uint8_t OSC_1_SHAPE = 19; 59 | const uint8_t OSC_1_MORPH = 20; 60 | const uint8_t MIXER_SUB_OSC = 23; 61 | 62 | const uint8_t OSC_2_WAVE = 104; 63 | const uint8_t OSC_2_COARSE = 85; 64 | const uint8_t OSC_2_PITCH = 76; 65 | const uint8_t MIXER_OSC_MIX = 21; 66 | 67 | const uint8_t FILTER_CUTOFF = 74; 68 | const uint8_t FILTER_RESO = 71; 69 | const uint8_t FILTER_EG_AMT = 24; 70 | const uint8_t FILTER_KEY_TRK = 26; 71 | 72 | const uint8_t EG_ATTACK = 73; 73 | const uint8_t EG_DECAY = 75; 74 | const uint8_t EG_SUSTAIN = 30; 75 | const uint8_t EG_RELEASE = 72; 76 | 77 | const uint8_t EG_OSC_AMT = 91; 78 | const uint8_t EG_OSC_DST = 89; 79 | const uint8_t VOICE_MODE = 102; 80 | const uint8_t PORTAMENTO = 5; 81 | 82 | const uint8_t LFO_WAVE = 12; 83 | const uint8_t LFO_RATE = 3; 84 | const uint8_t LFO_DEPTH = 17; 85 | const uint8_t LFO_FADE_TIME = 56; 86 | 87 | const uint8_t LFO_OSC_AMT = 13; 88 | const uint8_t LFO_OSC_DST = 103; 89 | const uint8_t LFO_FILTER_AMT = 25; 90 | const uint8_t AMP_GAIN = 15; 91 | 92 | const uint8_t AMP_ATTACK = 52; 93 | const uint8_t AMP_DECAY = 53; 94 | const uint8_t AMP_SUSTAIN = 54; 95 | const uint8_t AMP_RELEASE = 55; 96 | 97 | const uint8_t FILTER_MODE = 78; 98 | const uint8_t EG_AMP_MOD = 28; 99 | const uint8_t REL_EQ_DECAY = 29; 100 | const uint8_t P_BEND_RANGE = 57; 101 | 102 | const uint8_t BTH_FILTER_AMT = 60; 103 | const uint8_t BTH_AMP_MOD = 61; 104 | const uint8_t EG_VEL_SENS = 62; 105 | const uint8_t AMP_VEL_SENS = 63; 106 | 107 | const uint8_t VOICE_ASGN_MODE = 108; 108 | 109 | 110 | 111 | 112 | const uint8_t CHORUS_MIX = 27; 113 | const uint8_t CHORUS_RATE = 58; 114 | const uint8_t CHORUS_DEPTH = 59; 115 | 116 | 117 | const uint8_t DELAY_FEEDBACK = 92; 118 | const uint8_t DELAY_TIME = 90; 119 | const uint8_t DELAY_MODE = 35; 120 | 121 | 122 | const uint8_t PROG_N_TO_W_TO = 87; 123 | const uint8_t WRITE_P_TO_PROG = 106; 124 | const uint8_t PC_BY_CC_8 = 112; 125 | const uint8_t PC_BY_CC_9 = 113; 126 | const uint8_t PC_BY_CC_10 = 114; 127 | const uint8_t PC_BY_CC_11 = 115; 128 | const uint8_t PC_BY_CC_12 = 116; 129 | const uint8_t PC_BY_CC_13 = 117; 130 | const uint8_t PC_BY_CC_14 = 118; 131 | const uint8_t PC_BY_CC_15 = 119; 132 | 133 | 134 | const uint8_t ALL_SOUND_OFF = 120; 135 | const uint8_t RESET_ALL_CTRLS = 121; 136 | const uint8_t ALL_NOTES_OFF = 123; 137 | const uint8_t OMNI_MODE_OFF = 124; 138 | const uint8_t OMNI_MODE_ON = 125; 139 | const uint8_t MONO_MODE_ON = 126; 140 | const uint8_t POLY_MODE_ON = 127; 141 | 142 | const uint8_t OSC_WAVE_SAW = 0; 143 | const uint8_t OSC_WAVE_SINE = 25; 144 | const uint8_t OSC_WAVE_TRIANGLE = 75; 145 | const uint8_t OSC_WAVE_2_NOISE = 100; 146 | const uint8_t OSC_WAVE_1_PULSE = 127; 147 | const uint8_t OSC_WAVE_2_SQUARE = 127; 148 | 149 | const uint8_t OSC_DST_PITCH = 0; 150 | const uint8_t OSC_DST_PITCH_2 = 64; 151 | const uint8_t OSC_DST_SHAPE_1 = 127; 152 | 153 | const uint8_t LFO_WAVE_SINE = 0; 154 | const uint8_t LFO_WAVE_SAW_DOWN = 25; 155 | const uint8_t LFO_WAVE_TRIANGLE = 75; 156 | const uint8_t LFO_WAVE_S_AND_H = 100; 157 | const uint8_t LFO_WAVE_SQUARE = 127; 158 | 159 | const uint8_t VOICE_PARAPHONIC = 0; 160 | const uint8_t VOICE_POLYPHONIC = 25; 161 | const uint8_t VOICE_MONOPHONIC = 75; 162 | const uint8_t VOICE_LEGATO = 100; 163 | const uint8_t VOICE_LEGATO_PORTA = 127; 164 | 165 | 166 | const uint8_t PANEL_SCALE = 128 + 8; 167 | const uint8_t PANEL_TRANSPOSE = 128 + 9; 168 | const uint8_t PANEL_PLAY_MODE = 128 + 10; 169 | const uint8_t PANEL_MIDI_CH = 128 + 11; 170 | 171 | const uint8_t PANEL_PLAY_PIT = 128 + 12; 172 | const uint8_t PANEL_PLAY_VELO = 128 + 13; 173 | const uint8_t PANEL_PIT_OFST = 128 + 14; 174 | 175 | 176 | const uint8_t SEQ_PITCH_0 = 128 + 16; 177 | const uint8_t SEQ_PITCH_1 = 128 + 17; 178 | const uint8_t SEQ_PITCH_2 = 128 + 18; 179 | const uint8_t SEQ_PITCH_3 = 128 + 19; 180 | 181 | const uint8_t SEQ_PITCH_4 = 128 + 20; 182 | const uint8_t SEQ_PITCH_5 = 128 + 21; 183 | const uint8_t SEQ_PITCH_6 = 128 + 22; 184 | const uint8_t SEQ_PITCH_7 = 128 + 23; 185 | 186 | const uint8_t SEQ_PITCH_8 = 128 + 24; 187 | const uint8_t SEQ_PITCH_9 = 128 + 25; 188 | const uint8_t SEQ_PITCH_10 = 128 + 26; 189 | const uint8_t SEQ_PITCH_11 = 128 + 27; 190 | 191 | const uint8_t SEQ_PITCH_12 = 128 + 28; 192 | const uint8_t SEQ_PITCH_13 = 128 + 29; 193 | const uint8_t SEQ_PITCH_14 = 128 + 30; 194 | const uint8_t SEQ_PITCH_15 = 128 + 31; 195 | 196 | const uint8_t SEQ_VELO_0 = 128 + 32; 197 | const uint8_t SEQ_VELO_1 = 128 + 33; 198 | const uint8_t SEQ_VELO_2 = 128 + 34; 199 | const uint8_t SEQ_VELO_3 = 128 + 35; 200 | 201 | const uint8_t SEQ_VELO_4 = 128 + 36; 202 | const uint8_t SEQ_VELO_5 = 128 + 37; 203 | const uint8_t SEQ_VELO_6 = 128 + 38; 204 | const uint8_t SEQ_VELO_7 = 128 + 39; 205 | 206 | const uint8_t SEQ_VELO_8 = 128 + 40; 207 | const uint8_t SEQ_VELO_9 = 128 + 41; 208 | const uint8_t SEQ_VELO_10 = 128 + 42; 209 | const uint8_t SEQ_VELO_11 = 128 + 43; 210 | 211 | const uint8_t SEQ_VELO_12 = 128 + 44; 212 | const uint8_t SEQ_VELO_13 = 128 + 45; 213 | const uint8_t SEQ_VELO_14 = 128 + 46; 214 | const uint8_t SEQ_VELO_15 = 128 + 47; 215 | 216 | const uint8_t SEQ_TEMPO = 128 + 48; 217 | const uint8_t SEQ_CLOCK_SRC = 128 + 49; 218 | const uint8_t SEQ_GATE_TIME = 128 + 50; 219 | const uint8_t SEQ_NUM_STEPS = 128 + 51; 220 | 221 | const uint8_t SEQ_MODE = 128 + 52; 222 | const uint8_t SEQ_ACT_STEPS = 128 + 53; 223 | const uint8_t SEQ_TRANSPOSE = 128 + 54; 224 | const uint8_t SEQ_STEP_NOTE = 128 + 55; 225 | 226 | const uint8_t SEQ_ON_STEPS = 128 + 56; 227 | 228 | 229 | 230 | 231 | const uint8_t RD_PROGRAM_0 = 128 + 64; 232 | const uint8_t RD_PROGRAM_1 = 128 + 65; 233 | const uint8_t RD_PROGRAM_2 = 128 + 66; 234 | const uint8_t RD_PROGRAM_3 = 128 + 67; 235 | 236 | const uint8_t RD_PROGRAM_4 = 128 + 68; 237 | const uint8_t RD_PROGRAM_5 = 128 + 69; 238 | const uint8_t RD_PROGRAM_6 = 128 + 70; 239 | const uint8_t RD_PROGRAM_7 = 128 + 71; 240 | 241 | const uint8_t RD_PROGRAM_8 = 128 + 72; 242 | const uint8_t RD_PROGRAM_9 = 128 + 73; 243 | const uint8_t RD_PROGRAM_10 = 128 + 74; 244 | const uint8_t RD_PROGRAM_11 = 128 + 75; 245 | 246 | const uint8_t RD_PROGRAM_12 = 128 + 76; 247 | const uint8_t RD_PROGRAM_13 = 128 + 77; 248 | const uint8_t RD_PROGRAM_14 = 128 + 78; 249 | const uint8_t RD_PROGRAM_15 = 128 + 79; 250 | 251 | const uint8_t WR_PROGRAM_0 = 128 + 80; 252 | const uint8_t WR_PROGRAM_1 = 128 + 81; 253 | const uint8_t WR_PROGRAM_2 = 128 + 82; 254 | const uint8_t WR_PROGRAM_3 = 128 + 83; 255 | 256 | const uint8_t WR_PROGRAM_4 = 128 + 84; 257 | const uint8_t WR_PROGRAM_5 = 128 + 85; 258 | const uint8_t WR_PROGRAM_6 = 128 + 86; 259 | const uint8_t WR_PROGRAM_7 = 128 + 87; 260 | 261 | const uint8_t WR_PROGRAM_8 = 128 + 88; 262 | const uint8_t WR_PROGRAM_9 = 128 + 89; 263 | const uint8_t WR_PROGRAM_10 = 128 + 90; 264 | const uint8_t WR_PROGRAM_11 = 128 + 91; 265 | 266 | const uint8_t WR_PROGRAM_12 = 128 + 92; 267 | const uint8_t WR_PROGRAM_13 = 128 + 93; 268 | const uint8_t WR_PROGRAM_14 = 128 + 94; 269 | const uint8_t WR_PROGRAM_15 = 128 + 95; 270 | 271 | const uint8_t RD_PANEL_PRMS = 128 + 96; 272 | const uint8_t IN_PANEL_PRMS = 128 + 97; 273 | 274 | const uint8_t WR_PANEL_PRMS = 128 + 100; 275 | 276 | const uint8_t SEQ_RAND_PITCH = 128 + 104; 277 | const uint8_t SEQ_RAND_VELO = 128 + 105; 278 | -------------------------------------------------------------------------------- /PRA32-U-Change-History.md: -------------------------------------------------------------------------------- 1 | ## Digital Synth PRA32-U Change History 2 | 3 | - v3.2.0 (2025-05-11): 4 | - Renew High Pass Filter: No noise when changing Resonance 5 | - Fix a problem with Voice Assign Mode not being set to Mode 2 when the value is 1 6 | - PRA32-U with Panel: Fix a problem with Sustain Pedal being displayed as On when the value is 1 7 | - Fix documentation 8 | - Other improvements 9 | - Tested with Arduino-Pico version 4.5.3 10 | - v3.1.0 (2025-01-19): 11 | - Add the Voice Assign Mode [1|2] parameter 12 | - Old versions is equivalent to Mode 2 13 | - Sine Wave (Phase Modulation): Change the unit of Frequency Ratio from 0.25 to 0.5 14 | - Sine Wave (Phase Modulation): Improve Frequency Ratio to change smoothly 15 | - Extend Osc 2 Pitch range (-5 to +7 -> -12 to +12 semitone) 16 | - Extend Pitch modulation range by EG Osc Amt and LFO Osc Amt (-24 to +24 -> -30 to +30 semitone) 17 | - PRA32-U with Panel: Change MIDI clock and commands not to be transmitted via USB MIDI 18 | - To avoid freeze problem when transmitting and receiving USB MIDI at the same time 19 | - PRA32-U with Panel: Add the Filter EG Amt parameter to the EG pages as well 20 | - PRA32-U with Panel: Rename the parameter name Seq Pattern to Seq Mode 21 | - PRA32-U with Panel: Reduce memory usage 22 | - Fix an issue where emulated EEPROM would not work with some compilation options 23 | - Other improvements 24 | - Tested with Arduino-Pico version 4.4.1 25 | - Overclock to 153.6 MHz (instead of 147.6 MHz) 26 | - v3.0.0 (2024-11-13): 27 | - Change Control Numbers: Osc 1 Wave 102 to 14, Voice Mode 14 to 102 28 | - Mixer Noise is not disabled even if Osc 2 Wave is Nos (Noise) 29 | - Change Preset programs: Change Delay Mode to S (Stereo Delay) 30 | - Rename PRA32-U CTRL to PRA32-U Editor 31 | - Officially support the option PRA32-U with Panel 32 | - Faster OLED display updates 33 | - Change the page layout and the default pages 34 | - Change the specification of Panel Scale parameter 35 | - Change the specification of Seq Last Step parameter and rename Seq Last Step to Seq Num Steps (Number of Steps) 36 | - Add Panel Pitch Ofst (Pitch Offset) parameter 37 | - Add Seq On Steps parameter 38 | - Add Seq Rand Pitch (Randomize Pitch 0-7) function 39 | - Add Seq Rand Velo (Randomize Velo 0-7) function 40 | - Fix Seq Gate Time behavior 41 | - Tested with Arduino-Pico version 4.2.0 42 | - v2.6.1 (2024-09-22): 43 | - Fix Bug: PWM Audio: Notes are played a whole tone higher 44 | - Other improvements 45 | - v2.6.0 (2024-09-15): 46 | - Extend "Delay Time" range (20-300 to 1-333.3 ms) 47 | - Update the option PRA32-U with Panel to Prototype 3 (experimental) 48 | - Add the parameter "Seq Step Note" (Quarter Note, Eighth Note, Sixteenth Note) 49 | - "Panel Play Pitch" and "Seq Pitch 0-7" displays reflect "Panel Transpose" (but not "Seq Transpose") 50 | - Fix "Panel Scale", "Panel Transpose", and "Seq Transpose" update timing in "Seq Pattern" Reverse and Bounce 51 | - Fix a problem with "Seq Pattern" displaying incorrectly when the value is 1 52 | - PRA32-U CTRL, PRA32-U with Panel: Add ms display of "Delay Time" 53 | - Fix MIDI Implementation Chart 54 | - Other improvements 55 | - Tested with Arduino-Pico version 4.0.2 56 | - v2.5.1 (2024-09-08): 57 | - Fix a problem with noise in PRA32-U CTRL Recall operation when PWM Audio is used 58 | - This problem occurred when 3 or 4 notes were playing in Poly Mode and PRA32_U_USE_DEBUG_PRINT was commented out (default) 59 | - Add description to Delay Time in Parameter Guide 60 | - v2.5.0 (2024-09-04): 61 | - PRA32-U with Panel: Support MIDI Clock and Start/Stop Commands transmission 62 | - Fix "pra32-u-make-sample-wav-file.cc" 63 | - Other changes 64 | - Tested with Arduino-Pico version 4.0.1 65 | - v2.4.1 (2024-09-01): 66 | - PRA32-U with Panel: Fix a problem with MIDI clock being processed even when sequencer is stopped 67 | - PRA32-U with Panel: Fix a problem where Start/Stop MIDI commands are processed even when Seq Clock Src is Internal 68 | - Fix MIDI Implementation Chart 69 | - Tested with Arduino-Pico version 4.0.1 70 | - v2.4.0 (2024-08-25): 71 | - Increase the output level using Extra Amp and Limiter 72 | - For safety, change the mode of the PWM audio output pins to OUTPUT_12MA 73 | - Update the option PRA32-U with Panel to Prototype 2 (experimental) 74 | - Change the page structure (Add the page groups A, B, C, and D) 75 | - Change the specification of "Panel Scale" 76 | - Add the parameter "Panel MIDI Ch" (Basic Channel) 77 | - Add Step Sequencer Mode ("Seq") to "Panel Play Mode" 78 | - Add the option PRA32_U_KEY_INPUT_PIN_MODE (default: INPUT_PULLDOWN) 79 | - Add the functions "Write Panel Prms", "Read Panel Prms", and "Init Panel Prms" 80 | - PRA32-U CTRL: Change the Note ON velocity to 64 81 | - Other changes 82 | - Tested with Arduino-Pico version 3.9.5 83 | - v2.3.1 (2024-04-21): 84 | - Abolish the option to write user programs to the flash when using PWM audio due to performance problem uncovered 85 | - Tested with Arduino-Pico version 3.7.2 86 | - v2.3.0 (2024-04-21): 87 | - Allow user programs to be written to the flash when using I2S DAC without mute-off pin 88 | - Add the option to write user programs to the flash when using PWM audio (for Raspberry Pi Pico/H/W/WH) 89 | - Add the option PRA32_U_I2S_SWAP_LEFT_AND_RIGHT 90 | - Add the option PRA32-U with Panel, Prototype 1 (experimental) 91 | - Change the default program #0 to #8 92 | - Use core 1 for Debug Print 93 | - Other changes 94 | - Tested with Arduino-Pico version 3.7.2 95 | - v2.2.2 (2024-02-10): 96 | - Fix a bug that the parameters are not written to the flash when using I2S DAC, even if PRA32_U_I2S_DAC_MUTE_OFF_PIN is defined 97 | - Tested with Arduino-Pico version 3.7.0 98 | - v2.2.1 (2024-02-04): 99 | - No more need to install Adafruit TinyUSB Library separately from Arduino-Pico 100 | - v2.2.0 (2024-01-26): 101 | - Use error diffusion for PWM audio output by default 102 | - Eliminate noise when PWM audio output is silent 103 | - Change the display of PRA32-U CTRL 104 | - Modify Presets; 105 | - Tested with Adafruit TinyUSB Library version 2.3.3 106 | - v2.1.0 (2024-01-14): 107 | - Reduce noise from PMIC (Turn off RT6150 Power Save); 108 | - Support Osc 1 Sine Wave Phase Modulation (Osc 1 Shape and Osc 1 Morph); 109 | - Add the function of writing the parameters to Program #8-15 and the flash; 110 | - Modify Presets; 111 | - Add JSON files for PRA32-U CTRL; 112 | - Use core 0 for main processing; 113 | - Rename constant macros; 114 | - Officially support PWM audio output option; 115 | - Rename Serial MIDI to UART MIDI; 116 | - Enable UART MIDI to be used at the same time as USB MIDI; 117 | - Use Serial1 for Debug Print and Serial2 for UART MIDI; 118 | - Raspberry Pi Pico/RP2040 core version 3.6.3 is recommended 119 | - v2.0.1 (2024-01-05): 120 | - Fix Ping Pong Delay to start from the left 121 | - Fix Parameter Guide (Filter Resonance) 122 | - v2.0.0 (2024-01-04): 123 | - Add Delay Mode (Ping Pong Delay); 124 | - Support Breath Controller (Breath Filter Amt and Breath Amp Mod); 125 | - Support Note ON Velocity (EG Velocity Sensitivity and Amp Velocity Sensitivity); 126 | - Modify EG and Amp EG; 127 | - Modify Presets; 128 | - Change the control numbers of Control Changes 129 | (Voice Mode: 27 -> 14, LFO Wave: 18 -> 12, Filter Mode: 86 -> 78, EG Amp Mod: 87 -> 28, and Chorus Mix: 93 -> 27); 130 | - Change the meanings of the values of Control Changes 131 | (Osc 1 Wave, Osc 2 Wave, EG Osc Dst, Voice Mode, LFO Wave, LFO Osc Dst, Filter Mode, EG Amp Mod, Release = Decay, Breath Amp Mod, and Delay Mode); 132 | - Increase DMA buffer size (audio latency: 2.7 ms -> 5.3 ms); 133 | - Use 2 cores for signal processing in Polyphonic and Paraphonic Modes; 134 | - Add PWM audio output option (experimental) 135 | - v1.2.1: Fixed an oscillation problem caused by Delay Feedback 136 | - v1.2.0: Renew High Pass Filter; Raspberry Pi Pico/RP2040 core version 3.6.2 is recommended 137 | - v1.1.1: Revert Filter clipping in High Pass mode 138 | - v1.1.0: Fix Filter clipping (behavior during oscillation); Extend Pitch Bend Range; 139 | Delete the code for Waveshare Pico-Audio Rev2.1 140 | - v1.0.3: Fix a problem with received MIDI messages being dropped; Improve README; Fix UF2 files 141 | - v1.0.2: Turn MIDI Thru Off 142 | - v1.0.1: Fix unstable PRA32-U CTRL operation when using Serial MIDI (DIN/TRS MIDI); Fix README 143 | - v1.0.0: Change Control Numbers of Control Changes; 144 | Change the meanings of the values of Osc 1/2 Wave, Voice Mode, and LFO Wave; Add Sine Wave to Osc 1 and 2; 145 | Limit the change range and the change rate of Osc 1 Shape; Change Noise specifications; 146 | Change Sub Osc Triangle Wave to Sine Wave; Change LFO Triangle Wave 2 to Sine Wave; 147 | Improve resolution of Filter Resonance; Limit the change rate of Filter Cutoff due to modulation; 148 | Change Amp Level to Amp Gain; Change Chorus Mode to Chorus Mix; Add Delay Fx; 149 | Delete Chorus Delay Time, Chorus Bypass, and Pitch Bend by CC; Update Presets; Other changes 150 | - v0.4.0 (Prototype): Extend Osc 2 Coarse range; Change Osc 2 Fine curve; Rename "Osc 2 Fine" to "Osc 2 Pitch"; 151 | Change LFO Osc/Filter Amt curve; Update Presets 152 | - v0.3.1 (Prototype): Adjust smoothing time when parameters change; Update Presets; Fix LFO Osc/Filter Amt; Other changes 153 | - v0.3.0 (Prototype): Extend Note Number range; Remove drift of Oscillators; Improve Triangle Wave; Change Portamento curve; 154 | Improve Filter Low Cut Mode; Rename Filter Mode LC (Low Cut) to HP (High Pass); Modify Filter coefficients; 155 | Change EG curve; Extend LFO Rate curve; Change LFO Fade Time; Change Chorue Rate curve; Update Presets; 156 | Fix Filter Key Track (Fix problem with strange sound when pitch is outside of note number range); 157 | Fix behavior when Portamento is 0 (Off); Other changes 158 | - v0.2.0 (Prototype): Change Sampling Rate to 48000 Hz; Support MCLK for I2S; Support Polyphonic Mode; 159 | Extend Filter Cutoff frequency change range to 10+ octaves; 160 | Change "Mixer Sub Osc" to "Mixer Noise/Sub Osc"; Add "EG Amp Mod", "Release = Decay", and "Filter Mode"; 161 | Enable Osc 1 Shape, Morph, Noise/Sub Osc, and Osc 2 in Paraphonic and Polyphonic Modes; Other changes 162 | - v0.1.0 (Prototype): The first release 163 | -------------------------------------------------------------------------------- /Digital-Synth-PRA32-U/pra32-u-control-panel-page-table.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | const uint8_t PAGE_GROUP_DEFAULT = 0; 4 | const uint8_t PAGE_INDEX_DEFAULT_A = 7; 5 | const uint8_t PAGE_INDEX_DEFAULT_B = 2; 6 | const uint8_t PAGE_INDEX_DEFAULT_C = 0; 7 | const uint8_t PAGE_INDEX_DEFAULT_D = 4; 8 | 9 | struct PRA32_U_ControlPanelPage { 10 | char page_name_line_0 [10 + 1]; 11 | char page_name_line_1 [10 + 1]; 12 | char control_target_a_name_line_0[10 + 1]; 13 | char control_target_a_name_line_1[10 + 1]; 14 | uint8_t control_target_a; 15 | char control_target_b_name_line_0[10 + 1]; 16 | char control_target_b_name_line_1[10 + 1]; 17 | uint8_t control_target_b; 18 | char control_target_c_name_line_0[10 + 1]; 19 | char control_target_c_name_line_1[10 + 1]; 20 | uint8_t control_target_c; 21 | }; 22 | 23 | static const PRA32_U_ControlPanelPage g_control_panel_page_table_a[] = { 24 | { "Page=A-00 ", "Info ", "PRA32-U ", "with Panel", 0xFF , " ",PRA32_U_VERSION, 0xFF , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 25 | { "Page=A-01 ", "Voice a ", "Voice ", "Mode ", VOICE_MODE , "Voice ", "Asgn Mode ", VOICE_ASGN_MODE, "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 26 | { "Page=A-02 ", "Voice b ", "Portamento", " ", PORTAMENTO , "Pitch ", "Bend Range", P_BEND_RANGE , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 27 | { "Page=A-03 ", "Osc a ", "Osc 1 ", "Wave ", OSC_1_WAVE , "Mixer ", "Noise/Sub ", MIXER_SUB_OSC , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 28 | { "Page=A-04 ", "Osc b ", "Osc 1 ", "Shape ", OSC_1_SHAPE , "Osc 1 ", "Morph ", OSC_1_MORPH , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 29 | { "Page=A-05 ", "Osc c ", "Osc 2 ", "Wave ", OSC_2_WAVE , "Mixer ", "Osc Mix ", MIXER_OSC_MIX , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 30 | { "Page=A-06 ", "Osc d ", "Osc 2 ", "Coarse ", OSC_2_COARSE , "Osc 2 ", "Pitch ", OSC_2_PITCH , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 31 | { "Page=A-07 ", "Filter a ", "Filter ", "Cutoff ", FILTER_CUTOFF , "Filter ", "Resonance ", FILTER_RESO , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 32 | { "Page=A-08 ", "Filter b ", "Filter ", "EG Amt ", FILTER_EG_AMT , "Filter ", "Key Track ", FILTER_KEY_TRK , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 33 | { "Page=A-09 ", "Filter c ", " ", " ", 0xFF , "Filter ", "Mode ", FILTER_MODE , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 34 | { "Page=A-10 ", "EG a ", "EG ", "Attack ", EG_ATTACK , "EG ", "Decay ", EG_DECAY , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 35 | { "Page=A-11 ", "EG b ", "EG ", "Sustain ", EG_SUSTAIN , "EG ", "Release ", EG_RELEASE , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 36 | { "Page=A-12 ", "EG c ", "EG ", "Amp Mod ", EG_AMP_MOD , "Release ", "= Decay ", REL_EQ_DECAY , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 37 | { "Page=A-13 ", "EG d ", "EG ", "Osc Amt ", EG_OSC_AMT , "EG ", "Osc Dst ", EG_OSC_DST , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 38 | { "Page=A-14 ", "EG e ", "Filter ", "EG Amt ", FILTER_EG_AMT , "EG ", "Velo Sens ", EG_VEL_SENS , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 39 | { "Page=A-15 ", "Amp a ", "Amp ", "Attack ", AMP_ATTACK , "Amp ", "Decay ", AMP_DECAY , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 40 | { "Page=A-16 ", "Amp c ", "Amp ", "Sustain ", AMP_SUSTAIN , "Amp ", "Release ", AMP_RELEASE , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 41 | { "Page=A-17 ", "Amp b ", "Amp ", "Gain ", AMP_GAIN , "Amp ", "Velo Sens ", AMP_VEL_SENS , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 42 | { "Page=A-18 ", "LFO a ", "LFO ", "Wave ", LFO_WAVE , "LFO ", "Fade Time ", LFO_FADE_TIME , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 43 | { "Page=A-19 ", "LFO b ", "LFO ", "Rate ", LFO_RATE , "LFO ", "Depth ", LFO_DEPTH , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 44 | { "Page=A-20 ", "LFO c ", "LFO ", "Osc Amt ", LFO_OSC_AMT , "LFO ", "Osc Dst ", LFO_OSC_DST , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 45 | { "Page=A-21 ", "LFO d ", "LFO ", "Filter Amt", LFO_FILTER_AMT , " ", " ", 0xFF , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 46 | { "Page=A-22 ", "Breath ", "Breath ", "Filter Amt", BTH_FILTER_AMT , "Breath ", "Amp Mod ", BTH_AMP_MOD , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 47 | { "Page=A-23 ", "Chorus a ", "Chorus ", "Mix ", CHORUS_MIX , " ", " ", 0xFF , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 48 | { "Page=A-24 ", "Chorus b ", "Chorus ", "Rate ", CHORUS_RATE , "Chorus ", "Depth ", CHORUS_DEPTH , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 49 | { "Page=A-25 ", "Delay a ", "Delay ", "Feedback ", DELAY_FEEDBACK , "Delay ", "Time ", DELAY_TIME , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 50 | { "Page=A-26 ", "Delay b ", "Delay ", "Mode ", DELAY_MODE , " ", " ", 0xFF , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 51 | }; 52 | 53 | static const PRA32_U_ControlPanelPage g_control_panel_page_table_b[] = { 54 | { "Page=B-00 ", "Panel a ", "Panel ", "Play Mode ", PANEL_PLAY_MODE, "Panel ", "MIDI Ch ", PANEL_MIDI_CH , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 55 | { "Page=B-01 ", "Panel b ", "Panel ", "Play Pitch", PANEL_PLAY_PIT , "Panel ", "Play Velo ", PANEL_PLAY_VELO, "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 56 | { "Page=B-02 ", "Panel c ", "Panel ", "Scale ", PANEL_SCALE , "Panel ", "Pitch Ofst", PANEL_PIT_OFST , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 57 | { "Page=B-03 ", "Panel d ", "Panel ", "Transpose ", PANEL_TRANSPOSE, " ", " ", 0xFF , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 58 | { "Page=B-04 ", "Seq a ", "Seq ", "Step Note ", SEQ_STEP_NOTE , "Seq ", "Clock Src ", SEQ_CLOCK_SRC , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 59 | { "Page=B-05 ", "Seq b ", "Seq ", "Transpose ", SEQ_TRANSPOSE , " ", " ", 0xFF , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 60 | { "Page=B-06 ", "Seq c ", "Seq ", "Tempo ", SEQ_TEMPO , "Seq ", "Gate Time ", SEQ_GATE_TIME , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 61 | { "Page=B-07 ", "Seq d ", "Seq ", "Mode ", SEQ_MODE , "Seq ", "Num Steps ", SEQ_NUM_STEPS , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 62 | { "Page=B-08 ", "Seq e ", "Seq ", "On Steps ", SEQ_ON_STEPS , "Seq ", "Act Steps ", SEQ_ACT_STEPS , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 63 | { "Page=B-09 ", "Seq f ", "Seq ", "Rand Pitch", SEQ_RAND_PITCH , "Seq ", "Rand Velo ", SEQ_RAND_VELO , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 64 | { "Page=B-10 ", "Seq 0 ", "Seq ", "Pitch 0 ", SEQ_PITCH_0 , "Seq ", "Velo 0 ", SEQ_VELO_0 , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 65 | { "Page=B-11 ", "Seq 1 ", "Seq ", "Pitch 1 ", SEQ_PITCH_1 , "Seq ", "Velo 1 ", SEQ_VELO_1 , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 66 | { "Page=B-12 ", "Seq 2 ", "Seq ", "Pitch 2 ", SEQ_PITCH_2 , "Seq ", "Velo 2 ", SEQ_VELO_2 , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 67 | { "Page=B-13 ", "Seq 3 ", "Seq ", "Pitch 3 ", SEQ_PITCH_3 , "Seq ", "Velo 3 ", SEQ_VELO_3 , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 68 | { "Page=B-14 ", "Seq 4 ", "Seq ", "Pitch 4 ", SEQ_PITCH_4 , "Seq ", "Velo 4 ", SEQ_VELO_4 , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 69 | { "Page=B-15 ", "Seq 5 ", "Seq ", "Pitch 5 ", SEQ_PITCH_5 , "Seq ", "Velo 5 ", SEQ_VELO_5 , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 70 | { "Page=B-16 ", "Seq 6 ", "Seq ", "Pitch 6 ", SEQ_PITCH_6 , "Seq ", "Velo 6 ", SEQ_VELO_6 , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 71 | { "Page=B-17 ", "Seq 7 ", "Seq ", "Pitch 7 ", SEQ_PITCH_7 , "Seq ", "Velo 7 ", SEQ_VELO_7 , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 72 | { "Page=B-18 ", "Control a ", "Modulation", " ", MODULATION , " ", " ", 0xFF , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 73 | { "Page=B-19 ", "Control b ", "Breath ", "Controller", BTH_CONTROLLER , "Sustain ", "Pedal ", SUSTAIN_PEDAL , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 74 | }; 75 | 76 | static const PRA32_U_ControlPanelPage g_control_panel_page_table_c[] = { 77 | { "Page=C-00 ", "Write 8 ", "Write ", "Program 8", WR_PROGRAM_8 , "Write ", "Program 9", WR_PROGRAM_9 , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 78 | { "Page=C-01 ", "Write 10 ", "Write ", "Program 10", WR_PROGRAM_10 , "Write ", "Program 11", WR_PROGRAM_11 , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 79 | { "Page=C-02 ", "Write 12 ", "Write ", "Program 12", WR_PROGRAM_12 , "Write ", "Program 13", WR_PROGRAM_13 , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 80 | { "Page=C-03 ", "Write 14 ", "Write ", "Program 14", WR_PROGRAM_14 , "Write ", "Program 15", WR_PROGRAM_15 , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 81 | { "Page=C-04 ", "Write a ", "Write ", "Panel Prms", WR_PANEL_PRMS , " ", " ", 0xFF , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 82 | }; 83 | 84 | static const PRA32_U_ControlPanelPage g_control_panel_page_table_d[] = { 85 | { "Page=D-00 ", "Read 0 ", "Read ", "Program 0", RD_PROGRAM_0 , "Read ", "Program 1", RD_PROGRAM_1 , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 86 | { "Page=D-01 ", "Read 2 ", "Read ", "Program 2", RD_PROGRAM_2 , "Read ", "Program 3", RD_PROGRAM_3 , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 87 | { "Page=D-02 ", "Read 4 ", "Read ", "Program 4", RD_PROGRAM_4 , "Read ", "Program 5", RD_PROGRAM_5 , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 88 | { "Page=D-03 ", "Read 6 ", "Read ", "Program 6", RD_PROGRAM_6 , "Read ", "Program 7", RD_PROGRAM_7 , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 89 | { "Page=D-04 ", "Read 8 ", "Read ", "Program 8", RD_PROGRAM_8 , "Read ", "Program 9", RD_PROGRAM_9 , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 90 | { "Page=D-05 ", "Read 10 ", "Read ", "Program 10", RD_PROGRAM_10 , "Read ", "Program 11", RD_PROGRAM_11 , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 91 | { "Page=D-06 ", "Read 12 ", "Read ", "Program 12", RD_PROGRAM_12 , "Read ", "Program 13", RD_PROGRAM_13 , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 92 | { "Page=D-07 ", "Read 14 ", "Read ", "Program 14", RD_PROGRAM_14 , "Read ", "Program 15", RD_PROGRAM_15 , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 93 | { "Page=D-08 ", "Read a ", "Read ", "Panel Prms", RD_PANEL_PRMS , "Init ", "Panel Prms", IN_PANEL_PRMS , "Panel ", "Play Pitch", PANEL_PLAY_PIT , }, 94 | }; 95 | 96 | const PRA32_U_ControlPanelPage* g_control_panel_page_table[] = { 97 | g_control_panel_page_table_a, 98 | g_control_panel_page_table_b, 99 | g_control_panel_page_table_c, 100 | g_control_panel_page_table_d, 101 | }; 102 | 103 | uint32_t g_number_of_pages[] = { 104 | (sizeof(g_control_panel_page_table_a) / sizeof(g_control_panel_page_table_a[0])), 105 | (sizeof(g_control_panel_page_table_b) / sizeof(g_control_panel_page_table_b[0])), 106 | (sizeof(g_control_panel_page_table_c) / sizeof(g_control_panel_page_table_c[0])), 107 | (sizeof(g_control_panel_page_table_d) / sizeof(g_control_panel_page_table_d[0])), 108 | }; 109 | 110 | #define NUMBER_OF_PAGE_GROUPS (sizeof(g_control_panel_page_table) / sizeof(g_control_panel_page_table[0])) 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Digital Synth PRA32-U v3.2.0 2 | 3 | - 2025-05-11 ISGK Instruments 4 | - 5 | 6 | 7 | ## Overview 8 | 9 | - 4 Voice Polyphonic/Paraphonic Synthesizer for Raspberry Pi Pico/RP2040 10 | - Built-in Chorus and Delay FX 11 | - Controlled by MIDI -- PRA32-U is a MIDI sound module 12 | - Having the function of writing the parameters to the user programs and the flash 13 | - Modifiable with Arduino IDE and Arduino-Pico (by Earle F. Philhower, III) 14 | - An **I2S DAC** hardware (e.g. Pimoroni Pico Audio Pack) is required 15 | - PWM Audio can also be used instead of I2S (PWM Audio does not require an I2S DAC hardware) 16 | - Prebuilt UF2 files ("bin") 17 | - "Digital-Synth-PRA32-U-Pimoroni-Pico-Audio-Pack.uf2" is for Raspberry Pi Pico and Pimoroni Pico Audio Pack 18 | - "Digital-Synth-PRA32-U-PWM-Audio.uf2" is for Raspberry Pi Pico and PWM Audio 19 | 20 | 21 | ## [Change History](/PRA32-U-Change-History.md) 22 | 23 | 24 | ## Preparation for modification 25 | 26 | - Please install **Arduino IDE** 27 | - NOTE: Large noise is generated during the sketch upload if other than Update Method: "Default (UF2)" is used 28 | - Info: 29 | - Please install Arduino-Pico = **Raspberry Pi Pico/RP2040/RP2350** (by Earle F. Philhower, III) core 30 | - Additional Board Manager URL: 31 | - This sketch is tested with version **4.5.3**: 32 | - Info: 33 | - Please install Arduino **MIDI Library** (by Francois Best, lathoub) 34 | - This sketch is tested with version **5.0.2**: 35 | - Info: 36 | 37 | 38 | ## Features 39 | 40 | ### MIDI (Input) 41 | 42 | #### USB MIDI Device (Default) 43 | 44 | - MIDI Device Name: "Digital Synth PRA32-U" 45 | - NOTE: Select USB Stack: "Adafruit TinyUSB" in the Arduino IDE "Tools" menu 46 | 47 | 48 | #### UART MIDI (Optional) 49 | 50 | - UART MIDI can also be used 51 | - Noise caused by USB communication can be avoided 52 | - Uncomment out `//#define PRA32_U_USE_UART_MIDI` in "Digital-Synth-PRA32-U.ino" 53 | and modify `PRA32_U_UART_MIDI_SPEED`, `PRA32_U_UART_MIDI_TX_PIN`, and `PRA32_U_UART_MIDI_RX_PIN` 54 | - Speed: 31250 bps (default, for DIN/TRS MIDI) or 38400 bps (for PC) 55 | - GP4 and GP5 pins are used by UART1 TX and UART1 RX by default 56 | - DIN/TRS MIDI is available by using (and modifying) Adafruit MIDI FeatherWing Kit, for example 57 | - Adafruit [MIDI FeatherWing Kit](https://www.adafruit.com/product/4740) (Product ID: 4740) 58 | - M5Stack [Midi Unit with DIN Connector (SAM2695)](https://shop.m5stack.com/products/midi-unit-with-din-connector-sam2695) (SKU: U187) in Separate mode 59 | - 木下研究所 [MIDI-UARTインターフェースさん キット](https://www.switch-science.com/products/8117) (Shipping to Japan only) 60 | - necobit電子 [MIDI Unit for GROVE](https://necobit.com/denshi/grove-midi-unit/) (Shipping to Japan only) 61 | - We recommend using [Hairless MIDI<->Serial Bridge](https://projectgus.github.io/hairless-midiserial/) on PC 62 | - On Windows, We recommend using [loopMIDI](https://www.tobias-erichsen.de/software/loopmidi.html) (virtual loopback MIDI cable) 63 | - On Mac, a virtual MIDI bus (port) can be created by using the IAC bus 64 | 65 | 66 | ### Audio (Output) 67 | 68 | #### I2S (Default) 69 | 70 | - Use an I2S DAC (Texas Instruments PCM5100A, PCM5101A, or PCM5102A is recommended), Sampling Rate: 48 kHz, Bit Depth: 16 bit 71 | - NOTE: The RP2040 system clock (sysclk) changes to overclocked 153.6 MHz by I2S Audio Library setSysClk() 72 | - Modify `PRA32_U_I2S_DAC_MUTE_OFF_PIN`, `PRA32_U_I2S_DATA_PIN`, `PRA32_U_I2S_MCLK_PIN`, `PRA32_U_I2S_MCLK_MULT`, 73 | `PRA32_U_I2S_BCLK_PIN`, `PRA32_U_I2S_SWAP_BCLK_AND_LRCLK_PINS`, and `PRA32_U_I2S_SWAP_LEFT_AND_RIGHT` 74 | in "Digital-Synth-PRA32-U.ino" to match the hardware configuration 75 | - The default setting is for Pimoroni [Pico Audio Pack](https://shop.pimoroni.com/products/pico-audio-pack) (PIM544) 76 | - GY-PCM5102 (PCM5102A I2S DAC Module) can also be used 77 | ``` 78 | #define PRA32_U_I2S_DAC_MUTE_OFF_PIN (22) 79 | #define PRA32_U_I2S_DATA_PIN (9) 80 | //#define PRA32_U_I2S_MCLK_PIN (0) 81 | //#define PRA32_U_I2S_MCLK_MULT (0) 82 | #define PRA32_U_I2S_BCLK_PIN (10) // LRCLK Pin is PRA32_U_I2S_BCLK_PIN + 1 83 | #define PRA32_U_I2S_SWAP_BCLK_AND_LRCLK_PINS (false) 84 | #define PRA32_U_I2S_SWAP_LEFT_AND_RIGHT (false) 85 | ``` 86 | - The following is setting is for [Pimoroni Pico VGA Demo Base](https://shop.pimoroni.com/products/pimoroni-pico-vga-demo-base) (PIM553) 87 | ``` 88 | //#define PRA32_U_I2S_DAC_MUTE_OFF_PIN (0) 89 | #define PRA32_U_I2S_DATA_PIN (26) 90 | //#define PRA32_U_I2S_MCLK_PIN (0) 91 | //#define PRA32_U_I2S_MCLK_MULT (0) 92 | #define PRA32_U_I2S_BCLK_PIN (27) // LRCLK Pin is is PRA32_U_I2S_BCLK_PIN + 1 93 | #define PRA32_U_I2S_SWAP_BCLK_AND_LRCLK_PINS (false) 94 | #define PRA32_U_I2S_SWAP_LEFT_AND_RIGHT (false) 95 | ``` 96 | - The following is setting is for [Waveshare Pico-Audio](https://www.waveshare.com/wiki/Pico-Audio) Rev2.1 Version (WAVESHARE-20167) 97 | ``` 98 | //#define PRA32_U_I2S_DAC_MUTE_OFF_PIN (0) 99 | #define PRA32_U_I2S_DATA_PIN (22) 100 | #define PRA32_U_I2S_MCLK_PIN (26) 101 | #define PRA32_U_I2S_MCLK_MULT (256) 102 | #define PRA32_U_I2S_BCLK_PIN (27) // LRCLK Pin is is PRA32_U_I2S_BCLK_PIN + 1 103 | #define PRA32_U_I2S_SWAP_BCLK_AND_LRCLK_PINS (true) 104 | #define PRA32_U_I2S_SWAP_LEFT_AND_RIGHT (true) 105 | ``` 106 | 107 | 108 | #### PWM Audio (Optional) 109 | 110 | - PWM Audio can also be used instead of I2S 111 | - NOTE: Probably smaller output volume than I2S DAC boards 112 | - NOTE: To avoid noise, the parameters will not be written to the flash when using PWM audio 113 | - We recommend adding RC filter (post LPF) circuits to reduce PWM ripples 114 | - A 1st-order LPFs with a cutoff frequency 7.2 kHz (R = 220 ohm, C = 100 nF) works well 115 | - See "PWM audio" in [Hardware design with RP2040](https://datasheets.raspberrypi.com/rp2040/hardware-design-with-rp2040.pdf) 116 | for details on PWM audio 117 | - NOTE: Select CPU Speed: "150 MHz (Overclock)" in the Arduino IDE "Tools" menu 118 | - Uncomment out `//#define PRA32_U_USE_PWM_AUDIO_INSTEAD_OF_I2S` 119 | in "Digital-Synth-PRA32-U.ino" and modify `PRA32_U_PWM_AUDIO_L_PIN` and `PRA32_U_PWM_AUDIO_R_PIN` 120 | - The following is setting is for Pimoroni Pico VGA Demo Base (PIM553) 121 | ``` 122 | #define PRA32_U_PWM_AUDIO_L_PIN (28) 123 | #define PRA32_U_PWM_AUDIO_R_PIN (27) 124 | ``` 125 | 126 | 127 | ## Files 128 | 129 | - "Digital-Synth-PRA32-U.ino" is a Arduino sketch for Raspberry Pi Pico/RP2040/RP2350 core 130 | - Modify `PRA32_U_MIDI_CH` to change the MIDI Channel 131 | - "pra32-u-make-sample-wav-file.cc" is for debugging on PC 132 | - GCC (g++) for PC is required 133 | - "pra32-u-make-sample-wav-file-cc.bat" makes a sample WAV file (working on Windows) 134 | - "pra32-u-generate-*.rb" generates source or header files 135 | - A Ruby execution environment is required 136 | 137 | 138 | ## PRA32-U Editor 139 | 140 | - "pra32-u-editor.html": Editor (MIDI Controller) Application for PRA32-U, HTML App (Web App) 141 | - Modify `PRA32_U_MIDI_CH` to change the MIDI Channel 142 | - We recommend using Google Chrome, which implements Web MIDI API 143 | - Select "Digital Synth PRA32-U" in the list "MIDI Out" 144 | - Functions 145 | - PRA32-U Editor converts Program Changes (#0-7 for Preset programs, #8-15 for user programs) into Control Changes 146 | - When Program Change #127 is entered or Control Change #111 is changed from Off (63 or lower) to On (64 or higher), "Rand Ctrl" is processed 147 | - PRA32-U Editor stores the current control values and the user programs (#8-15) in a Web browser (localStorage) 148 | - Current parameter values and user programs (#8-15) can be imported/exported from/to JSON files 149 | - When not using PRA32-U Editor 150 | - PRA32-U can also be controlled by MIDI without using PRA32-U Editor 151 | - Refer to "PRA32-U-MIDI-Implementation-Chart.txt" for the supported functions 152 | - The default program is #8 153 | - Programs #0-15 can be modified by editing "pra32-u-program-table.h" 154 | - PRA32-U Editor functions related to parameter writing 155 | - Write: Write the current parameters to PRA32-U (Program #8-15 and the flash) 156 | - Program Change: Send Program Change to PRA32-U directry 157 | (NOTE: The current parameters of PRA32-U will not be updated) 158 | 159 | 160 | ## Examples of Option Combinations 161 | 162 | - PRA32-U (USB MIDI Device, I2S), Default 163 | - PRA32-U (USB MIDI Device, UART MIDI, I2S) 164 | - PRA32-U (USB MIDI Device, PWM Audio) 165 | - PRA32-U with Panel (USB MIDI Device, UART MIDI, I2S, Control Panel) 166 | 167 | 168 | ## [Parameter Guide](/PRA32-U-Parameter-Guide.md) 169 | 170 | 171 | ## [MIDI Implementation Chart](/PRA32-U-MIDI-Implementation-Chart.md) 172 | 173 | 174 | ## Synthesizer Block Diagram 175 | 176 | ### Polyphonic Mode 177 | 178 | ```mermaid 179 | graph LR 180 | subgraph V1[Voice 1] 181 | V1O1[Osc 1 w/ Sub Osc] --> V1OM[Osc Mixer] 182 | V1O2[Osc 2] --> V1OM 183 | V1OM --> V1F[Filter] 184 | V1F --> V1A[Amp] 185 | E[EG] -.-> V1O1 & V1O2 & V1F 186 | V1AE[Amp EG] -.-> V1A 187 | end 188 | V1A --> VM[Voice Mixer] 189 | V2[Voice 2] & V3[Voice 3] & V4[Voice 4] --> VM 190 | VM --> C[Chorus FX] --> D[Delay FX] --> AO[Audio Out] 191 | C --> D 192 | D --> AO 193 | N[Noise Gen] --> V1O2 & V1OM & V2 & V3 & V4 194 | N -.-> L[LFO w/ S/H] 195 | L -.-> V1O1 & V1O2 & V1F & V2 & V3 & V4 196 | ``` 197 | 198 | 199 | ### Paraphonic Mode 200 | 201 | ```mermaid 202 | graph LR 203 | subgraph V1[Voice 1] 204 | V1O1[Osc 1 w/ Sub Osc] --> V1OM[Osc Mixer] 205 | V1O2[Osc 2] --> V1OM 206 | V1OM --> V1G[Gate] 207 | end 208 | V1G --> VM[Voice Mixer] 209 | V2[Voice 2] & V3[Voice 3] & V4[Voice 4] --> VM 210 | VM --> F[Filter] --> A[Amp] --> C[Chorus FX] --> D[Delay FX] --> AO[Audio Out] 211 | C --> D 212 | D --> AO 213 | N[Noise Gen] --> V1O2 & V1OM & V2 & V3 & V4 214 | N -.-> L[LFO w/ S/H] 215 | L -.-> V1O1 & V1O2 & V2 & V3 & V4 & F 216 | E[EG] -.-> V1O1 & V1O2 & V2 & V3 & V4 & F 217 | AE[Amp EG] -.-> A 218 | ``` 219 | 220 | 221 | ### Monophonic Mode 222 | 223 | ```mermaid 224 | graph LR 225 | O1[Osc 1 w/ Sub Osc] --> OM[Osc Mixer] 226 | O2[Osc 2] --> OM 227 | OM --> F[Filter] --> A[Amp] --> C[Chorus FX] --> D[Delay FX] --> AO[Audio Out] 228 | C --> D 229 | D --> AO 230 | N[Noise Gen] --> O2 & OM 231 | N -.-> L[LFO w/ S/H] 232 | L -.-> O1 & O2 & F 233 | E[EG] -.-> O1 & O2 & F 234 | AE[Amp EG] -.-> A 235 | ``` 236 | 237 | 238 | ## Simple Circuit for PWM Audio 239 | 240 | ### Circuit Diagram (Simple Circuit for PWM Audio) 241 | 242 | ![Circuit Diagram](./pra32-u-pwm-audio-circuit-diagram.png) 243 | 244 | - This image was created with Fritzing. 245 | - Adding 10 uF electrolytic capacitors (AC coupling capacitors) will cut the DC components of the audio outputs. 246 | - NOTE: Connect an amplifier or an active speaker to the audio jack. 247 | Connecting a headphone or a passive speaker may cause a large current to flow and damage the devices. 248 | 249 | 250 | ### Actual Wiring Diagram (Simple Circuit for PWM Audio) 251 | 252 | ![Actual Wiring Diagram](./pra32-u-pwm-audio-bread-board.png) 253 | 254 | - This image was created with Fritzing. 255 | 256 | 257 | ## [PRA32-U with Panel](./README-PRA32-U-with-Panel.md) (Optional) 258 | 259 | 260 | ## License 261 | 262 | ![CC0](http://i.creativecommons.org/p/zero/1.0/88x31.png) 263 | 264 | **Digital Synth PRA32-U v3.2.0 by ISGK Instruments (Ryo Ishigaki)** 265 | 266 | To the extent possible under law, ISGK Instruments (Ryo Ishigaki) 267 | has waived all copyright and related or neighboring rights 268 | to Digital Synth PRA32-U v3.2.0. 269 | 270 | You should have received a copy of the CC0 legalcode along with this 271 | work. If not, see . 272 | 273 | 274 | ### For Your Information 275 | 276 | If PRA32-U is to be embedded in instruments or others, it would be nice 277 | (but not required) to display the following: 278 | 279 | - Powered by ISGK Instruments PRA32-U 280 | - Powered by PRA32-U 281 | -------------------------------------------------------------------------------- /PRA32-U-MIDI-Implementation-Chart.md: -------------------------------------------------------------------------------- 1 | ``` 2 | [Polyphonic/Paraphonic Synthesizer] Date: 2025-05-11 3 | Model: Digital Synth PRA32-U MIDI Implementation Chart Version: 3.2.0 4 | +-------------------------------+---------------+---------------+-------------------------------------+ 5 | | Function... | Transmitted | Recognized | Remarks | 6 | +-------------------------------+---------------+---------------+-------------------------------------+ 7 | | Basic Default | x | 1 | | 8 | | Channel Changed | x | x | $5 | 9 | +-------------------------------+---------------+---------------+-------------------------------------+ 10 | | Mode Default | x | Mode 3 | | 11 | | Messages | x | x | | 12 | | Altered | ************* | | | 13 | +-------------------------------+---------------+---------------+-------------------------------------+ 14 | | Note | x | 0-127 | | 15 | | Number : True Voice | ************* | 0-127 | | 16 | +-------------------------------+---------------+---------------+-------------------------------------+ 17 | | Velocity Note ON | x | o | | 18 | | Note OFF | x | x | | 19 | +-------------------------------+---------------+---------------+-------------------------------------+ 20 | | After Key's | x | x | | 21 | | Touch Ch's | x | x | | 22 | +-------------------------------+---------------+---------------+-------------------------------------+ 23 | | Pitch Bend | x | o | | 24 | +-------------------------------+---------------+---------------+-------------------------------------+ 25 | | Control 1 | x | o | Modulation | 26 | | Change 2 | x | o | Breath Controller | 27 | | 64 | x | o | Sustain Pedal [Off|On] | 28 | | | | | | 29 | | 14 | x | o | Osc 1 Wave [Saw|Sin|-|Tri|-|Pls] | 30 | | 19 | x | o | Osc 1 Shape $2 | 31 | | 20 | x | o | Osc 1 Morph $2 | 32 | | 23 | x | o | Mixer Noise/Sub Osc [N|S] | 33 | | | | | | 34 | | 104 | x | o | Osc 2 Wave [Saw|Sin|-|Tri|Nos|Sqr] | 35 | | 85 | x | o | Osc 2 Coarse [-|+] | 36 | | 76 | x | o | Osc 2 Pitch [-|+] | 37 | | 21 | x | o | Mixer Osc Mix [1|2] | 38 | | | | | | 39 | | 74 | x | o | Filter Cutoff | 40 | | 71 | x | o | Filter Resonance | 41 | | 24 | x | o | Filter EG Amt [-|+] | 42 | | 26 | x | o | Filter Key Track [0.0|0.5|1.0] $1 | 43 | | | | | | 44 | | 73 | x | o | EG Attack | 45 | | 75 | x | o | EG Decay | 46 | | 30 | x | o | EG Sustain | 47 | | 72 | x | o | EG Release | 48 | | | | | | 49 | | 91 | x | o | EG Osc Amt [-|+] | 50 | | 89 | x | o | EG Osc Dst [P|2P|1S] | 51 | | 102 | x | o | Voice Mode [Pol|Par|-|Mon|LP|Lgt] | 52 | | 5 | x | o | Portamento | 53 | | | | | | 54 | | 12 | x | o | LFO Wave [Tri|Sin|-|Saw|SH|Sqr] | 55 | | 3 | x | o | LFO Rate | 56 | | 17 | x | o | LFO Depth | 57 | | 56 | x | o | LFO Fade Time | 58 | | | | | | 59 | | 13 | x | o | LFO Osc Amt [-|+] | 60 | | 103 | x | o | LFO Osc Dst [P|2P|1S] | 61 | | 25 | x | o | LFO Filter Amt [-|+] | 62 | | 15 | x | o | Amp Gain | 63 | | | | | | 64 | | 52 | x | o | Amp Attack | 65 | | 53 | x | o | Amp Decay | 66 | | 54 | x | o | Amp Sustain | 67 | | 55 | x | o | Amp Release | 68 | | | | | | 69 | | 78 | x | o | Filter Mode [LP|HP] | 70 | | 28 | x | o | EG Amp Mod [Off|On] | 71 | | 29 | x | o | Release = Decay [Off|On] | 72 | | 57 | x | o | Pitch Bend Range | 73 | | | | | | 74 | | 60 | x | o | Breath Filter Amt [-|+] | 75 | | 61 | x | o | Breath Amp Mod [Off|Qad|Lin] | 76 | | 62 | x | o | EG Velocity Sensitivity | 77 | | 63 | x | o | Amp Velocity Sensitivity | 78 | | | | | | 79 | | 108 | x | o | Voice Assign Mode [1|2] | 80 | | | | | | 81 | | | | | | 82 | | | | | | 83 | | | | | | 84 | | 27 | x | o | Chorus Mix [Dry|Wet] | 85 | | 58 | x | o | Chorus Rate | 86 | | 59 | x | o | Chorus Depth | 87 | | | | | | 88 | | | | | | 89 | | 92 | x | o | Delay Feedback | 90 | | 90 | x | o | Delay Time | 91 | | 35 | x | o | Delay Mode [S|P] | 92 | | | | | | 93 | | | | | | 94 | | 87 | x | o | Program Number to Write to $4 | 95 | | 106 | x | o | Write Parameters to Program $4 | 96 | | 112 | x | o | Program Change #8 by CC | 97 | | 113 | x | o | Program Change #9 by CC | 98 | | 114 | x | o | Program Change #10 by CC | 99 | | 115 | x | o | Program Change #11 by CC | 100 | | 116 | x | o | Program Change #12 by CC | 101 | | 117 | x | o | Program Change #13 by CC | 102 | | 118 | x | o | Program Change #14 by CC | 103 | | 119 | x | o | Program Change #15 by CC | 104 | | 111 | x | x | [Reserved] | 105 | +-------------------------------+---------------+---------------+-------------------------------------+ 106 | | Program | x | o | | 107 | | Change : True # | ************* | 0-15 | Default 8 | 108 | +-------------------------------+---------------+---------------+-------------------------------------+ 109 | | System Exclusive | x | x | | 110 | +-------------------------------+---------------+---------------+-------------------------------------+ 111 | | System : Song Pos | x | x | | 112 | | Common : Song Sel | x | x | | 113 | | : Tune | x | x | | 114 | +-------------------------------+---------------+---------------+-------------------------------------+ 115 | | System : Clock | x $6 | x $7 | | 116 | | Real Time : Commands | x $6 | x $7 | Compatible only with Start/Stop | 117 | +-------------------------------+---------------+---------------+-------------------------------------+ 118 | | Aux : All Sound OFF | x | o 120 | | 119 | | Messages : Reset All | x | o 121 | | 120 | | Controllers | | | | 121 | | : Local ON/OFF | x | x | | 122 | | : All Notes OFF | x | o 123-127 | | 123 | | : Active Sense | x | x | | 124 | | : Reset | x | x | | 125 | +-------------------------------+---------------+---------------+-------------------------------------+ 126 | | Notes | $1 : Disabled in Paraphonic Mode | 127 | | | $2 : Disabled if Osc 1 Wave is not Sin or Pls | 128 | | | $4 : To write the current parameters to Program #8-15 and the | 129 | | | flash, set "Program Number to Write to" (# is the value mod 16) | 130 | | | and then change "Write Parameters to Program" from 0 to 1-127 | 131 | | | $5 : Basic Channel can be changed in PRA32-U with Panel | 132 | | | $6 : o in PRA32-U with Panel (No transmission via USB MIDI) | 133 | | | $7 : o in PRA32-U with Panel if Seq Clock Src is External | 134 | +-------------------------------+---------------------------------------------------------------------+ 135 | Mode 1: Omni On, Poly Mode 2: Omni On, Mono o: Yes 136 | Mode 3: Omni Off, Poly Mode 4: Omni Off, Mono x: No 137 | ``` 138 | -------------------------------------------------------------------------------- /Digital-Synth-PRA32-U/pra32-u-control-panel-font-table.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | uint8_t g_control_panel_font_table[128][6] = { 4 | 0b01111111, 5 | 0b01111111, 6 | 0b01111111, 7 | 0b01111111, 8 | 0b01111111, 9 | 0b00000000, 10 | 0b01111111, 11 | 0b01000001, 12 | 0b01000001, 13 | 0b01000001, 14 | 0b01111111, 15 | 0b00000000, 16 | 0b01111111, 17 | 0b01000001, 18 | 0b01000001, 19 | 0b01000001, 20 | 0b01111111, 21 | 0b00000000, 22 | 0b01111111, 23 | 0b01000001, 24 | 0b01000001, 25 | 0b01000001, 26 | 0b01111111, 27 | 0b00000000, 28 | 0b01111111, 29 | 0b01000001, 30 | 0b01000001, 31 | 0b01000001, 32 | 0b01111111, 33 | 0b00000000, 34 | 0b01111111, 35 | 0b01000001, 36 | 0b01000001, 37 | 0b01000001, 38 | 0b01111111, 39 | 0b00000000, 40 | 0b01111111, 41 | 0b01000001, 42 | 0b01000001, 43 | 0b01000001, 44 | 0b01111111, 45 | 0b00000000, 46 | 0b01111111, 47 | 0b01000001, 48 | 0b01000001, 49 | 0b01000001, 50 | 0b01111111, 51 | 0b00000000, 52 | 0b01111111, 53 | 0b01000001, 54 | 0b01000001, 55 | 0b01000001, 56 | 0b01111111, 57 | 0b00000000, 58 | 0b01111111, 59 | 0b01000001, 60 | 0b01000001, 61 | 0b01000001, 62 | 0b01111111, 63 | 0b00000000, 64 | 0b01111111, 65 | 0b01000001, 66 | 0b01000001, 67 | 0b01000001, 68 | 0b01111111, 69 | 0b00000000, 70 | 0b01111111, 71 | 0b01000001, 72 | 0b01000001, 73 | 0b01000001, 74 | 0b01111111, 75 | 0b00000000, 76 | 0b01111111, 77 | 0b01000001, 78 | 0b01000001, 79 | 0b01000001, 80 | 0b01111111, 81 | 0b00000000, 82 | 0b01111111, 83 | 0b01000001, 84 | 0b01000001, 85 | 0b01000001, 86 | 0b01111111, 87 | 0b00000000, 88 | 0b01111111, 89 | 0b01000001, 90 | 0b01000001, 91 | 0b01000001, 92 | 0b01111111, 93 | 0b00000000, 94 | 0b01111111, 95 | 0b01000001, 96 | 0b01000001, 97 | 0b01000001, 98 | 0b01111111, 99 | 0b00000000, 100 | 0b01111111, 101 | 0b01111111, 102 | 0b01111111, 103 | 0b01111111, 104 | 0b01111111, 105 | 0b00000000, 106 | 0b01111111, 107 | 0b01000001, 108 | 0b01000001, 109 | 0b01000001, 110 | 0b01111111, 111 | 0b00000000, 112 | 0b01111111, 113 | 0b01000001, 114 | 0b01000001, 115 | 0b01000001, 116 | 0b01111111, 117 | 0b00000000, 118 | 0b00010100, 119 | 0b01111111, 120 | 0b00010100, 121 | 0b01111111, 122 | 0b00010100, 123 | 0b00000000, 124 | 0b01111111, 125 | 0b01000001, 126 | 0b01000001, 127 | 0b01000001, 128 | 0b01111111, 129 | 0b00000000, 130 | 0b01111111, 131 | 0b01000001, 132 | 0b01000001, 133 | 0b01000001, 134 | 0b01111111, 135 | 0b00000000, 136 | 0b01111111, 137 | 0b01000001, 138 | 0b01000001, 139 | 0b01000001, 140 | 0b01111111, 141 | 0b00000000, 142 | 0b01111111, 143 | 0b01000001, 144 | 0b01000001, 145 | 0b01000001, 146 | 0b01111111, 147 | 0b00000000, 148 | 0b01111111, 149 | 0b01000001, 150 | 0b01000001, 151 | 0b01000001, 152 | 0b01111111, 153 | 0b00000000, 154 | 0b01111111, 155 | 0b01000001, 156 | 0b01000001, 157 | 0b01000001, 158 | 0b01111111, 159 | 0b00000000, 160 | 0b01111111, 161 | 0b01000001, 162 | 0b01000001, 163 | 0b01000001, 164 | 0b01111111, 165 | 0b00000000, 166 | 0b01111111, 167 | 0b01000001, 168 | 0b01000001, 169 | 0b01000001, 170 | 0b01111111, 171 | 0b00000000, 172 | 0b01111111, 173 | 0b01000001, 174 | 0b01000001, 175 | 0b01000001, 176 | 0b01111111, 177 | 0b00000000, 178 | 0b01111111, 179 | 0b01000001, 180 | 0b01000001, 181 | 0b01000001, 182 | 0b01111111, 183 | 0b00000000, 184 | 0b01111111, 185 | 0b01000001, 186 | 0b01000001, 187 | 0b01000001, 188 | 0b01111111, 189 | 0b00000000, 190 | 0b01111111, 191 | 0b01000001, 192 | 0b01000001, 193 | 0b01000001, 194 | 0b01111111, 195 | 0b00000000, 196 | 0b00000000, 197 | 0b00000000, 198 | 0b00000000, 199 | 0b00000000, 200 | 0b00000000, 201 | 0b00000000, 202 | 0b01111111, 203 | 0b01000001, 204 | 0b01000001, 205 | 0b01000001, 206 | 0b01111111, 207 | 0b00000000, 208 | 0b01111111, 209 | 0b01000001, 210 | 0b01000001, 211 | 0b01000001, 212 | 0b01111111, 213 | 0b00000000, 214 | 0b00100010, 215 | 0b01111111, 216 | 0b00100010, 217 | 0b01111111, 218 | 0b00100010, 219 | 0b00000000, 220 | 0b01111111, 221 | 0b01000001, 222 | 0b01000001, 223 | 0b01000001, 224 | 0b01111111, 225 | 0b00000000, 226 | 0b01111111, 227 | 0b01000001, 228 | 0b01000001, 229 | 0b01000001, 230 | 0b01111111, 231 | 0b00000000, 232 | 0b01111111, 233 | 0b01000001, 234 | 0b01000001, 235 | 0b01000001, 236 | 0b01111111, 237 | 0b00000000, 238 | 0b01111111, 239 | 0b01000001, 240 | 0b01000001, 241 | 0b01000001, 242 | 0b01111111, 243 | 0b00000000, 244 | 0b00000000, 245 | 0b00011100, 246 | 0b00100010, 247 | 0b01000001, 248 | 0b00000000, 249 | 0b00000000, 250 | 0b00000000, 251 | 0b01000001, 252 | 0b00100010, 253 | 0b00011100, 254 | 0b00000000, 255 | 0b00000000, 256 | 0b00100010, 257 | 0b00010100, 258 | 0b01111111, 259 | 0b00010100, 260 | 0b00100010, 261 | 0b00000000, 262 | 0b00001000, 263 | 0b00001000, 264 | 0b00111110, 265 | 0b00001000, 266 | 0b00001000, 267 | 0b00000000, 268 | 0b01111111, 269 | 0b01000001, 270 | 0b01000001, 271 | 0b01000001, 272 | 0b01111111, 273 | 0b00000000, 274 | 0b00001000, 275 | 0b00001000, 276 | 0b00001000, 277 | 0b00001000, 278 | 0b00001000, 279 | 0b00000000, 280 | 0b00000000, 281 | 0b00000000, 282 | 0b00110000, 283 | 0b00110000, 284 | 0b00000000, 285 | 0b00000000, 286 | 0b00100000, 287 | 0b00010000, 288 | 0b00001000, 289 | 0b00000100, 290 | 0b00000010, 291 | 0b00000000, 292 | 0b00111110, 293 | 0b01000001, 294 | 0b01000001, 295 | 0b01000001, 296 | 0b00111110, 297 | 0b00000000, 298 | 0b00000000, 299 | 0b01000010, 300 | 0b01111111, 301 | 0b01000000, 302 | 0b00000000, 303 | 0b00000000, 304 | 0b01000010, 305 | 0b01100001, 306 | 0b01010001, 307 | 0b01001001, 308 | 0b01000110, 309 | 0b00000000, 310 | 0b00100010, 311 | 0b01000001, 312 | 0b01001001, 313 | 0b01001001, 314 | 0b00110110, 315 | 0b00000000, 316 | 0b00011000, 317 | 0b00010100, 318 | 0b00010010, 319 | 0b01111111, 320 | 0b00010000, 321 | 0b00000000, 322 | 0b00100111, 323 | 0b01000101, 324 | 0b01000101, 325 | 0b01000101, 326 | 0b00111001, 327 | 0b00000000, 328 | 0b00111100, 329 | 0b01001010, 330 | 0b01001001, 331 | 0b01001001, 332 | 0b00110000, 333 | 0b00000000, 334 | 0b00000011, 335 | 0b00000001, 336 | 0b01110001, 337 | 0b00001101, 338 | 0b00000011, 339 | 0b00000000, 340 | 0b00110110, 341 | 0b01001001, 342 | 0b01001001, 343 | 0b01001001, 344 | 0b00110110, 345 | 0b00000000, 346 | 0b00000110, 347 | 0b01001001, 348 | 0b01001001, 349 | 0b00101001, 350 | 0b00011110, 351 | 0b00000000, 352 | 0b00000000, 353 | 0b00000000, 354 | 0b00110110, 355 | 0b00110110, 356 | 0b00000000, 357 | 0b00000000, 358 | 0b01111111, 359 | 0b01000001, 360 | 0b01000001, 361 | 0b01000001, 362 | 0b01111111, 363 | 0b00000000, 364 | 0b00001000, 365 | 0b00010100, 366 | 0b00100010, 367 | 0b01000001, 368 | 0b00000000, 369 | 0b00000000, 370 | 0b00010100, 371 | 0b00010100, 372 | 0b00010100, 373 | 0b00010100, 374 | 0b00010100, 375 | 0b00000000, 376 | 0b00000000, 377 | 0b01000001, 378 | 0b00100010, 379 | 0b00010100, 380 | 0b00001000, 381 | 0b00000000, 382 | 0b01111111, 383 | 0b01000001, 384 | 0b01000001, 385 | 0b01000001, 386 | 0b01111111, 387 | 0b00000000, 388 | 0b01111111, 389 | 0b01111111, 390 | 0b01111111, 391 | 0b01111111, 392 | 0b01111111, 393 | 0b00000000, 394 | 0b01111110, 395 | 0b00010001, 396 | 0b00010001, 397 | 0b00010001, 398 | 0b01111110, 399 | 0b00000000, 400 | 0b01111111, 401 | 0b01001001, 402 | 0b01001001, 403 | 0b01001001, 404 | 0b00110110, 405 | 0b00000000, 406 | 0b00111110, 407 | 0b01000001, 408 | 0b01000001, 409 | 0b01000001, 410 | 0b00100010, 411 | 0b00000000, 412 | 0b01111111, 413 | 0b01000001, 414 | 0b01000001, 415 | 0b00100010, 416 | 0b00011100, 417 | 0b00000000, 418 | 0b01111111, 419 | 0b01001001, 420 | 0b01001001, 421 | 0b01001001, 422 | 0b01000001, 423 | 0b00000000, 424 | 0b01111111, 425 | 0b00001001, 426 | 0b00001001, 427 | 0b00001001, 428 | 0b00000001, 429 | 0b00000000, 430 | 0b00111110, 431 | 0b01000001, 432 | 0b01001001, 433 | 0b01001001, 434 | 0b00111010, 435 | 0b00000000, 436 | 0b01111111, 437 | 0b00001000, 438 | 0b00001000, 439 | 0b00001000, 440 | 0b01111111, 441 | 0b00000000, 442 | 0b00000000, 443 | 0b01000001, 444 | 0b01111111, 445 | 0b01000001, 446 | 0b00000000, 447 | 0b00000000, 448 | 0b00100000, 449 | 0b01000000, 450 | 0b01000001, 451 | 0b00111111, 452 | 0b00000001, 453 | 0b00000000, 454 | 0b01111111, 455 | 0b00001000, 456 | 0b00010100, 457 | 0b00100010, 458 | 0b01000001, 459 | 0b00000000, 460 | 0b01111111, 461 | 0b01000000, 462 | 0b01000000, 463 | 0b01000000, 464 | 0b01000000, 465 | 0b00000000, 466 | 0b01111111, 467 | 0b00000010, 468 | 0b00001100, 469 | 0b00000010, 470 | 0b01111111, 471 | 0b00000000, 472 | 0b01111111, 473 | 0b00000100, 474 | 0b00001000, 475 | 0b00010000, 476 | 0b01111111, 477 | 0b00000000, 478 | 0b00111110, 479 | 0b01000001, 480 | 0b01000001, 481 | 0b01000001, 482 | 0b00111110, 483 | 0b00000000, 484 | 0b01111111, 485 | 0b00001001, 486 | 0b00001001, 487 | 0b00001001, 488 | 0b00000110, 489 | 0b00000000, 490 | 0b00111110, 491 | 0b01000001, 492 | 0b01010001, 493 | 0b00100001, 494 | 0b01011110, 495 | 0b00000000, 496 | 0b01111111, 497 | 0b00001001, 498 | 0b00011001, 499 | 0b00101001, 500 | 0b01000110, 501 | 0b00000000, 502 | 0b00100110, 503 | 0b01001001, 504 | 0b01001001, 505 | 0b01001001, 506 | 0b00110010, 507 | 0b00000000, 508 | 0b00000001, 509 | 0b00000001, 510 | 0b01111111, 511 | 0b00000001, 512 | 0b00000001, 513 | 0b00000000, 514 | 0b00111111, 515 | 0b01000000, 516 | 0b01000000, 517 | 0b01000000, 518 | 0b00111111, 519 | 0b00000000, 520 | 0b00011111, 521 | 0b00100000, 522 | 0b01000000, 523 | 0b00100000, 524 | 0b00011111, 525 | 0b00000000, 526 | 0b00111111, 527 | 0b01000000, 528 | 0b00111100, 529 | 0b01000000, 530 | 0b00111111, 531 | 0b00000000, 532 | 0b01100011, 533 | 0b00010100, 534 | 0b00001000, 535 | 0b00010100, 536 | 0b01100011, 537 | 0b00000000, 538 | 0b00000011, 539 | 0b00000100, 540 | 0b01111000, 541 | 0b00000100, 542 | 0b00000011, 543 | 0b00000000, 544 | 0b01100001, 545 | 0b01010001, 546 | 0b01001001, 547 | 0b01000101, 548 | 0b01000011, 549 | 0b00000000, 550 | 0b00000000, 551 | 0b00000000, 552 | 0b01111111, 553 | 0b01000001, 554 | 0b01000001, 555 | 0b00000000, 556 | 0b01111111, 557 | 0b01000001, 558 | 0b01000001, 559 | 0b01000001, 560 | 0b01111111, 561 | 0b00000000, 562 | 0b01000001, 563 | 0b01000001, 564 | 0b01111111, 565 | 0b00000000, 566 | 0b00000000, 567 | 0b00000000, 568 | 0b01111111, 569 | 0b01000001, 570 | 0b01000001, 571 | 0b01000001, 572 | 0b01111111, 573 | 0b00000000, 574 | 0b01111111, 575 | 0b01000001, 576 | 0b01000001, 577 | 0b01000001, 578 | 0b01111111, 579 | 0b00000000, 580 | 0b01111111, 581 | 0b01111111, 582 | 0b01111111, 583 | 0b01111111, 584 | 0b01111111, 585 | 0b00000000, 586 | 0b00100000, 587 | 0b01010100, 588 | 0b01010100, 589 | 0b01010100, 590 | 0b01111000, 591 | 0b00000000, 592 | 0b01111111, 593 | 0b00101000, 594 | 0b01000100, 595 | 0b01000100, 596 | 0b00111000, 597 | 0b00000000, 598 | 0b00111000, 599 | 0b01000100, 600 | 0b01000100, 601 | 0b01000100, 602 | 0b00101000, 603 | 0b00000000, 604 | 0b00111000, 605 | 0b01000100, 606 | 0b01000100, 607 | 0b00101000, 608 | 0b01111111, 609 | 0b00000000, 610 | 0b00111000, 611 | 0b01010100, 612 | 0b01010100, 613 | 0b01010100, 614 | 0b00011000, 615 | 0b00000000, 616 | 0b00001000, 617 | 0b00001000, 618 | 0b01111110, 619 | 0b00001001, 620 | 0b00001001, 621 | 0b00000000, 622 | 0b00001000, 623 | 0b01010100, 624 | 0b01010100, 625 | 0b01010100, 626 | 0b00111100, 627 | 0b00000000, 628 | 0b01111111, 629 | 0b00001000, 630 | 0b00000100, 631 | 0b00000100, 632 | 0b01111000, 633 | 0b00000000, 634 | 0b00000000, 635 | 0b00000000, 636 | 0b01111101, 637 | 0b00000000, 638 | 0b00000000, 639 | 0b00000000, 640 | 0b00100000, 641 | 0b01000000, 642 | 0b01000100, 643 | 0b00111101, 644 | 0b00000000, 645 | 0b00000000, 646 | 0b00000000, 647 | 0b01111111, 648 | 0b00010000, 649 | 0b00101000, 650 | 0b01000100, 651 | 0b00000000, 652 | 0b00000000, 653 | 0b01000001, 654 | 0b01111111, 655 | 0b01000000, 656 | 0b00000000, 657 | 0b00000000, 658 | 0b01111100, 659 | 0b00000100, 660 | 0b01111000, 661 | 0b00000100, 662 | 0b01111000, 663 | 0b00000000, 664 | 0b01111100, 665 | 0b00001000, 666 | 0b00000100, 667 | 0b00000100, 668 | 0b01111000, 669 | 0b00000000, 670 | 0b00111000, 671 | 0b01000100, 672 | 0b01000100, 673 | 0b01000100, 674 | 0b00111000, 675 | 0b00000000, 676 | 0b01111100, 677 | 0b00010100, 678 | 0b00010100, 679 | 0b00010100, 680 | 0b00001000, 681 | 0b00000000, 682 | 0b00001000, 683 | 0b00010100, 684 | 0b00010100, 685 | 0b00010100, 686 | 0b01111100, 687 | 0b00000000, 688 | 0b01111100, 689 | 0b00001000, 690 | 0b00000100, 691 | 0b00000100, 692 | 0b00001000, 693 | 0b00000000, 694 | 0b01001000, 695 | 0b01010100, 696 | 0b01010100, 697 | 0b01010100, 698 | 0b00100100, 699 | 0b00000000, 700 | 0b00000100, 701 | 0b00000100, 702 | 0b00111111, 703 | 0b01000100, 704 | 0b01000100, 705 | 0b00000000, 706 | 0b00111100, 707 | 0b01000000, 708 | 0b01000000, 709 | 0b00100000, 710 | 0b01111100, 711 | 0b00000000, 712 | 0b00011100, 713 | 0b00100000, 714 | 0b01000000, 715 | 0b00100000, 716 | 0b00011100, 717 | 0b00000000, 718 | 0b00111100, 719 | 0b01000000, 720 | 0b00111000, 721 | 0b01000000, 722 | 0b00111100, 723 | 0b00000000, 724 | 0b01000100, 725 | 0b00101000, 726 | 0b00010000, 727 | 0b00101000, 728 | 0b01000100, 729 | 0b00000000, 730 | 0b00001100, 731 | 0b01010000, 732 | 0b01010000, 733 | 0b01010000, 734 | 0b00111100, 735 | 0b00000000, 736 | 0b01000100, 737 | 0b01100100, 738 | 0b01010100, 739 | 0b01001100, 740 | 0b01000100, 741 | 0b00000000, 742 | 0b01111111, 743 | 0b01000001, 744 | 0b01000001, 745 | 0b01000001, 746 | 0b01111111, 747 | 0b00000000, 748 | 0b01111111, 749 | 0b01000001, 750 | 0b01000001, 751 | 0b01000001, 752 | 0b01111111, 753 | 0b00000000, 754 | 0b01111111, 755 | 0b01000001, 756 | 0b01000001, 757 | 0b01000001, 758 | 0b01111111, 759 | 0b00000000, 760 | 0b01111111, 761 | 0b01000001, 762 | 0b01000001, 763 | 0b01000001, 764 | 0b01111111, 765 | 0b00000000, 766 | 0b01111111, 767 | 0b01000001, 768 | 0b01000001, 769 | 0b01000001, 770 | 0b01111111, 771 | 0b00000000, 772 | }; 773 | -------------------------------------------------------------------------------- /Digital-Synth-PRA32-U/Digital-Synth-PRA32-U.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Digital Synth PRA32-U 3 | */ 4 | 5 | #define PRA32_U_VERSION "v3.2.0 " 6 | 7 | //#define PRA32_U_USE_DEBUG_PRINT // Serial1 8 | 9 | #define PRA32_U_USE_USB_MIDI // Select USB Stack: "Adafruit TinyUSB" in the Arduino IDE "Tools" menu 10 | 11 | //#define PRA32_U_USE_UART_MIDI // Serial2 12 | 13 | #define PRA32_U_UART_MIDI_SPEED (31250) 14 | //#define PRA32_U_UART_MIDI_SPEED (38400) 15 | 16 | #define PRA32_U_UART_MIDI_TX_PIN (4) 17 | #define PRA32_U_UART_MIDI_RX_PIN (5) 18 | 19 | #define PRA32_U_MIDI_CH (0) // 0-based 20 | 21 | // for Pimoroni Pico Audio Pack (PIM544) 22 | #define PRA32_U_I2S_DAC_MUTE_OFF_PIN (22) 23 | #define PRA32_U_I2S_DATA_PIN (9) 24 | //#define PRA32_U_I2S_MCLK_PIN (0) 25 | //#define PRA32_U_I2S_MCLK_MULT (0) 26 | #define PRA32_U_I2S_BCLK_PIN (10) // LRCLK Pin is PRA32_U_I2S_BCLK_PIN + 1 27 | #define PRA32_U_I2S_SWAP_BCLK_AND_LRCLK_PINS (false) 28 | #define PRA32_U_I2S_SWAP_LEFT_AND_RIGHT (false) 29 | 30 | #define PRA32_U_I2S_BUFFERS (4) 31 | #define PRA32_U_I2S_BUFFER_WORDS (64) 32 | 33 | //#define PRA32_U_USE_PWM_AUDIO_INSTEAD_OF_I2S // Select CPU Speed: "150 MHz (Overclock)" in the Arduino IDE "Tools" menu 34 | 35 | // for Pimoroni Pico VGA Demo Base (PIM553) 36 | #define PRA32_U_PWM_AUDIO_L_PIN (28) 37 | #define PRA32_U_PWM_AUDIO_R_PIN (27) 38 | 39 | #define PRA32_U_USE_2_CORES_FOR_SIGNAL_PROCESSING 40 | 41 | #define PRA32_U_USE_EMULATED_EEPROM 42 | 43 | //////////////////////////////////////////////////////////////// 44 | 45 | //#define PRA32_U_USE_CONTROL_PANEL // PRA32-U with Panel 46 | 47 | #define PRA32_U_USE_CONTROL_PANEL_KEY_INPUT // Use tactile switches 48 | #define PRA32_U_KEY_INPUT_ACTIVE_LEVEL (HIGH) 49 | #define PRA32_U_KEY_INPUT_PIN_MODE (INPUT_PULLDOWN) 50 | #define PRA32_U_KEY_INPUT_PREV_KEY_PIN (16) 51 | #define PRA32_U_KEY_INPUT_NEXT_KEY_PIN (18) 52 | #define PRA32_U_KEY_INPUT_PLAY_KEY_PIN (20) 53 | #define PRA32_U_KEY_ANTI_CHATTERING_WAIT (15) 54 | #define PRA32_U_KEY_LONG_PRESS_WAIT (375) 55 | 56 | #define PRA32_U_USE_CONTROL_PANEL_ANALOG_INPUT // Use ADC0, ADC1, and ADC2 57 | #define PRA32_U_ANALOG_INPUT_REVERSED (true) 58 | #define PRA32_U_ANALOG_INPUT_CORRECTION (-504) 59 | #define PRA32_U_ANALOG_INPUT_THRESHOLD (504) 60 | #define PRA32_U_ANALOG_INPUT_DENOMINATOR (504) 61 | 62 | #define PRA32_U_USE_CONTROL_PANEL_OLED_DISPLAY // Use SSD1306 monochrome 128x64 OLED 63 | #define PRA32_U_OLED_DISPLAY_I2C (i2c1) 64 | #define PRA32_U_OLED_DISPLAY_I2C_SDA_PIN (6) 65 | #define PRA32_U_OLED_DISPLAY_I2C_SCL_PIN (7) 66 | #define PRA32_U_OLED_DISPLAY_I2C_ADDRESS (0x3C) 67 | #define PRA32_U_OLED_DISPLAY_CONTRAST (0xFF) 68 | #define PRA32_U_OLED_DISPLAY_ROTATION (true) 69 | 70 | #define PRA32_U_DISABLE_USB_MIDI_TRANSMITTION 71 | 72 | //////////////////////////////////////////////////////////////// 73 | 74 | uint8_t g_midi_ch = PRA32_U_MIDI_CH; 75 | 76 | #include "hardware/adc.h" 77 | 78 | #if defined(PRA32_U_USE_CONTROL_PANEL) 79 | extern void PRA32_U_ControlPanel_on_control_change(uint8_t control_number); 80 | extern void PRA32_U_ControlPanel_on_clock(); 81 | extern void PRA32_U_ControlPanel_on_start(); 82 | extern void PRA32_U_ControlPanel_on_stop(); 83 | #endif // defined(PRA32_U_USE_CONTROL_PANEL_ANALOG_INPUT) 84 | 85 | #include "pra32-u-common.h" 86 | #include "pra32-u-synth.h" 87 | 88 | PRA32_U_Synth g_synth; 89 | 90 | #include 91 | #if defined(PRA32_U_USE_USB_MIDI) 92 | #include 93 | Adafruit_USBD_MIDI usbd_midi; 94 | MIDI_CREATE_INSTANCE(Adafruit_USBD_MIDI, usbd_midi, USB_MIDI); 95 | #endif // defined(PRA32_U_USE_USB_MIDI) 96 | 97 | #if defined(PRA32_U_USE_UART_MIDI) 98 | MIDI_CREATE_INSTANCE(HardwareSerial, Serial2, UART_MIDI); 99 | #endif 100 | 101 | #include "pra32-u-control-panel.h" 102 | 103 | #if defined(PRA32_U_USE_PWM_AUDIO_INSTEAD_OF_I2S) 104 | #include 105 | PWMAudio g_pwm_l(PRA32_U_PWM_AUDIO_L_PIN); 106 | PWMAudio g_pwm_r(PRA32_U_PWM_AUDIO_R_PIN); 107 | #endif // defined(PRA32_U_USE_PWM_AUDIO_INSTEAD_OF_I2S) 108 | 109 | #include 110 | I2S g_i2s_output(OUTPUT); 111 | 112 | static volatile uint32_t s_debug_measurement_elapsed0_us = 0; 113 | static volatile uint32_t s_debug_measurement_max0_us = 0; 114 | static volatile uint32_t s_debug_measurement_elapsed1_us = 0; 115 | static volatile uint32_t s_debug_measurement_max1_us = 0; 116 | 117 | void handleNoteOn(byte channel, byte pitch, byte velocity); 118 | void handleNoteOff(byte channel, byte pitch, byte velocity); 119 | void handleControlChange(byte channel, byte number, byte value); 120 | void handleProgramChange(byte channel, byte number); 121 | void handlePitchBend(byte channel, int bend); 122 | void handleClock(); 123 | void handleStart(); 124 | void handleStop(); 125 | void writeProgramsToFlashAndEndSketch(); 126 | 127 | void __not_in_flash_func(setup1)() { 128 | PRA32_U_ControlPanel_setup(); 129 | 130 | #if defined(PRA32_U_USE_DEBUG_PRINT) 131 | Serial1.setTX(0); 132 | Serial1.setRX(1); 133 | Serial1.begin(115200); 134 | #endif // defined(PRA32_U_USE_DEBUG_PRINT) 135 | } 136 | 137 | void __not_in_flash_func(loop1)() { 138 | boolean processed = g_synth.secondary_core_process(); 139 | if (processed) { 140 | static uint32_t s_loop_counter = 0; 141 | s_loop_counter++; 142 | if (s_loop_counter >= 16 * 400) { 143 | s_loop_counter = 0; 144 | } 145 | 146 | PRA32_U_ControlPanel_update_analog_inputs(s_loop_counter); 147 | PRA32_U_ControlPanel_update_display_buffer(s_loop_counter); 148 | PRA32_U_ControlPanel_update_display(s_loop_counter); 149 | 150 | #if defined(PRA32_U_USE_DEBUG_PRINT) 151 | switch (s_loop_counter) { 152 | case 1 * 400: 153 | Serial1.print("\e[1;1H\e[K"); 154 | Serial1.print(s_debug_measurement_elapsed1_us); 155 | break; 156 | case 2 * 400: 157 | Serial1.print("\e[2;1H\e[K"); 158 | Serial1.print(s_debug_measurement_max1_us); 159 | break; 160 | case 3 * 400: 161 | Serial1.print("\e[4;1H\e[K"); 162 | Serial1.print(s_debug_measurement_elapsed0_us); 163 | break; 164 | case 4 * 400: 165 | Serial1.print("\e[5;1H\e[K"); 166 | Serial1.print(s_debug_measurement_max0_us); 167 | break; 168 | default: 169 | PRA32_U_ControlPanel_debug_print(s_loop_counter); 170 | break; 171 | } 172 | #endif // defined(PRA32_U_USE_DEBUG_PRINT) 173 | } 174 | } 175 | 176 | void __not_in_flash_func(setup)() { 177 | #if defined(PRA32_U_USE_PWM_AUDIO_INSTEAD_OF_I2S) 178 | pinMode(PRA32_U_PWM_AUDIO_L_PIN, OUTPUT_12MA); 179 | pinMode(PRA32_U_PWM_AUDIO_R_PIN, OUTPUT_12MA); 180 | #if ((PRA32_U_PWM_AUDIO_L_PIN + 1) == PRA32_U_PWM_AUDIO_R_PIN) && ((PRA32_U_PWM_AUDIO_L_PIN % 2) == 0) 181 | g_pwm_l.setStereo(true); 182 | g_pwm_l.setBuffers(PRA32_U_I2S_BUFFERS, PRA32_U_I2S_BUFFER_WORDS); 183 | g_pwm_l.setFrequency(SAMPLING_RATE); 184 | g_pwm_l.begin(); 185 | #elif ((PRA32_U_PWM_AUDIO_R_PIN + 1) == PRA32_U_PWM_AUDIO_L_PIN) && ((PRA32_U_PWM_AUDIO_R_PIN % 2) == 0) 186 | g_pwm_r.setStereo(true); 187 | g_pwm_r.setBuffers(PRA32_U_I2S_BUFFERS, PRA32_U_I2S_BUFFER_WORDS); 188 | g_pwm_r.setFrequency(SAMPLING_RATE); 189 | g_pwm_r.begin(); 190 | #else 191 | g_pwm_l.setBuffers(PRA32_U_I2S_BUFFERS, PRA32_U_I2S_BUFFER_WORDS / 2); 192 | g_pwm_r.setBuffers(PRA32_U_I2S_BUFFERS, PRA32_U_I2S_BUFFER_WORDS / 2); 193 | g_pwm_l.setFrequency(SAMPLING_RATE); 194 | g_pwm_r.setFrequency(SAMPLING_RATE); 195 | g_pwm_l.begin(); 196 | g_pwm_r.begin(); 197 | #endif 198 | #else // defined(PRA32_U_USE_PWM_AUDIO_INSTEAD_OF_I2S) 199 | g_i2s_output.setSysClk(SAMPLING_RATE); 200 | g_i2s_output.setFrequency(SAMPLING_RATE); 201 | g_i2s_output.setDATA(PRA32_U_I2S_DATA_PIN); 202 | #if defined(PRA32_U_I2S_MCLK_PIN) 203 | g_i2s_output.setMCLK(PRA32_U_I2S_MCLK_PIN); 204 | g_i2s_output.setMCLKmult(PRA32_U_I2S_MCLK_MULT); 205 | #endif // defined(PRA32_U_I2S_MCLK_PIN) 206 | g_i2s_output.setBCLK(PRA32_U_I2S_BCLK_PIN); 207 | if (PRA32_U_I2S_SWAP_BCLK_AND_LRCLK_PINS) { 208 | g_i2s_output.swapClocks(); 209 | } 210 | g_i2s_output.setBitsPerSample(16); 211 | g_i2s_output.setBuffers(PRA32_U_I2S_BUFFERS, PRA32_U_I2S_BUFFER_WORDS); 212 | g_i2s_output.begin(); 213 | #endif // defined(PRA32_U_USE_PWM_AUDIO_INSTEAD_OF_I2S) 214 | 215 | #if defined(PRA32_U_USE_USB_MIDI) 216 | TinyUSB_Device_Init(0); 217 | USBDevice.setManufacturerDescriptor("ISGK Instruments"); 218 | USBDevice.setProductDescriptor("Digital Synth PRA32-U"); 219 | USB_MIDI.setHandleNoteOn(handleNoteOn); 220 | USB_MIDI.setHandleNoteOff(handleNoteOff); 221 | USB_MIDI.setHandleControlChange(handleControlChange); 222 | USB_MIDI.setHandleProgramChange(handleProgramChange); 223 | USB_MIDI.setHandlePitchBend(handlePitchBend); 224 | USB_MIDI.setHandleClock(handleClock); 225 | USB_MIDI.setHandleStart(handleStart); 226 | USB_MIDI.setHandleStop(handleStop); 227 | USB_MIDI.begin(MIDI_CHANNEL_OMNI); 228 | USB_MIDI.turnThruOff(); 229 | #endif // defined(PRA32_U_USE_USB_MIDI) 230 | 231 | #if defined(PRA32_U_USE_UART_MIDI) 232 | Serial2.setTX(PRA32_U_UART_MIDI_TX_PIN); 233 | Serial2.setRX(PRA32_U_UART_MIDI_RX_PIN); 234 | UART_MIDI.setHandleNoteOn(handleNoteOn); 235 | UART_MIDI.setHandleNoteOff(handleNoteOff); 236 | UART_MIDI.setHandleControlChange(handleControlChange); 237 | UART_MIDI.setHandleProgramChange(handleProgramChange); 238 | UART_MIDI.setHandlePitchBend(handlePitchBend); 239 | UART_MIDI.setHandleClock(handleClock); 240 | UART_MIDI.setHandleStart(handleStart); 241 | UART_MIDI.setHandleStop(handleStop); 242 | UART_MIDI.begin(MIDI_CHANNEL_OMNI); 243 | UART_MIDI.turnThruOff(); 244 | Serial2.begin(PRA32_U_UART_MIDI_SPEED); 245 | #endif // defined(PRA32_U_USE_UART_MIDI) 246 | 247 | #if defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_RASPBERRY_PI_PICO_2) 248 | pinMode(LED_BUILTIN, OUTPUT); 249 | digitalWrite(LED_BUILTIN, HIGH); 250 | #endif // defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_RASPBERRY_PI_PICO_2) 251 | 252 | #if defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_RASPBERRY_PI_PICO_W) || defined(ARDUINO_RASPBERRY_PI_PICO_2) || defined(ARDUINO_RASPBERRY_PI_PICO_2W) 253 | pinMode(23, OUTPUT); // RT6150 (PMIC) Power Save Pin 254 | digitalWrite(23, HIGH); 255 | #endif // defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_RASPBERRY_PI_PICO_W) || defined(ARDUINO_RASPBERRY_PI_PICO_2) || defined(ARDUINO_RASPBERRY_PI_PICO_2W) 256 | 257 | #if defined(PRA32_U_USE_PWM_AUDIO_INSTEAD_OF_I2S) 258 | #else // defined(PRA32_U_USE_PWM_AUDIO_INSTEAD_OF_I2S) 259 | #if defined(PRA32_U_I2S_DAC_MUTE_OFF_PIN) 260 | pinMode(PRA32_U_I2S_DAC_MUTE_OFF_PIN, OUTPUT); 261 | digitalWrite(PRA32_U_I2S_DAC_MUTE_OFF_PIN, HIGH); 262 | #endif // defined(PRA32_U_I2S_DAC_MUTE_OFF_PIN) 263 | #endif // defined(PRA32_U_USE_PWM_AUDIO_INSTEAD_OF_I2S) 264 | 265 | g_synth.initialize(); 266 | 267 | PRA32_U_ControlPanel_initialize_parameters(); 268 | 269 | delay(100); 270 | } 271 | 272 | void __not_in_flash_func(loop)() { 273 | 274 | #if defined(PRA32_U_USE_DEBUG_PRINT) 275 | uint32_t debug_measurement_start0_us = micros(); 276 | #endif // defined(PRA32_U_USE_DEBUG_PRINT) 277 | 278 | for (uint32_t i = 0; i < ((PRA32_U_I2S_BUFFER_WORDS + 31) / 32) + 1; i++) { 279 | #if defined(PRA32_U_USE_USB_MIDI) 280 | USB_MIDI.read(); 281 | #endif // defined(PRA32_U_USE_USB_MIDI) 282 | 283 | #if defined(PRA32_U_USE_UART_MIDI) 284 | UART_MIDI.read(); 285 | #endif 286 | } 287 | 288 | PRA32_U_ControlPanel_update_control(); 289 | 290 | #if defined(PRA32_U_USE_DEBUG_PRINT) 291 | uint32_t debug_measurement_start1_us = micros(); 292 | #endif // defined(PRA32_U_USE_DEBUG_PRINT) 293 | 294 | int16_t left_buffer[PRA32_U_I2S_BUFFER_WORDS]; 295 | int16_t right_buffer[PRA32_U_I2S_BUFFER_WORDS]; 296 | for (uint32_t i = 0; i < PRA32_U_I2S_BUFFER_WORDS; i++) { 297 | left_buffer[i] = g_synth.process(right_buffer[i]); 298 | } 299 | 300 | #if defined(PRA32_U_USE_DEBUG_PRINT) 301 | uint32_t debug_measurement_end_us = micros(); 302 | #endif // defined(PRA32_U_USE_DEBUG_PRINT) 303 | 304 | #if defined(PRA32_U_USE_PWM_AUDIO_INSTEAD_OF_I2S) 305 | for (uint32_t i = 0; i < PRA32_U_I2S_BUFFER_WORDS; i++) { 306 | #if ((PRA32_U_PWM_AUDIO_L_PIN + 1) == PRA32_U_PWM_AUDIO_R_PIN) && ((PRA32_U_PWM_AUDIO_L_PIN % 2) == 0) 307 | g_pwm_l.write(left_buffer[i]); 308 | g_pwm_l.write(right_buffer[i]); 309 | #elif ((PRA32_U_PWM_AUDIO_R_PIN + 1) == PRA32_U_PWM_AUDIO_L_PIN) && ((PRA32_U_PWM_AUDIO_R_PIN % 2) == 0) 310 | g_pwm_r.write(right_buffer[i]); 311 | g_pwm_r.write(left_buffer[i]); 312 | #else 313 | g_pwm_l.write(left_buffer[i]); 314 | g_pwm_r.write(right_buffer[i]); 315 | #endif 316 | } 317 | #else // defined(PRA32_U_USE_PWM_AUDIO_INSTEAD_OF_I2S) 318 | for (uint32_t i = 0; i < PRA32_U_I2S_BUFFER_WORDS; i++) { 319 | if (PRA32_U_I2S_SWAP_LEFT_AND_RIGHT) { 320 | g_i2s_output.write16(right_buffer[i], left_buffer[i]); 321 | } else { 322 | g_i2s_output.write16(left_buffer[i], right_buffer[i]); 323 | } 324 | } 325 | #endif // defined(PRA32_U_USE_PWM_AUDIO_INSTEAD_OF_I2S) 326 | 327 | #if defined(PRA32_U_USE_DEBUG_PRINT) 328 | s_debug_measurement_elapsed0_us = debug_measurement_end_us - debug_measurement_start0_us; 329 | s_debug_measurement_max0_us += (s_debug_measurement_elapsed0_us > s_debug_measurement_max0_us) * 330 | (s_debug_measurement_elapsed0_us - s_debug_measurement_max0_us); 331 | 332 | s_debug_measurement_elapsed1_us = debug_measurement_end_us - debug_measurement_start1_us; 333 | s_debug_measurement_max1_us += (s_debug_measurement_elapsed1_us > s_debug_measurement_max1_us) * 334 | (s_debug_measurement_elapsed1_us - s_debug_measurement_max1_us); 335 | #endif // defined(PRA32_U_USE_DEBUG_PRINT) 336 | } 337 | 338 | void __not_in_flash_func(handleNoteOn)(byte channel, byte pitch, byte velocity) 339 | { 340 | if ((channel - 1) == g_midi_ch) { 341 | g_synth.note_on(pitch, velocity); 342 | } 343 | } 344 | 345 | void __not_in_flash_func(handleNoteOff)(byte channel, byte pitch, byte velocity) 346 | { 347 | if ((channel - 1) == g_midi_ch) { 348 | (void) velocity; 349 | g_synth.note_off(pitch); 350 | } 351 | } 352 | 353 | void __not_in_flash_func(handleControlChange)(byte channel, byte number, byte value) 354 | { 355 | if ((channel - 1) == g_midi_ch) { 356 | g_synth.control_change(number, value); 357 | } 358 | } 359 | 360 | void __not_in_flash_func(handleProgramChange)(byte channel, byte number) 361 | { 362 | if ((channel - 1) == g_midi_ch) { 363 | g_synth.program_change(number); 364 | } 365 | } 366 | 367 | void __not_in_flash_func(handlePitchBend)(byte channel, int bend) 368 | { 369 | if ((channel - 1) == g_midi_ch) { 370 | g_synth.pitch_bend((bend + 8192) & 0x7F, (bend + 8192) >> 7); 371 | } 372 | } 373 | 374 | void __not_in_flash_func(handleClock)() 375 | { 376 | #if defined(PRA32_U_USE_CONTROL_PANEL) 377 | PRA32_U_ControlPanel_on_clock(); 378 | #endif // defined(PRA32_U_USE_CONTROL_PANEL_ANALOG_INPUT) 379 | } 380 | 381 | void __not_in_flash_func(handleStart)() 382 | { 383 | #if defined(PRA32_U_USE_CONTROL_PANEL) 384 | PRA32_U_ControlPanel_on_start(); 385 | #endif // defined(PRA32_U_USE_CONTROL_PANEL_ANALOG_INPUT) 386 | } 387 | 388 | void __not_in_flash_func(handleStop)() 389 | { 390 | #if defined(PRA32_U_USE_CONTROL_PANEL) 391 | PRA32_U_ControlPanel_on_stop(); 392 | #endif // defined(PRA32_U_USE_CONTROL_PANEL_ANALOG_INPUT) 393 | } 394 | -------------------------------------------------------------------------------- /Digital-Synth-PRA32-U/pra32-u-osc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pra32-u-common.h" 4 | #include "pra32-u-osc-table.h" 5 | #include 6 | 7 | 8 | class PRA32_U_Osc { 9 | static const uint8_t OSC_MIX_TABLE_LENGTH = 65; 10 | 11 | static const uint8_t PORTAMENTO_COEF_BASE = 190; 12 | 13 | static const uint8_t WAVEFORM_SAW = 0; 14 | static const uint8_t WAVEFORM_SQUARE = 1; 15 | static const uint8_t WAVEFORM_TRIANGLE = 2; 16 | static const uint8_t WAVEFORM_SINE = 3; 17 | static const uint8_t WAVEFORM_1_PULSE = 4; 18 | static const uint8_t WAVEFORM_2_NOISE = 5; 19 | 20 | uint32_t m_portamento_coef[4]; 21 | int16_t m_pitch_eg_amt[2]; 22 | int16_t m_pitch_lfo_amt[2]; 23 | 24 | uint8_t m_waveform[2]; 25 | int16_t m_pitch_bend; 26 | uint8_t m_pitch_bend_range; 27 | int16_t m_pitch_bend_normalized; 28 | uint32_t m_pitch_target[4]; 29 | uint32_t m_pitch_current[4]; 30 | const int16_t* m_wave_table[4 * 4]; 31 | const int16_t* m_wave_table_temp[4 * 3]; 32 | uint32_t m_freq[4 * 2]; 33 | uint32_t m_freq_base[4 * 2]; 34 | int16_t m_freq_offset[4 * 2]; 35 | uint32_t m_phase[4 * 2]; 36 | boolean m_osc_on[4]; 37 | int8_t m_osc_gain_effective[4]; 38 | int8_t m_osc_level; 39 | 40 | boolean m_gate_enabled; 41 | uint8_t m_mixer_osc_mix_control; 42 | uint8_t m_mixer_osc_mix_control_effective; 43 | int8_t m_osc2_pitch; 44 | int16_t m_osc2_detune; 45 | 46 | uint8_t m_phase_high; 47 | uint8_t m_osc1_shape_control; 48 | uint8_t m_osc1_shape_control_effective; 49 | uint8_t m_osc1_morph_control; 50 | uint8_t m_osc1_morph_control_effective; 51 | int32_t m_osc1_shape[4]; 52 | int32_t m_osc1_shape_effective[4]; 53 | uint16_t m_osc1_phase_modulation_depth[4]; 54 | uint16_t m_osc1_phase_modulation_frequency_ratio[4]; 55 | int8_t m_mixer_noise_sub_osc_control; 56 | int8_t m_mixer_noise_sub_osc_control_effective; 57 | int16_t m_mix_table[OSC_MIX_TABLE_LENGTH]; 58 | int8_t m_shape_eg_amt; 59 | int8_t m_shape_lfo_amt; 60 | 61 | public: 62 | PRA32_U_Osc() 63 | : m_portamento_coef() 64 | , m_pitch_eg_amt() 65 | , m_pitch_lfo_amt() 66 | 67 | , m_waveform() 68 | , m_pitch_bend() 69 | , m_pitch_bend_range() 70 | , m_pitch_bend_normalized() 71 | , m_pitch_target() 72 | , m_pitch_current() 73 | , m_wave_table() 74 | , m_wave_table_temp() 75 | , m_freq() 76 | , m_freq_base() 77 | , m_freq_offset() 78 | , m_phase() 79 | , m_osc_on() 80 | , m_osc_gain_effective() 81 | , m_osc_level() 82 | 83 | , m_gate_enabled() 84 | , m_mixer_osc_mix_control() 85 | , m_mixer_osc_mix_control_effective() 86 | , m_osc2_pitch() 87 | , m_osc2_detune() 88 | 89 | , m_phase_high() 90 | , m_osc1_shape_control() 91 | , m_osc1_shape_control_effective() 92 | , m_osc1_morph_control() 93 | , m_osc1_morph_control_effective() 94 | , m_osc1_shape() 95 | , m_osc1_shape_effective() 96 | , m_osc1_phase_modulation_depth() 97 | , m_osc1_phase_modulation_frequency_ratio() 98 | , m_mixer_noise_sub_osc_control() 99 | , m_mixer_noise_sub_osc_control_effective() 100 | , m_mix_table() 101 | , m_shape_eg_amt() 102 | , m_shape_lfo_amt() 103 | { 104 | m_portamento_coef[0] = 0; 105 | m_portamento_coef[1] = 0; 106 | m_portamento_coef[2] = 0; 107 | m_portamento_coef[3] = 0; 108 | 109 | set_gate_enabled (false); 110 | set_mixer_osc_mix(0); 111 | set_osc2_pitch (0); 112 | set_osc2_detune (0); 113 | 114 | m_waveform[0] = WAVEFORM_SAW; 115 | m_waveform[1] = WAVEFORM_SAW; 116 | m_pitch_target[0] = 60 << 24; 117 | m_pitch_target[1] = 60 << 24; 118 | m_pitch_target[2] = 60 << 24; 119 | m_pitch_target[3] = 60 << 24; 120 | m_pitch_current[0] = m_pitch_target[0]; 121 | m_pitch_current[1] = m_pitch_target[1]; 122 | m_pitch_current[2] = m_pitch_target[2]; 123 | m_pitch_current[3] = m_pitch_target[3]; 124 | m_wave_table[0] = g_osc_saw_wave_tables[0]; 125 | m_wave_table[1] = g_osc_saw_wave_tables[0]; 126 | m_wave_table[2] = g_osc_saw_wave_tables[0]; 127 | m_wave_table[3] = g_osc_saw_wave_tables[0]; 128 | m_wave_table[4] = g_osc_saw_wave_tables[0]; 129 | m_wave_table[5] = g_osc_saw_wave_tables[0]; 130 | m_wave_table[6] = g_osc_saw_wave_tables[0]; 131 | m_wave_table[7] = g_osc_saw_wave_tables[0]; 132 | m_wave_table[8] = g_osc_saw_wave_tables[0]; 133 | m_wave_table[9] = g_osc_saw_wave_tables[0]; 134 | m_wave_table[10] = g_osc_saw_wave_tables[0]; 135 | m_wave_table[11] = g_osc_saw_wave_tables[0]; 136 | m_wave_table[12] = g_osc_saw_wave_tables[0]; 137 | m_wave_table[13] = g_osc_saw_wave_tables[0]; 138 | m_wave_table[14] = g_osc_saw_wave_tables[0]; 139 | m_wave_table[15] = g_osc_saw_wave_tables[0]; 140 | m_wave_table_temp[0] = g_osc_saw_wave_tables[0]; 141 | m_wave_table_temp[1] = g_osc_saw_wave_tables[0]; 142 | m_wave_table_temp[2] = g_osc_saw_wave_tables[0]; 143 | m_wave_table_temp[3] = g_osc_saw_wave_tables[0]; 144 | m_wave_table_temp[4] = g_osc_saw_wave_tables[0]; 145 | m_wave_table_temp[5] = g_osc_saw_wave_tables[0]; 146 | m_wave_table_temp[6] = g_osc_saw_wave_tables[0]; 147 | m_wave_table_temp[7] = g_osc_saw_wave_tables[0]; 148 | m_wave_table_temp[8] = g_osc_saw_wave_tables[0]; 149 | m_wave_table_temp[9] = g_osc_saw_wave_tables[0]; 150 | m_wave_table_temp[10] = g_osc_saw_wave_tables[0]; 151 | m_wave_table_temp[11] = g_osc_saw_wave_tables[0]; 152 | m_freq[0] = g_osc_freq_table[0]; 153 | m_freq[1] = g_osc_freq_table[0]; 154 | m_freq[2] = g_osc_freq_table[0]; 155 | m_freq[3] = g_osc_freq_table[0]; 156 | m_freq[4] = g_osc_freq_table[0]; 157 | m_freq[5] = g_osc_freq_table[0]; 158 | m_freq[6] = g_osc_freq_table[0]; 159 | m_freq[7] = g_osc_freq_table[0]; 160 | m_freq_base[0] = g_osc_freq_table[0]; 161 | m_freq_base[1] = g_osc_freq_table[0]; 162 | m_freq_base[2] = g_osc_freq_table[0]; 163 | m_freq_base[3] = g_osc_freq_table[0]; 164 | m_freq_base[4] = g_osc_freq_table[0]; 165 | m_freq_base[5] = g_osc_freq_table[0]; 166 | m_freq_base[6] = g_osc_freq_table[0]; 167 | m_freq_base[7] = g_osc_freq_table[0]; 168 | m_osc_level = 72; 169 | 170 | m_osc1_shape[0] = 0; 171 | m_osc1_shape[1] = 0; 172 | m_osc1_shape[2] = 0; 173 | m_osc1_shape[3] = 0; 174 | m_osc1_shape_effective[0] = 0; 175 | m_osc1_shape_effective[1] = 0; 176 | m_osc1_shape_effective[2] = 0; 177 | m_osc1_shape_effective[3] = 0; 178 | 179 | for (uint8_t i = 0; i < OSC_MIX_TABLE_LENGTH; ++i) { 180 | m_mix_table[i] = static_cast(sqrtf(static_cast(i) / 181 | (OSC_MIX_TABLE_LENGTH - 1)) * (1 << 10)); 182 | } 183 | 184 | set_pitch_bend_range(2); 185 | } 186 | 187 | template 188 | INLINE void set_osc_waveform(uint8_t controller_value) { 189 | static uint8_t waveform_tables[2][6] = { 190 | { 191 | WAVEFORM_SAW, 192 | WAVEFORM_SINE, 193 | WAVEFORM_TRIANGLE, 194 | WAVEFORM_TRIANGLE, 195 | WAVEFORM_1_PULSE, 196 | WAVEFORM_1_PULSE, 197 | }, 198 | { 199 | WAVEFORM_SAW, 200 | WAVEFORM_SINE, 201 | WAVEFORM_TRIANGLE, 202 | WAVEFORM_TRIANGLE, 203 | WAVEFORM_2_NOISE, 204 | WAVEFORM_SQUARE, 205 | }, 206 | }; 207 | 208 | volatile int32_t index = ((controller_value * 10) + 127) / 254; 209 | 210 | // index = min(index, 5) 211 | index = index - 5; 212 | index = (index < 0) * index + 5; 213 | 214 | m_waveform[N] = waveform_tables[N][index]; 215 | #if 1 216 | if (controller_value < 6) { 217 | m_waveform[N] = waveform_tables[N][controller_value]; 218 | } 219 | #endif 220 | } 221 | 222 | INLINE void set_osc1_shape_control(uint8_t controller_value) { 223 | m_osc1_shape_control = controller_value; 224 | } 225 | 226 | INLINE void set_osc1_morph_control(uint8_t controller_value) { 227 | m_osc1_morph_control = controller_value; 228 | } 229 | 230 | INLINE void set_mixer_sub_osc_control(uint8_t controller_value) { 231 | m_mixer_noise_sub_osc_control = (((controller_value - 63) >> 1) << 1); 232 | } 233 | 234 | INLINE int16_t get_pitch_mod_amt_table(uint8_t controller_value) { 235 | static int16_t pitch_mod_amt_table[128] = { 236 | -7680, -7680, -7680, -7680, -7424, -7168, -6912, -6656, 237 | -6400, -6144, -5888, -5632, -5376, -5120, -4864, -4608, 238 | -4352, -4096, -3840, -3584, -3328, -3072, -2816, -2560, 239 | -2304, -2048, -1792, -1536, -1280, -1024, -768, -512, 240 | -256, -248, -240, -232, -224, -216, -208, -200, 241 | -192, -184, -176, -168, -160, -152, -144, -136, 242 | -128, -120, -112, -104, -96, -88, -80, -72, 243 | -64, -56, -48, -40, -32, -24, -16, -8, 244 | +0, +8, +16, +24, +32, +40, +48, +56, 245 | +64, +72, +80, +88, +96, +104, +112, +120, 246 | +128, +136, +144, +152, +160, +168, +176, +184, 247 | +192, +200, +208, +216, +224, +232, +240, +248, 248 | +256, +512, +768, +1024, +1280, +1536, +1792, +2048, 249 | +2304, +2560, +2816, +3072, +3328, +3584, +3840, +4096, 250 | +4352, +4608, +4864, +5120, +5376, +5632, +5888, +6144, 251 | +6400, +6656, +6912, +7168, +7424, +7680, +7680, +7680, 252 | }; 253 | 254 | return pitch_mod_amt_table[controller_value]; 255 | } 256 | 257 | template 258 | INLINE void set_pitch_eg_amt(uint8_t controller_value) { 259 | m_pitch_eg_amt[N] = get_pitch_mod_amt_table(controller_value); 260 | } 261 | 262 | INLINE void set_shape_eg_amt(uint8_t controller_value) { 263 | if (controller_value == 0) { 264 | controller_value = 1; 265 | } 266 | m_shape_eg_amt = ((controller_value - 64) << 1); 267 | } 268 | 269 | template 270 | INLINE void set_pitch_lfo_amt(uint8_t controller_value) { 271 | m_pitch_lfo_amt[N] = get_pitch_mod_amt_table(controller_value); 272 | } 273 | 274 | INLINE void set_shape_lfo_amt(uint8_t controller_value) { 275 | if (controller_value == 0) { 276 | controller_value = 1; 277 | } 278 | m_shape_lfo_amt = -((controller_value - 64) << 1); 279 | } 280 | 281 | INLINE void set_gate_enabled(boolean gate_enabled) { 282 | m_gate_enabled = gate_enabled; 283 | } 284 | 285 | INLINE void set_mixer_osc_mix(uint8_t controller_value) { 286 | m_mixer_osc_mix_control = ((controller_value + 1) >> 1) << 1; 287 | } 288 | 289 | INLINE void set_osc2_pitch(uint8_t controller_value) { 290 | if (controller_value < 4) { 291 | m_osc2_pitch = -60; 292 | } else if (controller_value < 124) { 293 | m_osc2_pitch = controller_value - 64; 294 | } else { 295 | m_osc2_pitch = 60; 296 | } 297 | } 298 | 299 | INLINE void set_osc2_detune(uint8_t controller_value) { 300 | static int16_t m_osc2_detune_table[128] = { 301 | -3072, -3072, -3072, -3072, -3072, -3072, -3072, -3072, 302 | -3072, -3072, -2944, -2816, -2688, -2560, -2432, -2304, 303 | -2176, -2048, -1920, -1792, -1664, -1536, -1408, -1280, 304 | -1152, -1024, -896, -768, -640, -512, -384, -256, 305 | -128, -124, -120, -116, -112, -108, -104, -100, 306 | -96, -92, -88, -84, -80, -76, -72, -68, 307 | -64, -60, -56, -52, -48, -44, -40, -36, 308 | -32, -28, -24, -20, -16, -12, -8, -4, 309 | +0, +4, +8, +12, +16, +20, +24, +28, 310 | +32, +36, +40, +44, +48, +52, +56, +60, 311 | +64, +68, +72, +76, +80, +84, +88, +92, 312 | +96, +100, +104, +108, +112, +116, +120, +124, 313 | +128, +256, +384, +512, +640, +768, +896, +1024, 314 | +1152, +1280, +1408, +1536, +1664, +1792, +1920, +2048, 315 | +2176, +2304, +2432, +2560, +2688, +2816, +2944, +3072, 316 | +3072, +3072, +3072, +3072, +3072, +3072, +3072, +3072, 317 | }; 318 | 319 | m_osc2_detune = m_osc2_detune_table[controller_value]; 320 | } 321 | 322 | template 323 | INLINE void set_portamento(uint8_t controller_value) { 324 | m_portamento_coef[N] = g_portamento_coef_table[controller_value]; 325 | } 326 | 327 | template 328 | INLINE void note_on(uint8_t note_number) { 329 | uint8_t n; 330 | if (note_number < NOTE_NUMBER_MIN) { 331 | n = NOTE_NUMBER_MIN; 332 | } else if (note_number > NOTE_NUMBER_MAX) { 333 | n = NOTE_NUMBER_MAX; 334 | } else { 335 | n = note_number; 336 | } 337 | 338 | m_pitch_target[N] = (n << 24); 339 | if (m_portamento_coef[N] == 0) { 340 | m_pitch_current[N] = m_pitch_target[N]; 341 | } 342 | m_osc_on[N] = true; 343 | } 344 | 345 | template 346 | INLINE void note_off() { 347 | m_osc_on[N] = false; 348 | } 349 | 350 | INLINE void set_pitch_bend_range(uint8_t controller_value) { 351 | uint8_t range = controller_value; 352 | if (range > 64) { 353 | range = 64; 354 | } 355 | m_pitch_bend_range = range; 356 | update_pitch_bend(); 357 | } 358 | 359 | INLINE void set_pitch_bend(int16_t pitch_bend) { 360 | m_pitch_bend = pitch_bend; 361 | update_pitch_bend(); 362 | } 363 | 364 | INLINE uint16_t get_osc_pitch(uint8_t index) { 365 | uint16_t shifted_pitch = (64 << 8) + (m_pitch_current[index] >> 16) + m_pitch_bend_normalized; 366 | uint16_t osc_pitch; 367 | if (shifted_pitch > (64 << 8) + (NOTE_NUMBER_MAX << 8)) { 368 | osc_pitch = (NOTE_NUMBER_MAX << 8); 369 | } else if (shifted_pitch < (64 << 8) + (NOTE_NUMBER_MIN << 8)) { 370 | osc_pitch = (NOTE_NUMBER_MIN << 8); 371 | } else { 372 | osc_pitch = (m_pitch_current[index] >> 16) + m_pitch_bend_normalized; 373 | } 374 | return osc_pitch; 375 | } 376 | 377 | template 378 | INLINE void process_at_low_rate_a(int16_t lfo_level, int16_t eg_level) { 379 | update_pitch_current(); 380 | update_osc1_shape(lfo_level, eg_level); 381 | update_osc1_shape_effective(); 382 | update_freq_base(lfo_level, eg_level); 383 | update_freq_base(lfo_level, eg_level); 384 | } 385 | 386 | INLINE void process_at_low_rate_b(uint8_t count, int16_t noise_int15) { 387 | switch (count & (0x08 - 1)) { 388 | case 0x00: 389 | update_freq_offset<0>(noise_int15); 390 | update_gate<0>(); 391 | break; 392 | case 0x01: 393 | update_freq_offset<4>(noise_int15); 394 | break; 395 | case 0x02: 396 | update_freq_offset<1>(noise_int15); 397 | update_gate<1>(); 398 | break; 399 | case 0x03: 400 | update_freq_offset<5>(noise_int15); 401 | update_mixer_control_effective(); 402 | break; 403 | case 0x04: 404 | update_freq_offset<2>(noise_int15); 405 | update_gate<2>(); 406 | break; 407 | case 0x05: 408 | update_freq_offset<6>(noise_int15); 409 | break; 410 | case 0x06: 411 | update_freq_offset<3>(noise_int15); 412 | update_gate<3>(); 413 | break; 414 | case 0x07: 415 | update_freq_offset<7>(noise_int15); 416 | update_osc1_control_effective(); 417 | break; 418 | } 419 | } 420 | 421 | template 422 | INLINE int16_t process(int16_t noise_int15) { 423 | #if 1 424 | return process_osc(noise_int15) >> 8; 425 | #else 426 | return = 0; 427 | #endif 428 | } 429 | 430 | private: 431 | INLINE const int16_t* get_wave_table(uint8_t waveform, uint8_t note_number) { 432 | static int16_t** wave_table_table[6] = { 433 | g_osc_saw_wave_tables, // WAVEFORM_SAW = 0 434 | g_osc_square_wave_tables, // WAVEFORM_SQUARE = 1 435 | g_osc_triangle_wave_tables, // WAVEFORM_TRIANGLE = 2 436 | g_osc_sine_wave_tables, // WAVEFORM_SINE = 3 437 | g_osc_saw_wave_tables, // WAVEFORM_1_PULSE = 4 438 | g_osc_square_wave_tables, // WAVEFORM_2_NOISE = 5 439 | }; 440 | 441 | return wave_table_table[waveform][note_number - NOTE_NUMBER_MIN]; 442 | } 443 | 444 | INLINE int16_t get_wave_level(const int16_t* wave_table, uint32_t phase) { 445 | uint16_t phase16 = phase >> 8; 446 | uint16_t curr_index = phase16 >> (16 - OSC_WAVE_TABLE_SAMPLES_BITS); 447 | uint16_t next_weight = phase16 & ((1 << (16 - OSC_WAVE_TABLE_SAMPLES_BITS)) - 1); 448 | int16_t curr_data = wave_table[curr_index + 0]; 449 | int16_t next_data = wave_table[curr_index + 1]; 450 | int16_t level = curr_data + (((next_data - curr_data) * next_weight) >> (16 - OSC_WAVE_TABLE_SAMPLES_BITS)); // lerp 451 | return level; 452 | } 453 | 454 | template 455 | INLINE int32_t process_osc(int16_t noise_int15) { 456 | int32_t result = 0; 457 | 458 | int16_t osc1_gain = m_mix_table[(OSC_MIX_TABLE_LENGTH - 1) - (m_mixer_osc_mix_control_effective >> 1)]; 459 | int16_t osc2_gain = m_mix_table[ (m_mixer_osc_mix_control_effective >> 1)]; 460 | 461 | m_phase[N] += m_freq[N]; 462 | boolean new_period_osc1 = (m_phase[N] & 0x00FFFFFF) < m_freq[N]; // crossing the begin of a osc 1 wave, the begin or the middle of a sub osc wave 463 | m_wave_table[N] = reinterpret_cast((reinterpret_cast(m_wave_table[N]) * (1 - new_period_osc1))); 464 | m_wave_table[N] = reinterpret_cast( reinterpret_cast( m_wave_table[N]) + 465 | (reinterpret_cast(m_wave_table_temp[N]) * new_period_osc1)); 466 | m_wave_table[N + 12] = reinterpret_cast((reinterpret_cast(m_wave_table[N + 12]) * (1 - new_period_osc1))); 467 | m_wave_table[N + 12] = reinterpret_cast( reinterpret_cast( m_wave_table[N + 12]) + 468 | (reinterpret_cast(m_wave_table_temp[N + 8]) * new_period_osc1)); 469 | if (m_waveform[0] == WAVEFORM_SINE) { 470 | // For Sine Wave (wave_3) 471 | 472 | // phase_modulation_depth_candidate = max(m_osc1_shape_effective[N] - (128 << 8), 0) 473 | volatile int32_t phase_modulation_depth_candidate = m_osc1_shape_effective[N] - (128 << 8); 474 | phase_modulation_depth_candidate = (phase_modulation_depth_candidate > 0) * phase_modulation_depth_candidate; 475 | 476 | m_osc1_phase_modulation_depth[N] = phase_modulation_depth_candidate; 477 | 478 | volatile int32_t phase_modulation_frequency_ratio_candidate = (((m_osc1_morph_control_effective + 2) >> 2) << 1) + 2; 479 | m_osc1_phase_modulation_frequency_ratio[N] = (m_osc1_phase_modulation_frequency_ratio[N] * (1 - new_period_osc1)) + (phase_modulation_frequency_ratio_candidate * new_period_osc1); 480 | 481 | uint32_t phase_3 = (((m_phase[N] >> 1) & 0x01FFFFFF) * m_osc1_phase_modulation_frequency_ratio[N]) >> 1; 482 | const int16_t* wave_table_sine = get_wave_table(WAVEFORM_SINE, 60); 483 | int16_t wave_3 = get_wave_level(wave_table_sine, phase_3); 484 | 485 | uint32_t phase_0 = m_phase[N] + ((wave_3 * m_osc1_phase_modulation_depth[N]) >> 4); 486 | int32_t wave_0 = get_wave_level(wave_table_sine, phase_0); 487 | result += (wave_0 * osc1_gain * m_osc_gain_effective[N]) >> 10; 488 | } else { 489 | int32_t wave_0 = get_wave_level(m_wave_table[N], m_phase[N]); 490 | result += (wave_0 * osc1_gain * m_osc_gain_effective[N]) >> 10; 491 | 492 | // For Pulse Wave (wave_3) 493 | uint32_t phase_3 = m_phase[N] + (m_osc1_shape_effective[N] << 8); 494 | boolean new_period_osc1_add = ((phase_3 + 0x00800000) & 0x00FFFFFF) < (m_freq[N] + 0x00010000); // crossing the middle of a saw wave 495 | m_wave_table[N + 8] = reinterpret_cast((reinterpret_cast(m_wave_table[N + 8]) * (1 - new_period_osc1_add))); 496 | m_wave_table[N + 8] = reinterpret_cast( reinterpret_cast( m_wave_table[N + 8]) + 497 | (reinterpret_cast(m_wave_table_temp[N]) * new_period_osc1_add)); 498 | int16_t wave_3 = get_wave_level(m_wave_table[N + 8], phase_3); 499 | result += ((((wave_3 * osc1_gain * m_osc_gain_effective[N]) >> 10) * (((m_osc1_morph_control_effective - 63) >> 1) << 1)) >> 6) * (m_waveform[0] == WAVEFORM_1_PULSE); 500 | } 501 | 502 | if (m_mixer_noise_sub_osc_control_effective >= 0) { 503 | // Sub Osc (wave_1) 504 | int16_t wave_1 = get_wave_level(m_wave_table[N + 12], m_phase[N] >> 1); 505 | result += (wave_1 * m_mixer_noise_sub_osc_control * m_osc_gain_effective[N]) >> 6; 506 | } else { 507 | // Noise (wave_1) 508 | int16_t wave_1 = noise_int15 >> 1; 509 | result += (wave_1 * -m_mixer_noise_sub_osc_control_effective * m_osc_gain_effective[N]) >> 6; 510 | } 511 | 512 | m_phase[N + 4] += m_freq[N + 4]; 513 | boolean new_period_osc2 = (m_phase[N + 4] & 0x00FFFFFF) < m_freq[N + 4]; 514 | m_wave_table[N + 4] = reinterpret_cast((reinterpret_cast(m_wave_table[N + 4]) * (1 - new_period_osc2))); 515 | m_wave_table[N + 4] = reinterpret_cast( reinterpret_cast( m_wave_table[N + 4]) + 516 | (reinterpret_cast(m_wave_table_temp[N + 4]) * new_period_osc2)); 517 | if (m_waveform[1] != WAVEFORM_2_NOISE) { 518 | int16_t wave_2 = get_wave_level(m_wave_table[N + 4], m_phase[N + 4]); 519 | result += (wave_2 * osc2_gain * m_osc_gain_effective[N]) >> 10; 520 | } else { 521 | // Noise (wave_2) 522 | int16_t wave_2 = noise_int15 >> 1; 523 | result += (wave_2 * osc2_gain * m_osc_gain_effective[N]) >> 10; 524 | } 525 | 526 | return result; 527 | } 528 | 529 | template 530 | INLINE void update_pitch_current() { 531 | if (m_osc_on[N]) { 532 | if (m_pitch_current[N] <= m_pitch_target[N]) { 533 | m_pitch_current[N] = m_pitch_target[N] - mul_s32_s32_h32((m_pitch_target[N] - m_pitch_current[N]) << 2, m_portamento_coef[N]); 534 | } else { 535 | m_pitch_current[N] = m_pitch_current[N] + mul_s32_s32_h32((m_pitch_target[N] - m_pitch_current[N]) << 2, (1 << 30) - m_portamento_coef[N]); 536 | } 537 | } 538 | } 539 | 540 | template 541 | INLINE void update_freq_base(int16_t lfo_level, int16_t eg_level) { 542 | int16_t pitch_eg_amt; 543 | if (N >= 4) { 544 | pitch_eg_amt = m_pitch_eg_amt[1]; 545 | } else { 546 | pitch_eg_amt = m_pitch_eg_amt[0]; 547 | } 548 | uint16_t pitch_temp = (64 << 8) + (m_pitch_current[N & 0x03] >> 16) + m_pitch_bend_normalized + ((eg_level * pitch_eg_amt) >> 14); 549 | 550 | uint8_t coarse = high_byte(pitch_temp); 551 | if (coarse < (NOTE_NUMBER_MIN + 64)) { 552 | pitch_temp = ((NOTE_NUMBER_MIN + 64) << 8); 553 | } else if (coarse >= (NOTE_NUMBER_MAX + 64)) { 554 | pitch_temp = ((NOTE_NUMBER_MAX + 64) << 8); 555 | } 556 | 557 | if (N >= 4) { 558 | pitch_temp += (lfo_level * m_pitch_lfo_amt[1]) >> 14; 559 | pitch_temp += (m_osc2_pitch << 8) + m_osc2_detune; 560 | } else { 561 | pitch_temp += (lfo_level * m_pitch_lfo_amt[0]) >> 14; 562 | } 563 | 564 | coarse = high_byte(pitch_temp); 565 | if (coarse < (NOTE_NUMBER_MIN + 64)) { 566 | pitch_temp = NOTE_NUMBER_MIN << 8; 567 | } else if (coarse >= (NOTE_NUMBER_MAX + 64)) { 568 | pitch_temp = NOTE_NUMBER_MAX << 8; 569 | } else { 570 | pitch_temp -= (64 << 8); 571 | } 572 | 573 | pitch_temp += 128; // For g_osc_tune_table[] 574 | 575 | 576 | coarse = high_byte(pitch_temp); 577 | m_freq_base[N] = g_osc_freq_table[coarse - NOTE_NUMBER_MIN]; 578 | if (N >= 4) { 579 | m_wave_table_temp[N] = get_wave_table(m_waveform[1], coarse); 580 | } else { 581 | m_wave_table_temp[N] = get_wave_table(m_waveform[0], coarse); 582 | 583 | // coarse_sub = max((coarse - 12), NOTE_NUMBER_MIN) 584 | volatile int32_t coarse_sub = (coarse - 12) - NOTE_NUMBER_MIN; 585 | coarse_sub = (coarse_sub > 0) * coarse_sub + NOTE_NUMBER_MIN; 586 | 587 | m_wave_table_temp[N + 8] = get_wave_table(WAVEFORM_SINE, coarse_sub); 588 | } 589 | 590 | 591 | uint8_t fine = low_byte(pitch_temp); 592 | int32_t offset = 593 | ((static_cast((m_freq_base[N] >> 1) * g_osc_tune_table[fine >> (8 - OSC_TUNE_TABLE_STEPS_BITS)]) >> 594 | OSC_TUNE_DENOMINATOR_BITS) >> 0) << 1; 595 | m_freq_base[N] += offset; 596 | m_freq[N] = m_freq_base[N] + m_freq_offset[N]; 597 | } 598 | 599 | template 600 | INLINE void update_freq_offset(int16_t noise_int15) { 601 | static_cast(noise_int15); 602 | m_freq_offset[N] = (N >> 2) << 1; 603 | m_freq[N] = m_freq_base[N] + m_freq_offset[N]; 604 | } 605 | 606 | template 607 | INLINE void update_gate() { 608 | if (m_gate_enabled) { 609 | if (m_osc_on[N]) { 610 | const int8_t half_level = (m_osc_level >> 1) + 1; 611 | 612 | if (m_osc_gain_effective[N] >= (m_osc_level - half_level)) { 613 | m_osc_gain_effective[N] = m_osc_level; 614 | } else { 615 | m_osc_gain_effective[N] += half_level; 616 | } 617 | } else { 618 | const int8_t one_fourth_level = (m_osc_level >> 2) + 1; 619 | 620 | if (m_osc_gain_effective[N] <= one_fourth_level) { 621 | m_osc_gain_effective[N] = 0; 622 | } else { 623 | m_osc_gain_effective[N] -= one_fourth_level; 624 | } 625 | } 626 | } else { 627 | m_osc_gain_effective[N] = m_osc_level; 628 | } 629 | } 630 | 631 | INLINE void update_osc1_control_effective() { 632 | m_osc1_shape_control_effective += (m_osc1_shape_control_effective < m_osc1_shape_control); 633 | m_osc1_shape_control_effective -= (m_osc1_shape_control_effective > m_osc1_shape_control); 634 | 635 | m_osc1_morph_control_effective += (m_osc1_morph_control_effective < m_osc1_morph_control); 636 | m_osc1_morph_control_effective -= (m_osc1_morph_control_effective > m_osc1_morph_control); 637 | } 638 | 639 | INLINE void update_mixer_control_effective() { 640 | m_mixer_osc_mix_control_effective += (m_mixer_osc_mix_control_effective < m_mixer_osc_mix_control); 641 | m_mixer_osc_mix_control_effective -= (m_mixer_osc_mix_control_effective > m_mixer_osc_mix_control); 642 | 643 | m_mixer_noise_sub_osc_control_effective += (m_mixer_noise_sub_osc_control_effective < m_mixer_noise_sub_osc_control); 644 | m_mixer_noise_sub_osc_control_effective -= (m_mixer_noise_sub_osc_control_effective > m_mixer_noise_sub_osc_control); 645 | } 646 | 647 | template 648 | INLINE void update_osc1_shape(int16_t lfo_level, int16_t eg_level) { 649 | volatile int32_t osc1_shape = (128 << 8) + (m_osc1_shape_control_effective << 8) 650 | + ((eg_level * m_shape_eg_amt) >> 5) - ((lfo_level * m_shape_lfo_amt) >> 5); 651 | 652 | // osc1_shape = clamp(y_0, (0 << 8), (256 << 8)) 653 | osc1_shape = osc1_shape - (256 << 8); 654 | osc1_shape = (osc1_shape < 0) * osc1_shape + (256 << 8) - (0 << 8); 655 | osc1_shape = (osc1_shape > 0) * osc1_shape + (0 << 8); 656 | 657 | m_osc1_shape[N] = osc1_shape; 658 | } 659 | 660 | template 661 | INLINE void update_osc1_shape_effective() { 662 | // effective_new = clamp(m_osc1_shape[N], (m_osc1_shape_effective[N] - 0x0100), (m_osc1_shape_effective[N] + 0x0100)) 663 | volatile int32_t effective_new = m_osc1_shape[N] - (m_osc1_shape_effective[N] + 0x0100); 664 | effective_new = (effective_new < 0) * effective_new + (m_osc1_shape_effective[N] + 0x0100) - (m_osc1_shape_effective[N] - 0x0100); 665 | effective_new = (effective_new > 0) * effective_new + (m_osc1_shape_effective[N] - 0x0100); 666 | m_osc1_shape_effective[N] = effective_new; 667 | } 668 | 669 | INLINE void update_pitch_bend() { 670 | int16_t b = m_pitch_bend + 1; 671 | b >>= 3; 672 | m_pitch_bend_normalized = (b * m_pitch_bend_range) >> 2; 673 | } 674 | }; 675 | --------------------------------------------------------------------------------