├── .gitignore ├── .gitmodules ├── README.md ├── bootloader ├── bootloader.cc └── makefile ├── makefile ├── midi └── midi.h └── module_tester ├── __init__.py ├── display.cc ├── display.h ├── frequency_counter.cc ├── frequency_counter.h ├── hardware_config.h ├── hardware_design ├── enclosure │ └── module_tester.eps └── pcb │ ├── module_tester-v01.brd │ ├── module_tester-v01.sch │ ├── module_tester-v02.brd │ └── module_tester-v02.sch ├── makefile ├── midi_dispatcher.cc ├── midi_dispatcher.h ├── module_tester.cc ├── note_stack.h ├── parameter.cc ├── parameter.h ├── pulse_counter.cc ├── pulse_counter.h ├── resources.cc ├── resources.h ├── resources ├── __init__.py ├── characters.py ├── lookup_tables.py ├── resources.py ├── strings.py └── waveforms.py ├── signal_generator.cc ├── signal_generator.h ├── ui.cc └── ui.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Precompiled python modules 2 | *.pyc 3 | 4 | # Eagle backups 5 | *.s#? 6 | *.b#? 7 | *.bak 8 | 9 | # Gerber files 10 | *.CUT 11 | *.GBL 12 | *.GBO 13 | *.GBS 14 | *.GML 15 | *.GTL 16 | *.GTO 17 | *.GTP 18 | *.GTS 19 | *.TXT 20 | *.dri 21 | *.gpi 22 | *.pro 23 | 24 | # Assembly files 25 | *assembly.zip 26 | *top.pdf 27 | *bottom.pdf 28 | *.csv 29 | *partlist.txt 30 | 31 | # LaTeX compiled file 32 | *.pdf 33 | *.log 34 | *.aux 35 | 36 | # OS X crap 37 | .DS_Stor? 38 | 39 | # Build directory 40 | build 41 | 42 | # BETA Layout variants 43 | *_beta.sch 44 | *_BETA.sch 45 | *_beta.brd 46 | *_BETA.brd 47 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "avrlib"] 2 | path = avrlib 3 | url = git@github.com:pichenettes/avril.git 4 | [submodule "tools"] 5 | path = tools 6 | url = git@github.com:pichenettes/avril-firmware_tools.git 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Eurorack module tester. 2 | 3 | This project is a test signal generator for Eurorack synth modules. It also provides frequency/clock measurement tools, and a +/12V and +5V power supply - making it a compact "all in one" box for powering and debugging a module. 4 | 5 | Original developer: Emilie Gillet (emilie.o.gillet@gmail.com) 6 | 7 | The firmware is released under a GPL3.0 license. The PCB layouts and schematics are released under a Creative Commons cc-by-sa 3.0 license. 8 | 9 | To build code and flash a blank 644p: 10 | make -f module_tester/makefile bootstrap 11 | -------------------------------------------------------------------------------- /bootloader/bootloader.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Emilie Gillet. 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // This program is distributed in the hope that it will be useful, 8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | // GNU General Public License for more details. 11 | // You should have received a copy of the GNU General Public License 12 | // along with this program. If not, see . 13 | // 14 | // ----------------------------------------------------------------------------- 15 | // 16 | // Bootloader supporting MIDI SysEx update. 17 | // 18 | // Caveat: assumes the firmware flashing is always done from first to last 19 | // block, in increasing order. Random access flashing is not supported! 20 | 21 | #include 22 | #include 23 | 24 | #include "module_tester/hardware_config.h" 25 | #include "avrlib/devices/shift_register.h" 26 | #include "avrlib/gpio.h" 27 | #include "avrlib/serial.h" 28 | 29 | using namespace avrlib; 30 | using namespace module_tester; 31 | 32 | ShiftRegisterOutput status_leds; 33 | EncoderClickLine click; 34 | MidiIO midi; 35 | 36 | uint16_t page = 0; 37 | uint8_t rx_buffer[2 * (SPM_PAGESIZE + 1)]; 38 | 39 | void (*main_entry_point)(void) = 0x0000; 40 | 41 | inline void Init() { 42 | cli(); 43 | click.set_mode(DIGITAL_INPUT); 44 | click.High(); 45 | status_leds.Init(); 46 | } 47 | 48 | void WriteBufferToFlash() { 49 | uint16_t i; 50 | const uint8_t* p = rx_buffer; 51 | eeprom_busy_wait(); 52 | 53 | boot_page_erase(page); 54 | boot_spm_busy_wait(); 55 | 56 | for (i = 0; i < SPM_PAGESIZE; i += 2) { 57 | uint16_t w = *p++; 58 | w |= (*p++) << 8; 59 | boot_page_fill(page + i, w); 60 | } 61 | 62 | boot_page_write(page); 63 | boot_spm_busy_wait(); 64 | boot_rww_enable(); 65 | } 66 | 67 | static const uint8_t sysex_header[] = { 68 | 0xf0, // 69 | 0x00, 0x21, 0x02, // Manufacturer ID for Mutable instruments. 70 | 0x00, 0x7f, // Product ID for other product. 71 | }; 72 | 73 | enum SysExReceptionState { 74 | MATCHING_HEADER = 0, 75 | READING_COMMAND = 1, 76 | READING_DATA = 2, 77 | }; 78 | 79 | inline void MidiLoop() { 80 | uint8_t byte; 81 | uint16_t bytes_read = 0; 82 | uint16_t rx_buffer_index; 83 | uint8_t state = MATCHING_HEADER; 84 | uint8_t checksum; 85 | uint8_t sysex_commands[2]; 86 | uint8_t current_led = 1; 87 | uint8_t status = 0; 88 | uint8_t progress_counter = 0; 89 | 90 | midi.Init(); 91 | page = 0; 92 | status_leds.Write(0x55); 93 | while (1) { 94 | byte = midi.Read(); 95 | // In case we see a realtime message in the stream, safely ignore it. 96 | if (byte > 0xf0 && byte != 0xf7) { 97 | continue; 98 | } 99 | status_leds.Write(status); 100 | switch (state) { 101 | case MATCHING_HEADER: 102 | if (byte == sysex_header[bytes_read]) { 103 | ++bytes_read; 104 | if (bytes_read == sizeof(sysex_header)) { 105 | bytes_read = 0; 106 | state = READING_COMMAND; 107 | } 108 | } else { 109 | bytes_read = 0; 110 | } 111 | break; 112 | 113 | case READING_COMMAND: 114 | if (byte < 0x80) { 115 | sysex_commands[bytes_read++] = byte; 116 | if (bytes_read == 2) { 117 | bytes_read = 0; 118 | rx_buffer_index = 0; 119 | checksum = 0; 120 | state = READING_DATA; 121 | } 122 | } else { 123 | state = MATCHING_HEADER; 124 | current_led = 1; 125 | status = 0; 126 | bytes_read = 0; 127 | } 128 | break; 129 | 130 | case READING_DATA: 131 | if (byte < 0x80) { 132 | if (bytes_read & 1) { 133 | rx_buffer[rx_buffer_index] |= byte & 0xf; 134 | if (rx_buffer_index < SPM_PAGESIZE) { 135 | checksum += rx_buffer[rx_buffer_index]; 136 | } 137 | ++rx_buffer_index; 138 | } else { 139 | rx_buffer[rx_buffer_index] = (byte << 4); 140 | } 141 | ++bytes_read; 142 | } else if (byte == 0xf7) { 143 | if (sysex_commands[0] == 0x7f && 144 | sysex_commands[1] == 0x00 && 145 | bytes_read == 0) { 146 | // Reset. 147 | return; 148 | } else if (rx_buffer_index == SPM_PAGESIZE + 1 && 149 | sysex_commands[0] == 0x7e && 150 | sysex_commands[1] == 0x00 && 151 | rx_buffer[rx_buffer_index - 1] == checksum) { 152 | // Block write. 153 | WriteBufferToFlash(); 154 | page += SPM_PAGESIZE; 155 | ++progress_counter; 156 | if (progress_counter == 32) { 157 | status |= current_led; 158 | current_led <<= 1; 159 | if (current_led == 0) { 160 | current_led = 1; 161 | status = 0; 162 | } 163 | progress_counter = 0; 164 | } 165 | status ^= current_led; 166 | } else { 167 | current_led = 1; 168 | status = 0; 169 | } 170 | state = MATCHING_HEADER; 171 | bytes_read = 0; 172 | } 173 | break; 174 | } 175 | } 176 | } 177 | 178 | int main(void) { 179 | uint8_t watchdog_status = MCUSR; 180 | MCUSR = 0; 181 | WDTCSR |= _BV(WDCE) | _BV(WDE); 182 | WDTCSR = 0; 183 | 184 | Init(); 185 | if (!click.value()) { 186 | MidiLoop(); 187 | } 188 | status_leds.Write(0xaa); 189 | main_entry_point(); 190 | } 191 | -------------------------------------------------------------------------------- /bootloader/makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2009 Emilie Gillet. 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # This program is distributed in the hope that it will be useful, 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | # GNU General Public License for more details. 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program. If not, see . 13 | 14 | VERSION = 0.01 15 | MCU_NAME = 644 16 | TARGET = module_tester_bootloader 17 | PACKAGES = bootloader 18 | EXTRA_DEFINES = -DDISABLE_DEFAULT_UART_RX_ISR -funsigned-char -fno-inline-small-functions -fmove-loop-invariants 19 | EXTRA_LD_FLAGS = ,--section-start=.text=0xfc00,--relax 20 | 21 | LOCK = 2f 22 | LFUSE = ff 23 | HFUSE = d6 24 | EFUSE = fd 25 | 26 | include avrlib/makefile.mk 27 | 28 | include $(DEP_FILE) 29 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2009 Emilie Gillet. 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # This program is distributed in the hope that it will be useful, 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | # GNU General Public License for more details. 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program. If not, see . 13 | 14 | include module_tester/makefile 15 | 16 | FIRMWARE = $(BUILD_DIR)module_tester.hex 17 | BOOTLOADER = $(BUILD_ROOT)module_tester_bootloader/module_tester_bootloader.hex 18 | 19 | bootstrap_all: $(FIRMWARE) $(BOOTLOADER) 20 | make -f bootloader/makefile fuses 21 | $(AVRDUDE) -B 1 $(AVRDUDE_COM_OPTS) $(AVRDUDE_ISP_OPTS) \ 22 | -U flash:w:$(FIRMWARE):i -U flash:w:$(BOOTLOADER):i \ 23 | -U lock:w:0x2f:m 24 | -------------------------------------------------------------------------------- /midi/midi.h: -------------------------------------------------------------------------------- 1 | // Copyright 2009 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | // ----------------------------------------------------------------------------- 17 | // 18 | // Decoding of MIDI messages. 19 | 20 | #ifndef MIDI_MIDI_H_ 21 | #define MIDI_MIDI_H_ 22 | 23 | namespace midi { 24 | 25 | const uint8_t kBankMsb = 0x00; 26 | const uint8_t kBankLsb = 0x20; 27 | const uint8_t kModulationWheelMsb = 0x01; 28 | const uint8_t kBreathController = 0x02; 29 | const uint8_t kFootPedalMsb = 0x04; 30 | const uint8_t kDataEntryMsb = 0x06; 31 | const uint8_t kVolume = 0x07; 32 | const uint8_t kFootPedalLsb = 0x24; 33 | const uint8_t kDataEntryLsb = 0x26; 34 | const uint8_t kPortamentoTimeMsb = 0x05; 35 | const uint8_t kHoldPedal = 0x40; 36 | const uint8_t kHarmonicIntensity = 0x47; 37 | const uint8_t kRelease = 0x48; 38 | const uint8_t kAttack = 0x49; 39 | const uint8_t kBrightness = 0x4a; 40 | const uint8_t kDataIncrement = 0x60; 41 | const uint8_t kDataDecrement = 0x61; 42 | const uint8_t kNrpnMsb = 0x63; 43 | const uint8_t kNrpnLsb = 0x62; 44 | const uint8_t kAssignableCcA = 0x10; 45 | const uint8_t kAssignableCcB = 0x11; 46 | const uint8_t kAssignableCcC = 0x12; 47 | const uint8_t kAssignableCcD = 0x13; 48 | 49 | // A device that responds to MIDI messages should implement this interface. 50 | // Everything is static - this is because the main synth class is a "static 51 | // singleton". Note that this allows all the MIDI processing code to be inlined! 52 | struct MidiDevice { 53 | static void NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) { } 54 | static void NoteOff(uint8_t channel, uint8_t note, uint8_t velocity) { } 55 | static void Aftertouch(uint8_t channel, uint8_t note, uint8_t velocity) { } 56 | static void Aftertouch(uint8_t channel, uint8_t velocity) { } 57 | static void ControlChange(uint8_t channel, uint8_t controller, 58 | uint8_t value) { } 59 | static void ProgramChange(uint8_t channel, uint8_t program) { } 60 | static void PitchBend(uint8_t channel, uint16_t pitch_bend) { } 61 | 62 | static void AllSoundOff(uint8_t channel) { } 63 | static void ResetAllControllers(uint8_t channel) { } 64 | static void LocalControl(uint8_t channel, uint8_t state) { } 65 | static void AllNotesOff(uint8_t channel) { } 66 | static void OmniModeOff(uint8_t channel) { } 67 | static void OmniModeOn(uint8_t channel) { } 68 | static void MonoModeOn(uint8_t channel, uint8_t num_channels) { } 69 | static void PolyModeOn(uint8_t channel) { } 70 | static void SysExStart() { } 71 | static void SysExByte(uint8_t sysex_byte) { } 72 | static void SysExEnd() { } 73 | static void BozoByte(uint8_t bozo_byte) { } 74 | 75 | static void Clock() { } 76 | static void Start() { } 77 | static void Continue() { } 78 | static void Stop() { } 79 | static void ActiveSensing() { } 80 | static void Reset() { } 81 | 82 | static uint8_t CheckChannel(uint8_t channel) { return 1; } 83 | static void RawByte(uint8_t byte) { } 84 | static void RawMidiData( 85 | uint8_t status, 86 | uint8_t* data, 87 | uint8_t data_size, 88 | uint8_t accepted_channel) { } 89 | }; 90 | 91 | template 92 | class MidiStreamParser { 93 | public: 94 | MidiStreamParser(); 95 | void PushByte(uint8_t byte); 96 | 97 | private: 98 | void MessageReceived(uint8_t status); 99 | 100 | uint8_t running_status_; 101 | uint8_t data_[3]; 102 | uint8_t data_size_; // Number of non-status byte received. 103 | uint8_t expected_data_size_; // Expected number of non-status bytes. 104 | 105 | DISALLOW_COPY_AND_ASSIGN(MidiStreamParser); 106 | }; 107 | 108 | template 109 | MidiStreamParser::MidiStreamParser() { 110 | running_status_ = 0; 111 | data_size_ = 0; 112 | expected_data_size_ = 0; 113 | } 114 | 115 | template 116 | void MidiStreamParser::PushByte(uint8_t byte) { 117 | // Realtime messages are immediately passed-through, and do not modify the 118 | // state of the parser. 119 | Device::RawByte(byte); 120 | if (byte >= 0xf8) { 121 | MessageReceived(byte); 122 | } else { 123 | if (byte >= 0x80) { 124 | uint8_t hi = byte & 0xf0; 125 | uint8_t lo = byte & 0x0f; 126 | data_size_ = 0; 127 | expected_data_size_ = 1; 128 | switch (hi) { 129 | case 0x80: 130 | case 0x90: 131 | case 0xa0: 132 | case 0xb0: 133 | expected_data_size_ = 2; 134 | break; 135 | case 0xc0: 136 | case 0xd0: 137 | break; // default data size of 1. 138 | case 0xe0: 139 | expected_data_size_ = 2; 140 | break; 141 | case 0xf0: 142 | if (lo > 0 && lo < 3) { 143 | expected_data_size_ = 2; 144 | } else if (lo >= 4) { 145 | expected_data_size_ = 0; 146 | } 147 | break; 148 | } 149 | if (running_status_ == 0xf0) { 150 | Device::SysExEnd(); 151 | } 152 | running_status_ = byte; 153 | if (running_status_ == 0xf0) { 154 | Device::SysExStart(); 155 | } 156 | } else { 157 | data_[data_size_++] = byte; 158 | } 159 | if (data_size_ >= expected_data_size_) { 160 | MessageReceived(running_status_); 161 | data_size_ = 0; 162 | if (running_status_ > 0xf0) { 163 | expected_data_size_ = 0; 164 | running_status_ = 0; 165 | } 166 | } 167 | } 168 | } 169 | 170 | template 171 | void MidiStreamParser::MessageReceived(uint8_t status) { 172 | if (!status) { 173 | Device::BozoByte(data_[0]); 174 | } 175 | 176 | uint8_t hi = status & 0xf0; 177 | uint8_t lo = status & 0x0f; 178 | 179 | // If this is a channel-specific message, check first that the receiver is 180 | // tune to this channel. 181 | if (hi != 0xf0 && !Device::CheckChannel(lo)) { 182 | Device::RawMidiData(status, data_, data_size_, 0); 183 | return; 184 | } 185 | Device::RawMidiData(status, data_, data_size_, 1); 186 | switch (hi) { 187 | case 0x80: 188 | Device::NoteOff(lo, data_[0], data_[1]); 189 | break; 190 | 191 | case 0x90: 192 | if (data_[1]) { 193 | Device::NoteOn(lo, data_[0], data_[1]); 194 | } else { 195 | Device::NoteOff(lo, data_[0], 0); 196 | } 197 | break; 198 | 199 | case 0xa0: 200 | Device::Aftertouch(lo, data_[0], data_[1]); 201 | break; 202 | 203 | case 0xb0: 204 | switch (data_[0]) { 205 | case 0x78: 206 | Device::AllSoundOff(lo); 207 | break; 208 | case 0x79: 209 | Device::ResetAllControllers(lo); 210 | break; 211 | case 0x7a: 212 | Device::LocalControl(lo, data_[1]); 213 | break; 214 | case 0x7b: 215 | Device::AllNotesOff(lo); 216 | break; 217 | case 0x7c: 218 | Device::OmniModeOff(lo); 219 | break; 220 | case 0x7d: 221 | Device::OmniModeOn(lo); 222 | break; 223 | case 0x7e: 224 | Device::MonoModeOn(lo, data_[1]); 225 | break; 226 | case 0x7f: 227 | Device::PolyModeOn(lo); 228 | break; 229 | default: 230 | Device::ControlChange(lo, data_[0], data_[1]); 231 | break; 232 | } 233 | break; 234 | 235 | case 0xc0: 236 | Device::ProgramChange(lo, data_[0]); 237 | break; 238 | 239 | case 0xd0: 240 | Device::Aftertouch(lo, data_[0]); 241 | break; 242 | 243 | case 0xe0: 244 | Device::PitchBend(lo, (static_cast(data_[1]) << 7) + data_[0]); 245 | break; 246 | 247 | case 0xf0: 248 | switch(lo) { 249 | case 0x0: 250 | Device::SysExByte(data_[0]); 251 | break; 252 | case 0x1: 253 | case 0x2: 254 | case 0x3: 255 | case 0x4: 256 | case 0x5: 257 | case 0x6: 258 | // TODO(pichenettes): implement this if it makes sense. 259 | break; 260 | case 0x7: 261 | Device::SysExEnd(); 262 | break; 263 | case 0x8: 264 | Device::Clock(); 265 | break; 266 | case 0x9: 267 | break; 268 | case 0xa: 269 | Device::Start(); 270 | break; 271 | case 0xb: 272 | Device::Continue(); 273 | break; 274 | case 0xc: 275 | Device::Stop(); 276 | break; 277 | case 0xe: 278 | Device::ActiveSensing(); 279 | break; 280 | case 0xf: 281 | Device::Reset(); 282 | break; 283 | } 284 | break; 285 | } 286 | } 287 | 288 | } // namespace midi 289 | 290 | #endif // MIDI_MIDI_H_ 291 | -------------------------------------------------------------------------------- /module_tester/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pichenettes/module_tester/65ca98c42e6f65bd73fa91d14427bf81404dbf05/module_tester/__init__.py -------------------------------------------------------------------------------- /module_tester/display.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Emilie Gillet.. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | 16 | #include "module_tester/display.h" 17 | 18 | namespace module_tester { 19 | 20 | Lcd lcd; 21 | BufferedDisplay display; 22 | 23 | } // namespace module_tester 24 | -------------------------------------------------------------------------------- /module_tester/display.h: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Emilie Gillet.. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | 16 | #ifndef MODULE_TESTER_DISPLAY_H_ 17 | #define MODULE_TESTER_DISPLAY_H_ 18 | 19 | #include "avrlib/base.h" 20 | #include "avrlib/devices/buffered_display.h" 21 | #include "avrlib/devices/hd44780_lcd.h" 22 | #include "avrlib/gpio.h" 23 | #include "avrlib/parallel_io.h" 24 | 25 | #include "module_tester/hardware_config.h" 26 | 27 | using avrlib::BufferedDisplay; 28 | using avrlib::Gpio; 29 | using avrlib::Hd44780Lcd; 30 | using avrlib::PARALLEL_NIBBLE_HIGH; 31 | using avrlib::PARALLEL_NIBBLE_LOW; 32 | using avrlib::ParallelPort; 33 | using avrlib::PortD; 34 | 35 | namespace module_tester { 36 | 37 | static const uint8_t kDelimiter = 0x07; 38 | 39 | typedef Hd44780Lcd< 40 | LcdRsLine, LcdEnableLine, LcdDataBus, kLcdWidth, kLcdHeight> Lcd; 41 | 42 | extern Lcd lcd; 43 | extern BufferedDisplay display; 44 | 45 | } // namespace module_tester 46 | 47 | #endif // MODULE_TESTER_DISPLAY_H_ 48 | -------------------------------------------------------------------------------- /module_tester/frequency_counter.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Emilie Gillet.. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | // ----------------------------------------------------------------------------- 17 | // 18 | // Signal generator. 19 | 20 | #include "module_tester/frequency_counter.h" 21 | 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | #include "avrlib/string.h" 28 | 29 | #include "module_tester/parameter.h" 30 | 31 | namespace module_tester { 32 | 33 | /* static */ 34 | FrequencyCounterSettings FrequencyCounter::data_; 35 | 36 | /* static */ 37 | uint32_t FrequencyCounter::interval_; 38 | 39 | /* static */ 40 | uint16_t FrequencyCounter::num_measurements_; 41 | 42 | /* static */ 43 | uint32_t FrequencyCounter::current_interval_; 44 | 45 | /* static */ 46 | uint16_t FrequencyCounter::current_num_measurements_; 47 | 48 | /* static */ 49 | int16_t FrequencyCounter::stable_note_; 50 | 51 | /* static */ 52 | int16_t FrequencyCounter::previous_stable_note_; 53 | 54 | /* static */ 55 | int16_t FrequencyCounter::note_interval_; 56 | 57 | /* static */ 58 | uint8_t FrequencyCounter::note_acquisition_delay_; 59 | 60 | /* static */ 61 | void FrequencyCounter::Init() { 62 | data_.unit = FREQUENCY_COUNTER_UNIT_HZ; 63 | } 64 | 65 | /* static */ 66 | void FrequencyCounter::Print(char* buffer, uint8_t width) { 67 | float frequency = F_CPU * static_cast(current_num_measurements_) / 68 | static_cast(current_interval_); 69 | if (current_interval_ == 0 || current_num_measurements_ == 0) { 70 | frequency = 0; 71 | } 72 | if (data_.unit == FREQUENCY_COUNTER_UNIT_HZ) { 73 | sprintf(buffer, "freq%3.2fHz", frequency); 74 | } else if (data_.unit == FREQUENCY_COUNTER_UNIT_MIDI_NOTE) { 75 | char note[4]; 76 | 77 | float midi_note = 69.0f + (12.0f / 0.69314718f) * logf(frequency / 440.0f); 78 | float integral_part = roundf(midi_note); 79 | int16_t integral_part_int = static_cast(integral_part); 80 | if (integral_part_int > 127) { 81 | integral_part_int = 127; 82 | } else if (integral_part_int < 0) { 83 | integral_part_int = 0; 84 | } 85 | Parameter::PrintNote(integral_part_int, note); 86 | int16_t cents = static_cast(100.0 * (midi_note - integral_part)); 87 | if (frequency) { 88 | sprintf(buffer, "note%s %+d", note, cents); 89 | } else { 90 | sprintf(buffer, "note----"); 91 | } 92 | } else if (data_.unit == FREQUENCY_COUNTER_UNIT_INTERVAL) { 93 | float midi_note = 69.0f + (12.0f / 0.69314718f) * logf(frequency / 440.0f); 94 | int16_t note_integral = static_cast(midi_note * 100.0); 95 | if (!note_acquisition_delay_) { 96 | if (stable_note_ > (note_integral + 50) 97 | || note_integral > (stable_note_ + 50)) { 98 | note_acquisition_delay_ = 2; 99 | } 100 | } 101 | if (note_acquisition_delay_ > 0) { 102 | --note_acquisition_delay_; 103 | if (note_acquisition_delay_ == 0) { 104 | previous_stable_note_ = stable_note_; 105 | stable_note_ = note_integral; 106 | if (stable_note_ > (previous_stable_note_ + 50)) { 107 | note_interval_ = stable_note_ - previous_stable_note_; 108 | } else if (previous_stable_note_ > (stable_note_ + 50)) { 109 | note_interval_ = previous_stable_note_ - stable_note_; 110 | } 111 | } 112 | } 113 | sprintf(buffer, "int.%d", note_interval_); 114 | } 115 | avrlib::AlignRight(buffer + 4, width - 4); 116 | } 117 | 118 | /*extern */ 119 | FrequencyCounter frequency_counter; 120 | 121 | } // namespace module_tester 122 | -------------------------------------------------------------------------------- /module_tester/frequency_counter.h: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Emilie Gillet.. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | // ----------------------------------------------------------------------------- 17 | // 18 | // Signal generator. 19 | 20 | #ifndef MODULE_TESTER_FREQUENCY_COUNTER_H_ 21 | #define MODULE_TESTER_FREQUENCY_COUNTER_H_ 22 | 23 | #include "avrlib/base.h" 24 | 25 | namespace module_tester { 26 | 27 | enum FrequencyCounterUnit { 28 | FREQUENCY_COUNTER_UNIT_HZ, 29 | FREQUENCY_COUNTER_UNIT_MIDI_NOTE, 30 | FREQUENCY_COUNTER_UNIT_INTERVAL 31 | }; 32 | 33 | struct FrequencyCounterSettings { 34 | FrequencyCounterUnit unit; 35 | }; 36 | 37 | enum FrequencyCounterParameter { 38 | FC_PARAMETER_UNIT, 39 | }; 40 | 41 | class FrequencyCounter { 42 | public: 43 | FrequencyCounter() { } 44 | ~FrequencyCounter() { } 45 | 46 | static void Init(); 47 | static void UpdateFrequencyMeasurement(uint32_t interval) { 48 | interval_ += interval; 49 | ++num_measurements_; 50 | 51 | LongWord w; 52 | w.value = interval_; 53 | if (w.words[1] >= (F_CPU / 10) >> 16) { 54 | current_interval_ = interval_; 55 | current_num_measurements_ = num_measurements_; 56 | interval_ = 0; 57 | num_measurements_ = 0; 58 | } 59 | } 60 | 61 | static const FrequencyCounterSettings& data() { return data_; } 62 | static FrequencyCounterSettings* mutable_data() { return &data_; } 63 | 64 | static void Print(char* buffer, uint8_t width); 65 | 66 | private: 67 | static FrequencyCounterSettings data_; 68 | 69 | static uint32_t interval_; 70 | static uint32_t current_interval_; 71 | static uint16_t num_measurements_; 72 | static uint16_t current_num_measurements_; 73 | 74 | static int16_t stable_note_; 75 | static int16_t previous_stable_note_; 76 | static int16_t note_interval_; 77 | static uint8_t note_acquisition_delay_; 78 | 79 | DISALLOW_COPY_AND_ASSIGN(FrequencyCounter); 80 | }; 81 | 82 | extern FrequencyCounter frequency_counter; 83 | 84 | } // namespace module_tester 85 | 86 | #endif // MODULE_TESTER_FREQUENCY_COUNTER_H_ 87 | -------------------------------------------------------------------------------- /module_tester/hardware_config.h: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Emilie Gillet.. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | 16 | #ifndef MODULE_TESTER_HARDWARE_CONFIG_H_ 17 | #define MODULE_TESTER_HARDWARE_CONFIG_H_ 18 | 19 | #include "avrlib/base.h" 20 | #include "avrlib/gpio.h" 21 | #include "avrlib/parallel_io.h" 22 | #include "avrlib/serial.h" 23 | #include "avrlib/spi.h" 24 | #include "avrlib/timer.h" 25 | 26 | namespace module_tester { 27 | 28 | using avrlib::Gpio; 29 | using avrlib::MSB_FIRST; 30 | using avrlib::PARALLEL_NIBBLE_HIGH; 31 | using avrlib::PARALLEL_TRIPLE_LOW; 32 | using avrlib::ParallelPort; 33 | using avrlib::PortA; 34 | using avrlib::PortB; 35 | using avrlib::PortC; 36 | using avrlib::PortD; 37 | using avrlib::RingBuffer; 38 | using avrlib::SerialInput; 39 | using avrlib::SerialPort0; 40 | using avrlib::SpiMaster; 41 | using avrlib::Timer; 42 | 43 | // Encoder 44 | typedef Gpio EncoderALine; 45 | typedef Gpio EncoderBLine; 46 | typedef Gpio EncoderClickLine; 47 | 48 | // MIDI 49 | typedef avrlib::Serial< 50 | SerialPort0, 51 | 31250, 52 | avrlib::POLLED, 53 | avrlib::DISABLED> MidiIO; 54 | typedef RingBuffer > MidiBuffer; 55 | 56 | // LCD 57 | static const uint8_t kLcdWidth = 16; 58 | static const uint8_t kLcdHeight = 2; 59 | typedef Gpio LcdRsLine; 60 | typedef Gpio LcdEnableLine; 61 | typedef ParallelPort LcdDataBus; 62 | 63 | // IO 64 | typedef Gpio IOClockLine; 65 | typedef Gpio IOInputLine; 66 | typedef Gpio IOOutputLine; 67 | typedef Gpio IOEnableLine; 68 | 69 | // Timers 70 | typedef Timer<0> SystemTimer; 71 | typedef Timer<1> TuningTimer; 72 | typedef Timer<2> AudioTimer; 73 | 74 | // Clock and gate output 75 | typedef Gpio ClockOutput; 76 | typedef Gpio GateOutput; 77 | 78 | // Gate input 79 | typedef Gpio GateInput; 80 | 81 | // Spi DAC 82 | typedef SpiMaster, MSB_FIRST, 2> DacInterface; 83 | 84 | } // namespace module_tester 85 | 86 | #endif // MODULE_TESTER_HARDWARE_CONFIG_H_ 87 | -------------------------------------------------------------------------------- /module_tester/hardware_design/enclosure/module_tester.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pichenettes/module_tester/65ca98c42e6f65bd73fa91d14427bf81404dbf05/module_tester/hardware_design/enclosure/module_tester.eps -------------------------------------------------------------------------------- /module_tester/hardware_design/pcb/module_tester-v01.brd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pichenettes/module_tester/65ca98c42e6f65bd73fa91d14427bf81404dbf05/module_tester/hardware_design/pcb/module_tester-v01.brd -------------------------------------------------------------------------------- /module_tester/hardware_design/pcb/module_tester-v01.sch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pichenettes/module_tester/65ca98c42e6f65bd73fa91d14427bf81404dbf05/module_tester/hardware_design/pcb/module_tester-v01.sch -------------------------------------------------------------------------------- /module_tester/hardware_design/pcb/module_tester-v02.brd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pichenettes/module_tester/65ca98c42e6f65bd73fa91d14427bf81404dbf05/module_tester/hardware_design/pcb/module_tester-v02.brd -------------------------------------------------------------------------------- /module_tester/hardware_design/pcb/module_tester-v02.sch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pichenettes/module_tester/65ca98c42e6f65bd73fa91d14427bf81404dbf05/module_tester/hardware_design/pcb/module_tester-v02.sch -------------------------------------------------------------------------------- /module_tester/makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2013 Emilie Gillet.. 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # This program is distributed in the hope that it will be useful, 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | # GNU General Public License for more details. 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program. If not, see . 13 | 14 | VERSION = 1.0 15 | MCU_NAME = 644 16 | TARGET = module_tester 17 | PACKAGES = avrlib module_tester 18 | RESOURCES = module_tester/resources 19 | EXTRA_DEFINES = -DDISABLE_DEFAULT_UART_RX_ISR 20 | EXTRA_LD_FLAGS = ,-u,vfprintf -lprintf_flt 21 | SYSEX_FLAGS = --page_size=128 --device_id=127 22 | 23 | LFUSE = ff 24 | HFUSE = d6 25 | EFUSE = fd 26 | LOCK = 2f 27 | 28 | include avrlib/makefile.mk 29 | 30 | include $(DEP_FILE) 31 | -------------------------------------------------------------------------------- /module_tester/midi_dispatcher.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2009 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | // ----------------------------------------------------------------------------- 17 | // 18 | // Instance of the midi out filter class. 19 | 20 | #include "module_tester/midi_dispatcher.h" 21 | 22 | namespace module_tester { 23 | 24 | /* extern */ 25 | MidiDispatcher midi_dispatcher; 26 | 27 | } // namespace module_tester 28 | 29 | -------------------------------------------------------------------------------- /module_tester/midi_dispatcher.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | // ----------------------------------------------------------------------------- 17 | 18 | #ifndef MODULE_TESTER_MIDI_DISPATCHER_H_ 19 | #define MODULE_TESTER_MIDI_DISPATCHER_H_ 20 | 21 | #include "module_tester/signal_generator.h" 22 | 23 | #include "midi/midi.h" 24 | 25 | namespace module_tester { 26 | 27 | class MidiDispatcher : public midi::MidiDevice { 28 | public: 29 | MidiDispatcher() { } 30 | 31 | // ------ MIDI in handling --------------------------------------------------- 32 | 33 | // Forwarded to the controller. 34 | static inline void NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) { 35 | signal_generator.NoteOn(note, velocity); 36 | } 37 | static inline void NoteOff(uint8_t channel, uint8_t note, uint8_t velocity) { 38 | signal_generator.NoteOff(note); 39 | } 40 | 41 | // Handled. 42 | static inline void ControlChange( 43 | uint8_t channel, 44 | uint8_t controller, 45 | uint8_t value) { 46 | if (controller == midi::kModulationWheelMsb) { 47 | signal_generator.Modulation(value); 48 | } 49 | } 50 | static inline void PitchBend(uint8_t channel, uint16_t pitch_bend) { 51 | signal_generator.PitchBend(pitch_bend); 52 | } 53 | static void Aftertouch(uint8_t channel, uint8_t note, uint8_t velocity) { } 54 | static void Aftertouch(uint8_t channel, uint8_t velocity) { } 55 | static void AllSoundOff(uint8_t channel) { } 56 | static void ResetAllControllers(uint8_t channel) { } 57 | static void AllNotesOff(uint8_t channel) { } 58 | static void OmniModeOff(uint8_t channel) { } 59 | static void OmniModeOn(uint8_t channel) { } 60 | static void ProgramChange(uint8_t channel, uint8_t program) { } 61 | 62 | static void Reset() { } 63 | static void Clock() { } 64 | static void Start() { } 65 | static void Stop() { } 66 | static void Continue() { } 67 | 68 | static void SysExStart() { } 69 | static void SysExByte(uint8_t sysex_byte) { } 70 | static void SysExEnd() { } 71 | 72 | static uint8_t CheckChannel(uint8_t channel) { 73 | return 1; 74 | } 75 | 76 | static void RawMidiData( 77 | uint8_t status, 78 | uint8_t* data, 79 | uint8_t data_size, 80 | uint8_t accepted_channel) { 81 | } 82 | 83 | private: 84 | DISALLOW_COPY_AND_ASSIGN(MidiDispatcher); 85 | }; 86 | 87 | extern MidiDispatcher midi_dispatcher; 88 | 89 | } // namespace module_tester 90 | 91 | #endif // MODULE_TESTER_MIDI_DISPATCHER_H_ 92 | -------------------------------------------------------------------------------- /module_tester/module_tester.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Emilie Gillet.. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | 16 | #include "avrlib/boot.h" 17 | #include "avrlib/gpio.h" 18 | #include "avrlib/parallel_io.h" 19 | #include "avrlib/timer.h" 20 | #include "avrlib/watchdog_timer.h" 21 | 22 | #include "midi/midi.h" 23 | 24 | #include "module_tester/frequency_counter.h" 25 | #include "module_tester/hardware_config.h" 26 | #include "module_tester/midi_dispatcher.h" 27 | #include "module_tester/pulse_counter.h" 28 | #include "module_tester/signal_generator.h" 29 | #include "module_tester/ui.h" 30 | 31 | using namespace avrlib; 32 | using namespace module_tester; 33 | using namespace midi; 34 | 35 | MidiIO midi_io; 36 | MidiBuffer midi_in_buffer; 37 | MidiStreamParser midi_parser; 38 | 39 | SystemTimer sys_timer; 40 | AudioTimer audio_timer; 41 | TuningTimer tuning_timer; 42 | 43 | ClockOutput clock_output; 44 | GateOutput gate_output; 45 | GateInput gate_input; 46 | 47 | DacInterface dac_interface; 48 | 49 | volatile uint8_t midi_clock_ticks = 0; 50 | 51 | inline void PollMidiIn() { 52 | if (midi_io.readable()) { 53 | uint8_t byte = midi_io.ImmediateRead(); 54 | if (byte == 0xf8) { 55 | ++midi_clock_ticks; 56 | } else { 57 | midi_in_buffer.NonBlockingWrite(byte); 58 | } 59 | } 60 | } 61 | 62 | // 4.9kHz: MIDI in/out; gate & clock poll. 63 | // 1.22kHz: UI poll 64 | ISR(TIMER0_OVF_vect, ISR_NOBLOCK) { 65 | static uint8_t cycle = 0; 66 | PollMidiIn(); 67 | if ((cycle & 0x1) == 0) { 68 | ui.Poll(); 69 | } 70 | if ((cycle & 0x3) == 0) { 71 | TickSystemClock(); 72 | } 73 | ++cycle; 74 | } 75 | 76 | // 2.5 MHz timebase used for time measurements 77 | volatile uint8_t num_overflows; 78 | 79 | ISR(TIMER1_CAPT_vect) { 80 | static uint16_t previous_time = 0; 81 | uint16_t current_time = ICR1; 82 | uint32_t interval = current_time + \ 83 | (static_cast(num_overflows) << 16) - previous_time; 84 | previous_time = current_time; 85 | num_overflows = 0; 86 | frequency_counter.UpdateFrequencyMeasurement(interval); 87 | } 88 | 89 | ISR(TIMER1_OVF_vect, ISR_NOBLOCK) { 90 | ++num_overflows; 91 | } 92 | 93 | static uint32_t pulse_duration; 94 | static uint32_t pulse_period; 95 | static uint8_t counter; 96 | static uint8_t gate_state; 97 | 98 | // 39kHz audio output clock 99 | ISR(TIMER2_OVF_vect) { 100 | Word sample_12bits; 101 | 102 | dac_interface.Strobe(); 103 | if (!(counter & 0x3)) { 104 | Word cv_sample; 105 | cv_sample.value = signal_generator.ReadCvSample(); 106 | sample_12bits.value = (cv_sample.value & 0x0fff) | 0x9000; 107 | dac_interface.Overwrite(sample_12bits.bytes[1]); 108 | if (signal_generator.data().clock.midi_mode) { 109 | uint8_t prescaler = signal_generator.clock_prescaler(); 110 | if (midi_clock_ticks >= prescaler) { 111 | midi_clock_ticks -= prescaler; 112 | signal_generator.RaiseClockPulse(); 113 | } 114 | if (signal_generator.DecreaseClockPulse()) { 115 | cv_sample.bytes[1] |= 0x80; 116 | } 117 | } 118 | clock_output.set_value(cv_sample.bytes[1] & 0x80); 119 | gate_output.set_value(cv_sample.bytes[1] & 0x40); 120 | dac_interface.Wait(); 121 | dac_interface.Send(sample_12bits.bytes[0]); 122 | dac_interface.Strobe(); 123 | } 124 | 125 | sample_12bits.value = signal_generator.ReadAudioSample() | 0x1000; 126 | dac_interface.Overwrite(sample_12bits.bytes[1]); 127 | uint8_t new_gate_state = !gate_input.value(); 128 | ui.set_gate(new_gate_state); 129 | if (gate_state && !new_gate_state) { 130 | pulse_duration = pulse_period; 131 | } 132 | if (new_gate_state && !gate_state) { 133 | pulse_counter.UpdatePulseMeasurement(pulse_duration, pulse_period); 134 | signal_generator.Trigger(); 135 | pulse_period = 0; 136 | } 137 | gate_state = new_gate_state; 138 | signal_generator.set_gate_state(new_gate_state); 139 | dac_interface.Wait(); 140 | dac_interface.Overwrite(sample_12bits.bytes[0]); 141 | ++pulse_period; 142 | ++counter; 143 | dac_interface.Wait(); 144 | } 145 | 146 | inline void Init() { 147 | sei(); 148 | UCSR0B = 0; 149 | ResetWatchdog(); 150 | 151 | midi_io.Init(); 152 | 153 | dac_interface.Init(); 154 | 155 | signal_generator.Init(); 156 | frequency_counter.Init(); 157 | pulse_counter.Init(); 158 | ui.Init(); 159 | 160 | sys_timer.set_prescaler(2); 161 | sys_timer.set_mode(TIMER_PWM_PHASE_CORRECT); 162 | sys_timer.Start(); 163 | 164 | clock_output.set_mode(DIGITAL_OUTPUT); 165 | gate_output.set_mode(DIGITAL_OUTPUT); 166 | gate_input.set_mode(DIGITAL_INPUT); 167 | 168 | audio_timer.set_prescaler(1); 169 | audio_timer.set_mode(TIMER_PWM_PHASE_CORRECT); 170 | audio_timer.Start(); 171 | 172 | dac_interface.Strobe(); 173 | dac_interface.Send(0x10 | 0x0f); 174 | dac_interface.Send(0xff); 175 | 176 | tuning_timer.set_mode(TIMER_NORMAL); 177 | tuning_timer.set_prescaler(1); 178 | tuning_timer.Start(); 179 | tuning_timer.StartInputCapture(); 180 | } 181 | 182 | int main(void) { 183 | Init(); 184 | ui.FlushEvents(); 185 | while (1) { 186 | signal_generator.Render(); 187 | 188 | // Handle UI events 189 | ui.DoEvents(); 190 | 191 | // Process MIDI events 192 | while (midi_in_buffer.readable()) { 193 | midi_parser.PushByte(midi_in_buffer.ImmediateRead()); 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /module_tester/note_stack.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | // ----------------------------------------------------------------------------- 17 | // 18 | // Stack of currently pressed keys. 19 | // 20 | // Currently pressed keys are stored as a linked list. The linked list is used 21 | // as a LIFO stack to allow monosynth-like behaviour. An example of such 22 | // behaviour is: 23 | // player presses and holds C4-> C4 is played. 24 | // player presses and holds C5 (while holding C4) -> C5 is played. 25 | // player presses and holds G4 (while holding C4&C5)-> G4 is played. 26 | // player releases C5 -> G4 is played. 27 | // player releases G4 -> C4 is played. 28 | // 29 | // The nodes used in the linked list are pre-allocated from a pool of 16 30 | // nodes, so the "pointers" (to the root element for example) are not actual 31 | // pointers, but indices of an element in the pool. 32 | // 33 | // Additionally, an array of pointers is stored to allow random access to the 34 | // n-th note, sorted by ascending order of pitch (for arpeggiation). 35 | 36 | #ifndef MODULE_TESTER_NOTE_STACK_H_ 37 | #define MODULE_TESTER_NOTE_STACK_H_ 38 | 39 | #include "avrlib/base.h" 40 | 41 | #include 42 | 43 | namespace module_tester { 44 | 45 | static const uint8_t kFreeSlot = 0xff; 46 | 47 | struct NoteEntry { 48 | uint8_t note; 49 | uint8_t velocity; 50 | uint8_t next_ptr; // Base 1. 51 | }; 52 | 53 | // This looks crazy, but we are more concerned about RAM used than code size here. 54 | template 55 | class NoteStack { 56 | public: 57 | NoteStack() { } 58 | void Init() { Clear(); } 59 | 60 | void NoteOn(uint8_t note, uint8_t velocity) { 61 | // Remove the note from the list first (in case it is already here). 62 | NoteOff(note); 63 | // In case of saturation, remove the least recently played note from the 64 | // stack. 65 | if (size_ == capacity) { 66 | uint8_t least_recent_note = 0; 67 | for (uint8_t i = 1; i <= capacity; ++i) { 68 | if (pool_[i].next_ptr == 0) { 69 | least_recent_note = pool_[i].note; 70 | } 71 | } 72 | NoteOff(least_recent_note); 73 | } 74 | // Now we are ready to insert the new note. Find a free slot to insert it. 75 | uint8_t free_slot = 0; 76 | for (uint8_t i = 1; i <= capacity; ++i) { 77 | if (pool_[i].note == kFreeSlot) { 78 | free_slot = i; 79 | break; 80 | } 81 | } 82 | pool_[free_slot].next_ptr = root_ptr_; 83 | pool_[free_slot].note = note; 84 | pool_[free_slot].velocity = velocity; 85 | root_ptr_ = free_slot; 86 | // The last step consists in inserting the note in the sorted list. 87 | for (uint8_t i = 0; i < size_; ++i) { 88 | if (pool_[sorted_ptr_[i]].note > note) { 89 | for (uint8_t j = size_; j > i; --j) { 90 | sorted_ptr_[j] = sorted_ptr_[j - 1]; 91 | } 92 | sorted_ptr_[i] = free_slot; 93 | free_slot = 0; 94 | break; 95 | } 96 | } 97 | if (free_slot) { 98 | sorted_ptr_[size_] = free_slot; 99 | } 100 | ++size_; 101 | } 102 | 103 | void NoteOff(uint8_t note) { 104 | uint8_t current = root_ptr_; 105 | uint8_t previous = 0; 106 | while (current) { 107 | if (pool_[current].note == note) { 108 | break; 109 | } 110 | previous = current; 111 | current = pool_[current].next_ptr; 112 | } 113 | if (current) { 114 | if (previous) { 115 | pool_[previous].next_ptr = pool_[current].next_ptr; 116 | } else { 117 | root_ptr_ = pool_[current].next_ptr; 118 | } 119 | for (uint8_t i = 0; i < size_; ++i) { 120 | if (sorted_ptr_[i] == current) { 121 | for (uint8_t j = i; j < size_ - 1; ++j) { 122 | sorted_ptr_[j] = sorted_ptr_[j + 1]; 123 | } 124 | break; 125 | } 126 | } 127 | pool_[current].next_ptr = 0; 128 | pool_[current].note = kFreeSlot; 129 | pool_[current].velocity = 0; 130 | --size_; 131 | } 132 | } 133 | 134 | void Clear() { 135 | size_ = 0; 136 | memset(pool_ + 1, 0, sizeof(NoteEntry) * capacity); 137 | memset(sorted_ptr_ + 1, 0, capacity); 138 | root_ptr_ = 0; 139 | for (uint8_t i = 0; i <= capacity; ++i) { 140 | pool_[i].note = kFreeSlot; 141 | } 142 | } 143 | 144 | uint8_t size() const { return size_; } 145 | const NoteEntry& most_recent_note() const { return pool_[root_ptr_]; } 146 | const NoteEntry& least_recent_note() const { 147 | uint8_t current = root_ptr_; 148 | while (current && pool_[current].next_ptr) { 149 | current = pool_[current].next_ptr; 150 | } 151 | return pool_[current]; 152 | } 153 | const NoteEntry& played_note(uint8_t index) const { 154 | uint8_t current = root_ptr_; 155 | index = size_ - index - 1; 156 | for (uint8_t i = 0; i < index; ++i) { 157 | current = pool_[current].next_ptr; 158 | } 159 | return pool_[current]; 160 | } 161 | const NoteEntry& sorted_note(uint8_t index) const { 162 | return pool_[sorted_ptr_[index]]; 163 | } 164 | const NoteEntry& note(uint8_t index) const { return pool_[index]; } 165 | const NoteEntry& dummy() const { return pool_[0]; } 166 | 167 | private: 168 | uint8_t size_; 169 | NoteEntry pool_[capacity + 1]; // First element is a dummy node! 170 | uint8_t root_ptr_; // Base 1. 171 | uint8_t sorted_ptr_[capacity + 1]; // Base 1. 172 | 173 | DISALLOW_COPY_AND_ASSIGN(NoteStack); 174 | }; 175 | 176 | } // namespace module_tester 177 | 178 | #endif // MODULE_TESTER_NOTE_STACK_H_ 179 | -------------------------------------------------------------------------------- /module_tester/parameter.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Emilie Gillet.. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | // ----------------------------------------------------------------------------- 17 | // 18 | // Parameter definitions. 19 | 20 | #include "module_tester/parameter.h" 21 | 22 | #include 23 | 24 | #include "avrlib/string.h" 25 | 26 | #include "module_tester/frequency_counter.h" 27 | #include "module_tester/pulse_counter.h" 28 | #include "module_tester/signal_generator.h" 29 | #include "module_tester/ui.h" 30 | 31 | namespace module_tester { 32 | 33 | uint8_t Parameter::Increment( 34 | uint8_t current_value, 35 | int8_t increment) const { 36 | int16_t value = current_value; 37 | uint8_t new_value = current_value; 38 | value += increment; 39 | if (value >= min_value && value <= max_value) { 40 | new_value = static_cast(value); 41 | } 42 | return new_value; 43 | } 44 | 45 | static const prog_char note_names[] PROGMEM = " CC# DD# E FF# GG# AA# B"; 46 | static const prog_char octaves[] PROGMEM = "-0123456789"; 47 | 48 | /* static */ 49 | void Parameter::PrintNote(uint8_t note, char* buffer) { 50 | uint8_t octave = 0; 51 | while (note >= 12) { 52 | ++octave; 53 | note -= 12; 54 | } 55 | *buffer++ = ResourcesManager::Lookup( 56 | note_names, note << 1); 57 | *buffer++ = ResourcesManager::Lookup( 58 | note_names, 1 + (note << 1)); 59 | *buffer++ = ResourcesManager::Lookup( 60 | octaves, octave); 61 | *buffer = '\0'; 62 | } 63 | 64 | void Parameter::Format( 65 | uint8_t value, 66 | char prefix, 67 | char* buffer, 68 | uint8_t width) const { 69 | ResourcesManager::LoadStringResource(name, buffer, 6); 70 | AlignLeft(buffer, 6); 71 | buffer += 7; 72 | width -= 7; 73 | 74 | buffer[-1] = prefix; 75 | if (unit == UNIT_INTEGER) { 76 | UnsafeItoa(value, width, buffer); 77 | } else if (unit == UNIT_NOTE_FREQUENCY) { 78 | if (value >= 9) { 79 | PrintNote(value - 9 + 12, buffer); 80 | } else { 81 | ResourcesManager::LoadStringResource(STR_10HZ + value, buffer, width); 82 | } 83 | } else { 84 | ResourcesManager::LoadStringResource(unit + value, buffer, width); 85 | } 86 | AlignRight(buffer - 1, width + 1); 87 | } 88 | 89 | static const prog_Parameter parameters[] PROGMEM = { 90 | // 0 91 | { SG_PRM_CLOCK_TEMPO, 92 | UNIT_INTEGER, 40, 240, 93 | STR_TEMPO }, 94 | 95 | // 1 96 | { SG_PRM_CLOCK_RESOLUTION, 97 | STR_1_PPQN, CLOCK_RESOLUTION_1_PPQN, CLOCK_RESOLUTION_24_PPQN, 98 | STR_RESOL_ }, 99 | 100 | // 2 101 | { SG_PRM_CLOCK_PULSE_DURATION, 102 | STR_1MS, PULSE_DURATION_1_MS, PULSE_DURATION_50_PERCENT, 103 | STR_PULSE }, 104 | 105 | // 3 106 | { SG_PRM_CLOCK_MIDI_MODE, 107 | STR_OFF, 0, 1, 108 | STR_MIDI }, 109 | 110 | // 4 111 | { SG_PRM_GATE_PERIOD, 112 | STR__10MS, PERIOD_10_MS, PERIOD_10_S, 113 | STR_PERIOD }, 114 | 115 | // 5 116 | { SG_PRM_GATE_PULSE_DURATION, 117 | STR_1MS, PULSE_DURATION_1_MS, PULSE_DURATION_50_PERCENT, 118 | STR_PULSE }, 119 | 120 | // 6 121 | { SG_PRM_GATE_MIDI_MODE, 122 | STR__OFF, GATE_MIDI_MODE_OFF, GATE_MIDI_MODE_TRIGGER, 123 | STR_MIDI }, 124 | 125 | // 7 126 | { SG_PRM_CV_MODE, 127 | STR_TRIANGLE, CV_MODE_TRIANGLE_LFO, CV_MODE_C3_NOTE, 128 | STR_MODE }, 129 | 130 | // 8 131 | { SG_PRM_CV_PERIOD, 132 | STR__10MS, PERIOD_10_MS, PERIOD_10_S, 133 | STR_PERIOD }, 134 | 135 | // 8 136 | { SG_PRM_CV_RANGE, 137 | STR_11V, CV_RANGE_PM_1V, CV_RANGE_0_5V, 138 | STR_RANGE }, 139 | 140 | // 9 141 | { SG_PRM_CV_MIDI_MODE, 142 | STR___OFF, CV_MIDI_MODE_OFF, CV_MIDI_MODE_MOD_WHEEL, 143 | STR_MIDI }, 144 | 145 | // 10 146 | { SG_PRM_AUDIO_MODE, 147 | STR_SAW, AUDIO_MODE_SAW, AUDIO_MODE_NOISE, 148 | STR_SHAPE }, 149 | 150 | // 11 151 | { SG_PRM_AUDIO_FREQUENCY, 152 | UNIT_NOTE_FREQUENCY, 0, 96 + 9, 153 | STR_FREQ_ }, 154 | 155 | // 12 156 | { SG_PRM_AUDIO_ENVELOPE_MODE, 157 | STR__OFF, AUDIO_ENVELOPE_MODE_OFF, AUDIO_ENVELOPE_MODE_TRIGGER, 158 | STR_ENVEL_ }, 159 | 160 | // 13 161 | { SG_PRM_AUDIO_MIDI_MODE, 162 | STR____OFF, AUDIO_MIDI_MODE_OFF, AUDIO_MIDI_MODE_ON, 163 | STR_MIDI }, 164 | 165 | // 14 166 | { FC_PARAMETER_UNIT | DOMAIN_FREQUENCY_COUNTER, 167 | 0, FREQUENCY_COUNTER_UNIT_HZ, FREQUENCY_COUNTER_UNIT_INTERVAL, 168 | 0 }, 169 | 170 | // 15 171 | { PC_PARAMETER_UNIT | DOMAIN_PULSE_COUNTER, 172 | 0, PULSE_COUNTER_UNIT_SECONDS, PULSE_COUNTER_UNIT_BPM_24, 173 | 0 }, 174 | 175 | // 16 176 | { PC_PARAMETER_DURATION_UNIT | DOMAIN_PULSE_COUNTER, 177 | 0, PULSE_COUNTER_DURATION_UNIT_SECONDS, 178 | PULSE_COUNTER_DURATION_UNIT_DUTY_CYCLE, 179 | 0 } 180 | 181 | }; 182 | 183 | /* static */ 184 | Parameter ParameterManager::cached_definition_; 185 | 186 | /* static */ 187 | uint8_t ParameterManager::cached_index_ = 0xff; 188 | 189 | /* static */ 190 | const Parameter& ParameterManager::parameter(uint8_t index) { 191 | if (index != cached_index_) { 192 | memcpy_P(&cached_definition_, ¶meters[index], sizeof(Parameter)); 193 | cached_index_ = index; 194 | } 195 | return cached_definition_; 196 | } 197 | 198 | /* static */ 199 | void ParameterManager::SetValue(const Parameter& parameter, uint8_t value) { 200 | uint8_t* bytes = static_cast( 201 | static_cast(signal_generator.mutable_data())); 202 | uint8_t offset = parameter.offset; 203 | if (offset & DOMAIN_FREQUENCY_COUNTER) { 204 | bytes = static_cast( 205 | static_cast(frequency_counter.mutable_data())); 206 | } else if (offset & DOMAIN_PULSE_COUNTER) { 207 | bytes = static_cast( 208 | static_cast(pulse_counter.mutable_data())); 209 | } 210 | bytes[offset & DOMAIN_OFFSET_MASK] = value; 211 | } 212 | 213 | /* static */ 214 | uint8_t ParameterManager::GetValue(const Parameter& parameter) { 215 | if (parameter.offset & DOMAIN_FREQUENCY_COUNTER) { 216 | const uint8_t* bytes = static_cast( 217 | static_cast(&frequency_counter.data())); 218 | return bytes[parameter.offset & DOMAIN_OFFSET_MASK]; 219 | } else if (parameter.offset & DOMAIN_PULSE_COUNTER) { 220 | const uint8_t* bytes = static_cast( 221 | static_cast(&pulse_counter.data())); 222 | return bytes[parameter.offset & DOMAIN_OFFSET_MASK]; 223 | } else { 224 | const uint8_t* bytes = static_cast( 225 | static_cast(&signal_generator.data())); 226 | return bytes[parameter.offset]; 227 | } 228 | } 229 | 230 | /* extern */ 231 | ParameterManager parameter_manager; 232 | 233 | } // namespace module_tester 234 | -------------------------------------------------------------------------------- /module_tester/parameter.h: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Emilie Gillet.. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | // ----------------------------------------------------------------------------- 17 | // 18 | // Parameter definitions. 19 | 20 | #ifndef MODULE_TESTER_PARAMETER_DEFINITIONS_H_ 21 | #define MODULE_TESTER_PARAMETER_DEFINITIONS_H_ 22 | 23 | #include "avrlib/base.h" 24 | 25 | #include "module_tester/resources.h" 26 | 27 | namespace module_tester { 28 | 29 | enum Unit { 30 | UNIT_INTEGER, 31 | UNIT_NOTE_FREQUENCY, 32 | UNIT_OTHER 33 | }; 34 | 35 | enum Domain { 36 | DOMAIN_SIGNAL_GENERATOR, 37 | DOMAIN_FREQUENCY_COUNTER = 0x20, 38 | DOMAIN_PULSE_COUNTER = 0x40, 39 | DOMAIN_OFFSET_MASK = 0x1f 40 | }; 41 | 42 | enum GlobalParameter { 43 | PRM_CLOCK_TEMPO, 44 | PRM_CLOCK_RESOLUTION, 45 | PRM_CLOCK_PULSE_DURATION, 46 | PRM_CLOCK_MIDI_MODE, 47 | 48 | PRM_GATE_PERIOD, 49 | PRM_GATE_PULSE_DURATION, 50 | PRM_GATE_MIDI_MODE, 51 | 52 | PRM_CV_MODE, 53 | PRM_CV_PERIOD, 54 | PRM_CV_RANGE, 55 | PRM_CV_MIDI_MODE, 56 | 57 | PRM_SYNTH_MODE, 58 | PRM_SYNTH_FREQUENCY, 59 | PRM_SYNTH_ENVELOPE, 60 | PRM_SYNTH_MIDI_MODE, 61 | 62 | PRM_FREQUENCY_COUNTER_UNIT, 63 | 64 | PRM_PULSE_COUNTER_UNIT, 65 | PRM_PULSE_COUNTER_DURATION_UNIT, 66 | }; 67 | 68 | struct Parameter { 69 | uint8_t offset; 70 | ResourceId unit; 71 | uint8_t min_value; 72 | uint8_t max_value; 73 | ResourceId name; 74 | 75 | uint8_t Increment(uint8_t current_value, int8_t increment) const; 76 | void Format(uint8_t value, char prefix, char* buffer, uint8_t width) const; 77 | static void PrintNote(uint8_t note, char* buffer); 78 | }; 79 | 80 | typedef Parameter PROGMEM prog_Parameter; 81 | 82 | class ParameterManager { 83 | public: 84 | ParameterManager() { } 85 | static const Parameter& parameter(uint8_t index); 86 | 87 | static void SetValue(const Parameter& parameter, uint8_t value); 88 | 89 | static uint8_t GetValue(const Parameter& parameter); 90 | 91 | static void Increment(const Parameter& parameter, int8_t increment) { 92 | uint8_t value = GetValue(parameter); 93 | value = parameter.Increment(value, increment); 94 | SetValue(parameter, value); 95 | } 96 | 97 | private: 98 | static Parameter cached_definition_; 99 | static uint8_t cached_index_; 100 | 101 | DISALLOW_COPY_AND_ASSIGN(ParameterManager); 102 | }; 103 | 104 | extern ParameterManager parameter_manager; 105 | 106 | } // namespace module_tester 107 | 108 | #endif // MODULE_TESTER_PARAMETER_DEFINITIONS_H_ 109 | -------------------------------------------------------------------------------- /module_tester/pulse_counter.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Emilie Gillet.. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | // ----------------------------------------------------------------------------- 17 | // 18 | // Signal generator. 19 | 20 | #include "module_tester/pulse_counter.h" 21 | 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | #include "avrlib/string.h" 28 | 29 | #include "module_tester/parameter.h" 30 | 31 | namespace module_tester { 32 | 33 | /* static */ 34 | PulseCounterSettings PulseCounter::data_; 35 | 36 | /* static */ 37 | uint32_t PulseCounter::duration_; 38 | 39 | /* static */ 40 | uint32_t PulseCounter::period_; 41 | 42 | /* static */ 43 | uint16_t PulseCounter::num_measurements_; 44 | 45 | /* static */ 46 | uint32_t PulseCounter::current_duration_; 47 | 48 | /* static */ 49 | uint32_t PulseCounter::current_period_; 50 | 51 | /* static */ 52 | uint16_t PulseCounter::current_num_measurements_; 53 | 54 | /* static */ 55 | void PulseCounter::Init() { 56 | data_.unit = PULSE_COUNTER_UNIT_HZ; 57 | data_.duration_unit = PULSE_COUNTER_DURATION_UNIT_DUTY_CYCLE; 58 | } 59 | 60 | const prog_uint8_t bpm_divider[] PROGMEM = { 1, 4, 8, 24 }; 61 | 62 | /* static */ 63 | void PulseCounter::PrintPeriod(char* buffer, uint8_t width) { 64 | if (data_.unit == PULSE_COUNTER_UNIT_SECONDS) { 65 | float period = (510.0f / F_CPU) * \ 66 | static_cast(current_period_) / \ 67 | static_cast(current_num_measurements_); 68 | if (current_period_ == 0 || current_num_measurements_ == 0) { 69 | period = 0.0f; 70 | } 71 | if (period < 0.1f) { 72 | sprintf(buffer, "per.%.2fms", period * 1000.0f); 73 | } else { 74 | sprintf(buffer, "per.%.3fs", period); 75 | } 76 | } else if (data_.unit == PULSE_COUNTER_UNIT_HZ) { 77 | float frequency = (F_CPU / 510) * \ 78 | static_cast(current_num_measurements_) / \ 79 | static_cast(current_period_); 80 | if (current_period_ == 0 || current_num_measurements_ == 0) { 81 | frequency = 0.0f; 82 | } 83 | sprintf(buffer, "freq%.2fHz", frequency); 84 | } else { 85 | uint8_t divider = pgm_read_byte( 86 | bpm_divider + data_.unit - PULSE_COUNTER_UNIT_BPM_1); 87 | float bpm = 60 * (F_CPU / 510) * \ 88 | static_cast(current_num_measurements_) / \ 89 | static_cast(current_period_) / static_cast(divider); 90 | if (current_period_ == 0 || current_num_measurements_ == 0) { 91 | bpm = 0.0f; 92 | } 93 | sprintf(buffer, "bpm %.1f @%d", bpm, divider); 94 | } 95 | avrlib::AlignRight(buffer + 4, width - 4); 96 | } 97 | 98 | /* static */ 99 | void PulseCounter::PrintDuration(char* buffer, uint8_t width) { 100 | if (data_.duration_unit == PULSE_COUNTER_DURATION_UNIT_SECONDS) { 101 | float duration = (1000.0f * 510.0f / F_CPU) * \ 102 | static_cast(current_duration_) / \ 103 | static_cast(current_num_measurements_); 104 | if (current_duration_ == 0 || current_num_measurements_ == 0) { 105 | duration = 0.0f; 106 | } 107 | sprintf(buffer, "dur.%4.2fms", duration); 108 | } else { 109 | float ratio = 100.0f * static_cast(current_duration_) / \ 110 | static_cast(current_period_); 111 | if (current_duration_ == 0 || current_num_measurements_ == 0) { 112 | ratio = 0.0f; 113 | } 114 | sprintf(buffer, "duty%.2f%%", ratio); 115 | } 116 | avrlib::AlignRight(buffer + 4, width - 4); 117 | } 118 | 119 | /*extern */ 120 | PulseCounter pulse_counter; 121 | 122 | } // namespace module_tester 123 | -------------------------------------------------------------------------------- /module_tester/pulse_counter.h: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Emilie Gillet.. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | // ----------------------------------------------------------------------------- 17 | // 18 | // Signal generator. 19 | 20 | #ifndef MODULE_TESTER_PULSE_COUNTER_H_ 21 | #define MODULE_TESTER_PULSE_COUNTER_H_ 22 | 23 | #include "avrlib/base.h" 24 | 25 | namespace module_tester { 26 | 27 | enum PulseCounterUnit { 28 | PULSE_COUNTER_UNIT_SECONDS, 29 | PULSE_COUNTER_UNIT_HZ, 30 | PULSE_COUNTER_UNIT_BPM_1, 31 | PULSE_COUNTER_UNIT_BPM_4, 32 | PULSE_COUNTER_UNIT_BPM_8, 33 | PULSE_COUNTER_UNIT_BPM_24 34 | }; 35 | 36 | enum PulseCounterDurationUnit { 37 | PULSE_COUNTER_DURATION_UNIT_SECONDS, 38 | PULSE_COUNTER_DURATION_UNIT_DUTY_CYCLE, 39 | }; 40 | 41 | struct PulseCounterSettings { 42 | PulseCounterUnit unit; 43 | PulseCounterDurationUnit duration_unit; 44 | }; 45 | 46 | enum PulseCounterParameter { 47 | PC_PARAMETER_UNIT, 48 | PC_PARAMETER_DURATION_UNIT, 49 | }; 50 | 51 | class PulseCounter { 52 | public: 53 | PulseCounter() { } 54 | ~PulseCounter() { } 55 | 56 | static void Init(); 57 | static void UpdatePulseMeasurement(uint32_t duration, uint32_t period) { 58 | duration_ += duration; 59 | period_ += period; 60 | ++num_measurements_; 61 | 62 | if (period_ > (F_CPU / 510 / 2)) { 63 | current_duration_ = duration_; 64 | current_period_ = period_; 65 | current_num_measurements_ = num_measurements_; 66 | duration_ = 0; 67 | period_ = 0; 68 | num_measurements_ = 0; 69 | } 70 | } 71 | 72 | static const PulseCounterSettings& data() { return data_; } 73 | static PulseCounterSettings* mutable_data() { return &data_; } 74 | 75 | static void PrintPeriod(char* buffer, uint8_t width); 76 | static void PrintDuration(char* buffer, uint8_t width); 77 | 78 | private: 79 | static PulseCounterSettings data_; 80 | 81 | static uint32_t duration_; 82 | static uint32_t period_; 83 | static uint16_t num_measurements_; 84 | 85 | static uint32_t current_duration_; 86 | static uint32_t current_period_; 87 | static uint16_t current_num_measurements_; 88 | 89 | DISALLOW_COPY_AND_ASSIGN(PulseCounter); 90 | }; 91 | 92 | extern PulseCounter pulse_counter; 93 | 94 | } // namespace module_tester 95 | 96 | #endif // MODULE_TESTER_PULSE_COUNTER_H_ 97 | -------------------------------------------------------------------------------- /module_tester/resources.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Emilie Gillet.. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | // ----------------------------------------------------------------------------- 17 | // 18 | // Resources definitions. 19 | // 20 | // Automatically generated with: 21 | // make resources 22 | 23 | 24 | #include "module_tester/resources.h" 25 | 26 | namespace module_tester { 27 | 28 | static const prog_char str_tempo[] PROGMEM = "tempo"; 29 | static const prog_char str_resol_[] PROGMEM = "resol."; 30 | static const prog_char str_pulse[] PROGMEM = "pulse"; 31 | static const prog_char str_midi[] PROGMEM = "midi"; 32 | static const prog_char str_period[] PROGMEM = "period"; 33 | static const prog_char str_mode[] PROGMEM = "mode"; 34 | static const prog_char str_shape[] PROGMEM = "shape"; 35 | static const prog_char str_range[] PROGMEM = "range"; 36 | static const prog_char str_freq_[] PROGMEM = "freq."; 37 | static const prog_char str_envel_[] PROGMEM = "envel."; 38 | static const prog_char str_off[] PROGMEM = "off"; 39 | static const prog_char str_on[] PROGMEM = "on"; 40 | static const prog_char str_note[] PROGMEM = "note"; 41 | static const prog_char str_gate[] PROGMEM = "gate"; 42 | static const prog_char str_1_ppqn[] PROGMEM = "1 ppqn"; 43 | static const prog_char str_4_ppqn[] PROGMEM = "4 ppqn"; 44 | static const prog_char str_8_ppqn[] PROGMEM = "8 ppqn"; 45 | static const prog_char str_24_ppqn[] PROGMEM = "24 ppqn"; 46 | static const prog_char str_1ms[] PROGMEM = "1ms"; 47 | static const prog_char str_2ms[] PROGMEM = "2ms"; 48 | static const prog_char str_5ms[] PROGMEM = "5ms"; 49 | static const prog_char str_10ms[] PROGMEM = "10ms"; 50 | static const prog_char str_5_[] PROGMEM = "5%"; 51 | static const prog_char str_10_[] PROGMEM = "10%"; 52 | static const prog_char str_20_[] PROGMEM = "20%"; 53 | static const prog_char str_50_[] PROGMEM = "50%"; 54 | static const prog_char str_20ms[] PROGMEM = "20ms"; 55 | static const prog_char str_50ms[] PROGMEM = "50ms"; 56 | static const prog_char str_100ms[] PROGMEM = "100ms"; 57 | static const prog_char str_200ms[] PROGMEM = "200ms"; 58 | static const prog_char str_500ms[] PROGMEM = "500ms"; 59 | static const prog_char str_1s[] PROGMEM = "1s"; 60 | static const prog_char str_2s[] PROGMEM = "2s"; 61 | static const prog_char str_5s[] PROGMEM = "5s"; 62 | static const prog_char str_10s[] PROGMEM = "10s"; 63 | static const prog_char str_triangle[] PROGMEM = "triangle"; 64 | static const prog_char str_square[] PROGMEM = "square"; 65 | static const prog_char str_ramp_up[] PROGMEM = "ramp up"; 66 | static const prog_char str_ramp_dn[] PROGMEM = "ramp dn"; 67 | static const prog_char str_sine[] PROGMEM = "sine"; 68 | static const prog_char str_1oct_arp[] PROGMEM = "1oct arp"; 69 | static const prog_char str_2oct_arp[] PROGMEM = "2oct arp"; 70 | static const prog_char str_chrm_scl[] PROGMEM = "chrm.scl"; 71 | static const prog_char str_c1_note[] PROGMEM = "c1 note"; 72 | static const prog_char str_c3_note[] PROGMEM = "c3 note"; 73 | static const prog_char str_trigger[] PROGMEM = "trigger"; 74 | static const prog_char str_velocity[] PROGMEM = "velocity"; 75 | static const prog_char str_ptchbend[] PROGMEM = "ptchbend"; 76 | static const prog_char str_modwheel[] PROGMEM = "modwheel"; 77 | static const prog_char str_saw[] PROGMEM = "saw"; 78 | static const prog_char str_noise[] PROGMEM = "noise"; 79 | static const prog_char str_10hz[] PROGMEM = "10Hz"; 80 | static const prog_char str_20hz[] PROGMEM = "20Hz"; 81 | static const prog_char str_50hz[] PROGMEM = "50Hz"; 82 | static const prog_char str_100hz[] PROGMEM = "100Hz"; 83 | static const prog_char str_200hz[] PROGMEM = "200Hz"; 84 | static const prog_char str_500hz[] PROGMEM = "500Hz"; 85 | static const prog_char str_1_0khz[] PROGMEM = "1.0kHz"; 86 | static const prog_char str_2_0khz[] PROGMEM = "2.0kHz"; 87 | static const prog_char str_5_0khz[] PROGMEM = "5.0kHz"; 88 | static const prog_char str_11v[] PROGMEM = "1V"; 89 | static const prog_char str_12v[] PROGMEM = "2V"; 90 | static const prog_char str_15v[] PROGMEM = "5V"; 91 | static const prog_char str_S1v[] PROGMEM = "+1V"; 92 | static const prog_char str_S2v[] PROGMEM = "+2V"; 93 | static const prog_char str_S5v[] PROGMEM = "+5V"; 94 | 95 | 96 | PROGMEM const prog_char* const string_table[] = { 97 | str_tempo, 98 | str_resol_, 99 | str_pulse, 100 | str_midi, 101 | str_period, 102 | str_mode, 103 | str_shape, 104 | str_range, 105 | str_freq_, 106 | str_envel_, 107 | str_off, 108 | str_on, 109 | str_note, 110 | str_gate, 111 | str_1_ppqn, 112 | str_4_ppqn, 113 | str_8_ppqn, 114 | str_24_ppqn, 115 | str_1ms, 116 | str_2ms, 117 | str_5ms, 118 | str_10ms, 119 | str_5_, 120 | str_10_, 121 | str_20_, 122 | str_50_, 123 | str_10ms, 124 | str_20ms, 125 | str_50ms, 126 | str_100ms, 127 | str_200ms, 128 | str_500ms, 129 | str_1s, 130 | str_2s, 131 | str_5s, 132 | str_10s, 133 | str_triangle, 134 | str_square, 135 | str_ramp_up, 136 | str_ramp_dn, 137 | str_sine, 138 | str_1oct_arp, 139 | str_2oct_arp, 140 | str_chrm_scl, 141 | str_c1_note, 142 | str_c3_note, 143 | str_off, 144 | str_gate, 145 | str_trigger, 146 | str_off, 147 | str_note, 148 | str_velocity, 149 | str_ptchbend, 150 | str_modwheel, 151 | str_saw, 152 | str_square, 153 | str_triangle, 154 | str_sine, 155 | str_noise, 156 | str_10hz, 157 | str_20hz, 158 | str_50hz, 159 | str_100hz, 160 | str_200hz, 161 | str_500hz, 162 | str_1_0khz, 163 | str_2_0khz, 164 | str_5_0khz, 165 | str_11v, 166 | str_12v, 167 | str_15v, 168 | str_S1v, 169 | str_S2v, 170 | str_S5v, 171 | str_off, 172 | str_note, 173 | str_gate, 174 | str_on, 175 | }; 176 | 177 | const prog_uint16_t lut_audio_midi_note[] PROGMEM = { 178 | 3, 15, 31, 43, 55, 71, 83, 95, 179 | 111, 12, 13, 14, 15, 16, 17, 18, 180 | 19, 20, 21, 22, 23, 24, 25, 26, 181 | 27, 28, 29, 30, 31, 32, 33, 34, 182 | 35, 36, 37, 38, 39, 40, 41, 42, 183 | 43, 44, 45, 46, 47, 48, 49, 50, 184 | 51, 52, 53, 54, 55, 56, 57, 58, 185 | 59, 60, 61, 62, 63, 64, 65, 66, 186 | 67, 68, 69, 70, 71, 72, 73, 74, 187 | 75, 76, 77, 78, 79, 80, 81, 82, 188 | 83, 84, 85, 86, 87, 88, 89, 90, 189 | 91, 92, 93, 94, 95, 96, 97, 98, 190 | 99, 100, 101, 102, 103, 104, 105, 106, 191 | 107, 108, 109, 110, 111, 112, 113, 114, 192 | 115, 116, 117, 118, 119, 120, 121, 122, 193 | 123, 124, 125, 126, 127, 128, 129, 130, 194 | 131, 132, 133, 134, 135, 136, 137, 138, 195 | 139, 196 | }; 197 | const prog_uint16_t lut_envelope[] PROGMEM = { 198 | 4095, 4031, 3968, 3906, 3844, 3784, 3724, 3665, 199 | 3606, 3548, 3491, 3435, 3379, 3324, 3270, 3216, 200 | 3163, 3110, 3059, 3008, 2957, 2907, 2858, 2810, 201 | 2762, 2714, 2668, 2622, 2576, 2531, 2487, 2443, 202 | 2400, 2357, 2315, 2274, 2233, 2193, 2153, 2114, 203 | 2075, 2037, 1999, 1962, 1925, 1889, 1854, 1819, 204 | 1784, 1750, 1716, 1683, 1651, 1619, 1587, 1556, 205 | 1525, 1495, 1465, 1436, 1407, 1378, 1350, 1322, 206 | 1295, 1268, 1242, 1216, 1191, 1165, 1141, 1116, 207 | 1092, 1069, 1046, 1023, 1000, 978, 957, 935, 208 | 914, 894, 873, 854, 834, 815, 796, 777, 209 | 759, 741, 723, 706, 689, 673, 656, 640, 210 | 624, 609, 594, 579, 564, 550, 536, 522, 211 | 508, 495, 482, 469, 457, 445, 433, 421, 212 | 409, 398, 387, 376, 366, 355, 345, 335, 213 | 326, 316, 307, 298, 289, 280, 272, 264, 214 | 255, 248, 240, 232, 225, 218, 211, 204, 215 | 197, 191, 184, 178, 172, 166, 161, 155, 216 | 150, 144, 139, 134, 129, 124, 120, 115, 217 | 111, 107, 103, 99, 95, 91, 87, 84, 218 | 80, 77, 74, 71, 68, 65, 62, 59, 219 | 57, 54, 52, 49, 47, 45, 43, 41, 220 | 39, 37, 35, 33, 31, 30, 28, 27, 221 | 25, 24, 22, 21, 20, 19, 18, 17, 222 | 15, 15, 14, 13, 12, 11, 10, 10, 223 | 9, 8, 8, 7, 6, 6, 5, 5, 224 | 5, 4, 4, 3, 3, 3, 2, 2, 225 | 2, 2, 1, 1, 1, 1, 1, 1, 226 | 0, 0, 0, 0, 0, 0, 0, 0, 227 | 0, 0, 0, 0, 0, 0, 0, 0, 228 | 0, 0, 0, 0, 0, 0, 0, 0, 229 | 0, 0, 0, 0, 0, 0, 0, 0, 230 | 0, 231 | }; 232 | const prog_uint16_t lut_pulse_duration[] PROGMEM = { 233 | 9, 19, 49, 98, 0, 0, 0, 0, 234 | }; 235 | const prog_uint16_t lut_pulse_ratio[] PROGMEM = { 236 | 0, 0, 0, 0, 3276, 6553, 13107, 32768, 237 | }; 238 | const prog_uint16_t lut_clock_prescaler[] PROGMEM = { 239 | 24, 6, 3, 1, 240 | }; 241 | const prog_uint16_t lut_sine[] PROGMEM = { 242 | 32767, 33571, 34374, 35177, 35978, 36778, 37574, 38368, 243 | 39159, 39946, 40728, 41506, 42278, 43045, 43805, 44559, 244 | 45306, 46045, 46776, 47499, 48213, 48917, 49612, 50297, 245 | 50971, 51634, 52286, 52926, 53554, 54169, 54771, 55361, 246 | 55936, 56498, 57045, 57578, 58096, 58598, 59085, 59556, 247 | 60011, 60450, 60872, 61277, 61664, 62035, 62388, 62722, 248 | 63039, 63338, 63618, 63880, 64123, 64347, 64552, 64737, 249 | 64904, 65051, 65179, 65287, 65376, 65445, 65494, 65524, 250 | 65534, 65524, 65494, 65445, 65376, 65287, 65179, 65051, 251 | 64904, 64737, 64552, 64347, 64123, 63880, 63618, 63338, 252 | 63039, 62722, 62388, 62035, 61664, 61277, 60872, 60450, 253 | 60011, 59556, 59085, 58598, 58096, 57578, 57045, 56498, 254 | 55936, 55361, 54771, 54169, 53554, 52926, 52286, 51634, 255 | 50971, 50297, 49612, 48917, 48213, 47499, 46776, 46045, 256 | 45306, 44559, 43805, 43045, 42278, 41506, 40728, 39946, 257 | 39159, 38368, 37574, 36778, 35978, 35177, 34374, 33571, 258 | 32767, 31962, 31159, 30356, 29555, 28755, 27959, 27165, 259 | 26374, 25587, 24805, 24027, 23255, 22488, 21728, 20974, 260 | 20227, 19488, 18757, 18034, 17320, 16616, 15921, 15236, 261 | 14562, 13899, 13247, 12607, 11979, 11364, 10762, 10172, 262 | 9597, 9035, 8488, 7955, 7437, 6935, 6448, 5977, 263 | 5522, 5083, 4661, 4256, 3869, 3498, 3145, 2811, 264 | 2494, 2195, 1915, 1653, 1410, 1186, 981, 796, 265 | 629, 482, 354, 246, 157, 88, 39, 9, 266 | 0, 9, 39, 88, 157, 246, 354, 482, 267 | 629, 796, 981, 1186, 1410, 1653, 1915, 2195, 268 | 2494, 2811, 3145, 3498, 3869, 4256, 4661, 5083, 269 | 5522, 5977, 6448, 6935, 7437, 7955, 8488, 9035, 270 | 9597, 10172, 10762, 11364, 11979, 12607, 13247, 13899, 271 | 14562, 15236, 15921, 16616, 17320, 18034, 18757, 19488, 272 | 20227, 20974, 21728, 22488, 23255, 24027, 24805, 25587, 273 | 26374, 27165, 27959, 28755, 29555, 30356, 31159, 31962, 274 | 32766, 275 | }; 276 | 277 | 278 | PROGMEM const prog_uint16_t* const lookup_table_table[] = { 279 | lut_audio_midi_note, 280 | lut_envelope, 281 | lut_pulse_duration, 282 | lut_pulse_ratio, 283 | lut_clock_prescaler, 284 | lut_sine, 285 | }; 286 | 287 | const prog_uint32_t lut_tempo_phase_increment[] PROGMEM = { 288 | 2336462, 2394873, 2453285, 2511696, 2570108, 2628519, 2686931, 2745343, 289 | 2803754, 2862166, 2920577, 2978989, 3037400, 3095812, 3154223, 3212635, 290 | 3271047, 3329458, 3387870, 3446281, 3504693, 3563104, 3621516, 3679927, 291 | 3738339, 3796751, 3855162, 3913574, 3971985, 4030397, 4088808, 4147220, 292 | 4205631, 4264043, 4322455, 4380866, 4439278, 4497689, 4556101, 4614512, 293 | 4672924, 4731335, 4789747, 4848159, 4906570, 4964982, 5023393, 5081805, 294 | 5140216, 5198628, 5257039, 5315451, 5373863, 5432274, 5490686, 5549097, 295 | 5607509, 5665920, 5724332, 5782743, 5841155, 5899567, 5957978, 6016390, 296 | 6074801, 6133213, 6191624, 6250036, 6308447, 6366859, 6425271, 6483682, 297 | 6542094, 6600505, 6658917, 6717328, 6775740, 6834151, 6892563, 6950975, 298 | 7009386, 7067798, 7126209, 7184621, 7243032, 7301444, 7359855, 7418267, 299 | 7476679, 7535090, 7593502, 7651913, 7710325, 7768736, 7827148, 7885559, 300 | 7943971, 8002383, 8060794, 8119206, 8177617, 8236029, 8294440, 8352852, 301 | 8411263, 8469675, 8528087, 8586498, 8644910, 8703321, 8761733, 8820144, 302 | 8878556, 8936967, 8995379, 9053791, 9112202, 9170614, 9229025, 9287437, 303 | 9345848, 9404260, 9462671, 9521083, 9579495, 9637906, 9696318, 9754729, 304 | 9813141, 9871552, 9929964, 9988375, 10046787, 10105199, 10163610, 10222022, 305 | 10280433, 10338845, 10397256, 10455668, 10514079, 10572491, 10630903, 10689314, 306 | 10747726, 10806137, 10864549, 10922960, 10981372, 11039783, 11098195, 11156607, 307 | 11215018, 11273430, 11331841, 11390253, 11448664, 11507076, 11565487, 11623899, 308 | 11682311, 11740722, 11799134, 11857545, 11915957, 11974368, 12032780, 12091191, 309 | 12149603, 12208015, 12266426, 12324838, 12383249, 12441661, 12500072, 12558484, 310 | 12616895, 12675307, 12733719, 12792130, 12850542, 12908953, 12967365, 13025776, 311 | 13084188, 13142599, 13201011, 13259423, 13317834, 13376246, 13434657, 13493069, 312 | 13551480, 13609892, 13668303, 13726715, 13785127, 13843538, 13901950, 13960361, 313 | 14018773, 314 | }; 315 | const prog_uint32_t lut_gate_phase_increment[] PROGMEM = { 316 | 43808666, 21904333, 8761733, 4380866, 2190433, 876173, 438086, 219043, 317 | 87617, 43808, 318 | }; 319 | const prog_uint32_t lut_cv_phase_increment[] PROGMEM = { 320 | 43808666, 21904333, 8761733, 4380866, 2190433, 876173, 438086, 219043, 321 | 87617, 43808, 21904, 322 | }; 323 | const prog_uint32_t lut_audio_phase_increment[] PROGMEM = { 324 | 1095216, 2190433, 5476083, 10952166, 21904333, 54760833, 109521666, 219043332, 325 | 547608330, 1790854, 1897343, 2010165, 2129696, 2256334, 2390503, 2532650, 326 | 2683249, 2842803, 3011845, 3190939, 3380682, 3581708, 3794687, 4020331, 327 | 4259393, 4512669, 4781007, 5065300, 5366499, 5685607, 6023691, 6381878, 328 | 6761365, 7163416, 7589375, 8040663, 8518786, 9025339, 9562014, 10130601, 329 | 10732998, 11371215, 12047383, 12763757, 13522730, 14326833, 15178751, 16081327, 330 | 17037572, 18050679, 19124028, 20261202, 21465996, 22742431, 24094766, 25527515, 331 | 27045460, 28653667, 30357503, 32162654, 34075145, 36101359, 38248057, 40522405, 332 | 42931993, 45484862, 48189533, 51055031, 54090921, 57307335, 60715007, 64325309, 333 | 68150291, 72202718, 76496115, 81044811, 85863986, 90969724, 96379066, 102110063, 334 | 108181843, 114614671, 121430014, 128650618, 136300582, 144405436, 152992230, 162089622, 335 | 171727972, 181939449, 192758132, 204220127, 216363687, 229229342, 242860028, 257301237, 336 | 272601164, 288810873, 305984461, 324179244, 343455945, 363878899, 385516264, 408440254, 337 | 432727375, 458458684, 485720056, 514602474, 545202329, 577621747, 611968923, 648358489, 338 | 686911891, 727757798, 771032528, 816880509, 865454751, 916917369, 971440113, 1029204948, 339 | 1090404659, 1155243494, 1223937847, 1296716979, 1373823783, 1455515596, 1542065057, 1633761018, 340 | 1730909503, 1833834738, 1942880226, 2058409897, 2180809319, 2310486989, 2447875695, 2593433958, 341 | 2747647566, 342 | }; 343 | 344 | 345 | PROGMEM const prog_uint32_t* const lookup_table_32_table[] = { 346 | lut_tempo_phase_increment, 347 | lut_gate_phase_increment, 348 | lut_cv_phase_increment, 349 | lut_audio_phase_increment, 350 | }; 351 | 352 | const prog_uint8_t chr_special_characters[] PROGMEM = { 353 | 4, 4, 31, 4, 4, 0, 31, 0, 354 | 0, 0, 4, 4, 14, 31, 31, 0, 355 | 0, 0, 8, 21, 2, 0, 0, 0, 356 | 1, 1, 1, 3, 3, 0, 18, 12, 357 | 0, 25, 27, 6, 2, 4, 4, 8, 358 | 0, 16, 8, 4, 2, 1, 0, 0, 359 | 4, 0, 4, 0, 4, 0, 4, 0, 360 | }; 361 | 362 | 363 | const prog_uint8_t* const character_table[] = { 364 | chr_special_characters, 365 | }; 366 | 367 | const prog_uint8_t wav_sine[] PROGMEM = { 368 | 2, 2, 2, 3, 2, 3, 3, 4, 369 | 5, 4, 7, 5, 9, 7, 10, 11, 370 | 11, 13, 13, 17, 16, 18, 21, 21, 371 | 23, 25, 27, 28, 32, 31, 36, 36, 372 | 39, 41, 43, 46, 48, 51, 53, 55, 373 | 57, 62, 63, 65, 70, 70, 75, 76, 374 | 81, 82, 85, 89, 92, 94, 97, 100, 375 | 104, 107, 109, 112, 116, 119, 122, 124, 376 | 129, 130, 135, 137, 140, 144, 147, 148, 377 | 154, 155, 158, 163, 163, 169, 169, 174, 378 | 177, 178, 182, 185, 187, 191, 192, 195, 379 | 199, 200, 203, 205, 209, 210, 212, 216, 380 | 216, 220, 221, 223, 226, 227, 230, 230, 381 | 233, 235, 235, 239, 238, 241, 242, 243, 382 | 245, 245, 246, 249, 247, 251, 249, 252, 383 | 251, 252, 253, 253, 253, 254, 254, 254, 384 | 253, 255, 254, 253, 253, 254, 252, 253, 385 | 251, 251, 250, 250, 248, 248, 246, 246, 386 | 245, 242, 243, 240, 239, 238, 236, 234, 387 | 233, 231, 230, 226, 226, 224, 221, 219, 388 | 217, 215, 212, 211, 208, 206, 202, 201, 389 | 198, 196, 192, 190, 188, 184, 182, 179, 390 | 177, 173, 170, 168, 164, 162, 158, 156, 391 | 153, 149, 147, 143, 141, 136, 135, 131, 392 | 128, 125, 122, 118, 116, 113, 109, 106, 393 | 104, 101, 96, 95, 92, 88, 85, 83, 394 | 80, 77, 74, 71, 69, 66, 63, 61, 395 | 57, 56, 53, 50, 49, 45, 43, 42, 396 | 38, 37, 35, 32, 31, 29, 26, 26, 397 | 22, 22, 20, 19, 16, 16, 13, 14, 398 | 11, 10, 10, 8, 8, 6, 6, 5, 399 | 4, 5, 2, 4, 2, 2, 2, 3, 400 | 1, 401 | }; 402 | const prog_uint8_t wav_bandlimited_square_0[] PROGMEM = { 403 | 6, 5, 6, 6, 5, 6, 6, 5, 404 | 6, 6, 6, 5, 6, 5, 6, 6, 405 | 6, 5, 5, 7, 5, 6, 6, 5, 406 | 6, 5, 7, 5, 5, 7, 4, 7, 407 | 5, 6, 6, 5, 6, 6, 6, 5, 408 | 5, 7, 5, 5, 7, 5, 6, 5, 409 | 7, 4, 7, 5, 6, 5, 7, 4, 410 | 7, 5, 6, 5, 7, 6, 2, 11, 411 | 245, 254, 250, 248, 252, 250, 250, 251, 412 | 250, 250, 250, 251, 249, 251, 251, 250, 413 | 249, 252, 249, 251, 250, 250, 251, 250, 414 | 250, 250, 251, 250, 250, 251, 250, 250, 415 | 250, 251, 250, 250, 250, 251, 250, 250, 416 | 251, 250, 250, 250, 251, 250, 250, 250, 417 | 251, 250, 250, 251, 250, 250, 250, 251, 418 | 250, 250, 250, 251, 250, 251, 249, 251, 419 | 250, 251, 249, 251, 251, 249, 251, 250, 420 | 250, 251, 250, 250, 250, 251, 250, 250, 421 | 251, 250, 250, 250, 251, 250, 250, 250, 422 | 251, 250, 250, 251, 250, 250, 250, 251, 423 | 250, 250, 251, 249, 251, 251, 249, 251, 424 | 250, 251, 249, 251, 251, 249, 251, 250, 425 | 250, 251, 250, 250, 250, 251, 250, 250, 426 | 251, 249, 251, 252, 247, 252, 253, 243, 427 | 13, 2, 6, 7, 5, 5, 6, 6, 428 | 5, 7, 5, 5, 6, 7, 4, 6, 429 | 6, 6, 5, 6, 6, 5, 6, 6, 430 | 5, 6, 6, 5, 6, 6, 6, 5, 431 | 5, 7, 5, 6, 5, 6, 6, 6, 432 | 5, 6, 5, 6, 6, 6, 5, 6, 433 | 6, 5, 6, 6, 5, 6, 5, 7, 434 | 5, 5, 7, 5, 5, 7, 5, 6, 435 | 5, 436 | }; 437 | const prog_uint8_t wav_bandlimited_square_1[] PROGMEM = { 438 | 10, 11, 10, 10, 10, 11, 10, 10, 439 | 10, 11, 11, 9, 10, 11, 10, 11, 440 | 10, 10, 10, 11, 9, 11, 11, 10, 441 | 9, 12, 9, 11, 10, 11, 9, 11, 442 | 10, 10, 11, 10, 11, 9, 11, 10, 443 | 10, 11, 10, 10, 11, 10, 9, 12, 444 | 10, 9, 11, 12, 8, 10, 13, 8, 445 | 10, 12, 10, 7, 15, 10, 2, 25, 446 | 232, 253, 245, 242, 248, 247, 243, 247, 447 | 247, 243, 247, 246, 245, 245, 247, 245, 448 | 245, 246, 247, 244, 246, 246, 245, 246, 449 | 246, 245, 246, 246, 245, 246, 246, 245, 450 | 245, 247, 245, 245, 247, 245, 245, 247, 451 | 245, 245, 246, 246, 245, 246, 246, 246, 452 | 244, 247, 245, 246, 246, 245, 246, 245, 453 | 246, 246, 245, 246, 246, 245, 246, 245, 454 | 246, 246, 246, 245, 245, 247, 245, 245, 455 | 247, 244, 247, 245, 246, 245, 246, 246, 456 | 245, 246, 246, 245, 246, 246, 245, 245, 457 | 247, 245, 245, 247, 245, 245, 247, 245, 458 | 245, 246, 246, 245, 246, 246, 245, 246, 459 | 246, 245, 246, 246, 245, 245, 247, 245, 460 | 245, 247, 245, 245, 247, 245, 245, 246, 461 | 247, 243, 247, 247, 243, 245, 254, 231, 462 | 25, 2, 10, 15, 7, 10, 12, 9, 463 | 10, 12, 9, 10, 11, 11, 9, 10, 464 | 12, 9, 10, 11, 10, 10, 11, 10, 465 | 10, 11, 9, 11, 11, 9, 11, 10, 466 | 11, 9, 11, 11, 9, 11, 10, 10, 467 | 11, 10, 10, 10, 11, 10, 10, 11, 468 | 10, 10, 10, 11, 10, 10, 11, 10, 469 | 9, 12, 10, 9, 12, 9, 11, 10, 470 | 10, 471 | }; 472 | const prog_uint8_t wav_bandlimited_square_2[] PROGMEM = { 473 | 8, 7, 8, 7, 8, 7, 8, 8, 474 | 7, 8, 8, 6, 9, 7, 8, 7, 475 | 8, 7, 8, 8, 7, 8, 7, 8, 476 | 7, 9, 6, 9, 6, 9, 6, 9, 477 | 7, 8, 7, 8, 7, 9, 6, 8, 478 | 8, 7, 8, 8, 7, 7, 9, 6, 479 | 10, 4, 11, 5, 10, 5, 11, 3, 480 | 12, 3, 13, 2, 13, 2, 14, 1, 481 | 254, 243, 254, 243, 254, 242, 254, 244, 482 | 252, 245, 252, 245, 251, 246, 250, 247, 483 | 250, 247, 249, 248, 249, 248, 248, 249, 484 | 247, 250, 248, 248, 248, 249, 248, 249, 485 | 247, 250, 247, 250, 246, 251, 246, 250, 486 | 247, 250, 247, 249, 248, 248, 249, 248, 487 | 249, 248, 248, 249, 248, 248, 249, 248, 488 | 249, 247, 250, 247, 249, 248, 249, 247, 489 | 250, 247, 249, 249, 247, 250, 247, 249, 490 | 248, 249, 247, 250, 247, 249, 248, 249, 491 | 248, 248, 249, 248, 248, 249, 248, 249, 492 | 248, 248, 249, 247, 250, 247, 249, 248, 493 | 249, 248, 249, 247, 250, 247, 249, 248, 494 | 249, 247, 250, 248, 247, 250, 247, 250, 495 | 246, 251, 246, 251, 245, 252, 245, 252, 496 | 244, 253, 243, 254, 243, 254, 243, 253, 497 | 3, 12, 3, 13, 2, 13, 2, 13, 498 | 3, 11, 5, 10, 5, 10, 5, 10, 499 | 6, 9, 6, 9, 7, 7, 8, 8, 500 | 7, 8, 8, 6, 9, 7, 8, 7, 501 | 8, 7, 9, 6, 9, 6, 9, 6, 502 | 9, 7, 8, 7, 8, 7, 8, 8, 503 | 7, 8, 7, 8, 7, 9, 6, 9, 504 | 6, 9, 6, 9, 7, 8, 7, 8, 505 | 7, 506 | }; 507 | const prog_uint8_t wav_bandlimited_square_3[] PROGMEM = { 508 | 23, 22, 21, 22, 23, 23, 21, 22, 509 | 21, 24, 23, 21, 22, 21, 24, 23, 510 | 21, 21, 22, 23, 24, 21, 21, 21, 511 | 25, 23, 20, 22, 21, 25, 23, 20, 512 | 21, 22, 25, 23, 19, 22, 22, 25, 513 | 23, 19, 21, 23, 26, 21, 20, 20, 514 | 24, 26, 22, 18, 19, 26, 28, 21, 515 | 14, 20, 29, 33, 15, 3, 20, 84, 516 | 172, 235, 254, 240, 224, 225, 237, 241, 517 | 235, 229, 229, 236, 239, 233, 231, 231, 518 | 235, 238, 232, 232, 232, 235, 236, 234, 519 | 231, 232, 236, 235, 234, 231, 233, 235, 520 | 235, 234, 231, 234, 234, 236, 232, 233, 521 | 233, 235, 235, 232, 233, 233, 236, 234, 522 | 232, 233, 234, 235, 234, 233, 232, 234, 523 | 235, 235, 231, 234, 233, 236, 233, 233, 524 | 233, 234, 235, 233, 234, 232, 234, 235, 525 | 234, 232, 233, 235, 234, 235, 231, 233, 526 | 235, 235, 234, 231, 234, 234, 235, 234, 527 | 232, 232, 236, 235, 233, 231, 234, 235, 528 | 235, 233, 232, 233, 236, 234, 234, 230, 529 | 234, 236, 236, 231, 232, 233, 236, 237, 530 | 231, 229, 235, 238, 236, 229, 229, 235, 531 | 242, 236, 225, 224, 241, 253, 236, 170, 532 | 85, 21, 2, 16, 31, 31, 19, 14, 533 | 22, 27, 25, 21, 17, 22, 26, 25, 534 | 19, 20, 22, 25, 23, 21, 20, 22, 535 | 25, 23, 20, 21, 22, 25, 22, 21, 536 | 21, 22, 25, 22, 21, 20, 24, 23, 537 | 23, 20, 21, 24, 23, 22, 22, 20, 538 | 23, 24, 22, 21, 21, 24, 23, 21, 539 | 22, 21, 23, 24, 21, 22, 21, 23, 540 | 23, 541 | }; 542 | const prog_uint8_t wav_bandlimited_square_4[] PROGMEM = { 543 | 18, 19, 20, 22, 24, 24, 24, 22, 544 | 22, 20, 18, 18, 18, 20, 20, 23, 545 | 24, 25, 23, 23, 21, 18, 18, 18, 546 | 18, 19, 22, 24, 25, 25, 24, 22, 547 | 19, 18, 17, 16, 18, 20, 24, 25, 548 | 27, 26, 24, 22, 17, 14, 15, 13, 549 | 19, 22, 27, 30, 32, 30, 24, 16, 550 | 9, 4, 1, 8, 21, 46, 73, 111, 551 | 145, 182, 212, 233, 248, 255, 252, 247, 552 | 240, 231, 226, 225, 225, 229, 233, 239, 553 | 241, 242, 241, 238, 235, 232, 230, 229, 554 | 230, 232, 237, 237, 239, 240, 238, 236, 555 | 234, 232, 231, 231, 231, 235, 236, 238, 556 | 238, 239, 236, 236, 233, 232, 231, 232, 557 | 233, 235, 237, 237, 239, 237, 236, 235, 558 | 232, 233, 231, 232, 235, 235, 236, 239, 559 | 238, 236, 236, 234, 233, 231, 232, 233, 560 | 234, 237, 237, 238, 237, 238, 234, 233, 561 | 233, 230, 233, 233, 235, 237, 238, 239, 562 | 237, 237, 233, 233, 231, 230, 233, 233, 563 | 236, 239, 239, 240, 237, 236, 233, 229, 564 | 230, 230, 231, 235, 238, 242, 242, 240, 565 | 239, 234, 229, 225, 224, 226, 232, 239, 566 | 247, 253, 254, 249, 233, 211, 182, 146, 567 | 110, 74, 45, 22, 7, 2, 3, 9, 568 | 17, 24, 29, 33, 29, 28, 22, 17, 569 | 16, 12, 16, 17, 21, 25, 25, 28, 570 | 24, 24, 21, 17, 17, 17, 17, 19, 571 | 23, 24, 24, 26, 23, 22, 20, 18, 572 | 17, 18, 19, 21, 22, 24, 24, 24, 573 | 24, 20, 19, 19, 17, 19, 19, 22, 574 | 23, 23, 25, 23, 23, 19, 20, 17, 575 | 19, 576 | }; 577 | const prog_uint8_t wav_bandlimited_square_5[] PROGMEM = { 578 | 30, 29, 28, 29, 26, 25, 24, 22, 579 | 20, 18, 18, 15, 15, 13, 13, 12, 580 | 12, 13, 14, 14, 15, 18, 20, 20, 581 | 23, 26, 27, 28, 30, 33, 31, 33, 582 | 34, 31, 32, 30, 28, 26, 23, 21, 583 | 17, 14, 12, 9, 5, 5, 3, 1, 584 | 1, 4, 4, 8, 11, 17, 22, 32, 585 | 39, 48, 59, 71, 82, 95, 108, 121, 586 | 135, 148, 160, 174, 185, 197, 207, 217, 587 | 225, 232, 240, 243, 249, 251, 254, 253, 588 | 254, 254, 251, 250, 247, 244, 242, 238, 589 | 235, 233, 230, 227, 226, 225, 222, 224, 590 | 223, 223, 224, 226, 227, 229, 230, 233, 591 | 234, 238, 237, 241, 241, 242, 243, 244, 592 | 243, 243, 243, 241, 240, 239, 237, 236, 593 | 233, 233, 230, 229, 229, 226, 227, 226, 594 | 226, 226, 228, 227, 230, 230, 233, 233, 595 | 236, 237, 238, 241, 241, 243, 243, 243, 596 | 243, 244, 242, 241, 240, 239, 236, 235, 597 | 233, 230, 229, 227, 226, 223, 224, 223, 598 | 223, 224, 224, 225, 228, 230, 233, 235, 599 | 238, 241, 245, 247, 250, 251, 254, 253, 600 | 255, 253, 250, 250, 243, 239, 233, 225, 601 | 217, 207, 197, 185, 174, 160, 148, 134, 602 | 122, 108, 94, 84, 69, 60, 48, 39, 603 | 31, 23, 17, 11, 8, 4, 3, 2, 604 | 2, 2, 4, 6, 9, 12, 13, 18, 605 | 21, 23, 26, 28, 30, 31, 33, 32, 606 | 34, 31, 33, 29, 29, 28, 24, 24, 607 | 20, 20, 17, 16, 14, 14, 13, 11, 608 | 14, 11, 15, 13, 16, 18, 17, 21, 609 | 22, 24, 25, 26, 28, 29, 30, 28, 610 | 31, 611 | }; 612 | const prog_uint8_t wav_bandlimited_saw_0[] PROGMEM = { 613 | 66, 68, 68, 68, 71, 72, 71, 73, 614 | 74, 75, 76, 77, 78, 79, 79, 82, 615 | 81, 83, 83, 85, 86, 86, 88, 88, 616 | 90, 90, 92, 92, 94, 94, 95, 96, 617 | 98, 98, 99, 100, 101, 103, 102, 105, 618 | 104, 106, 108, 107, 109, 110, 110, 113, 619 | 112, 114, 114, 116, 117, 118, 117, 121, 620 | 120, 121, 123, 123, 124, 126, 126, 127, 621 | 129, 129, 129, 132, 133, 132, 134, 135, 622 | 136, 137, 138, 139, 140, 141, 141, 143, 623 | 144, 144, 146, 147, 147, 149, 150, 150, 624 | 151, 153, 153, 154, 156, 156, 158, 158, 625 | 159, 160, 161, 162, 164, 163, 166, 165, 626 | 168, 167, 169, 170, 171, 172, 172, 175, 627 | 173, 177, 176, 178, 179, 178, 182, 181, 628 | 182, 184, 184, 186, 186, 187, 188, 190, 629 | 189, 193, 191, 193, 194, 195, 197, 196, 630 | 198, 199, 200, 201, 202, 202, 204, 205, 631 | 206, 206, 208, 208, 210, 211, 211, 212, 632 | 214, 215, 214, 217, 218, 217, 220, 220, 633 | 221, 223, 222, 225, 224, 227, 227, 227, 634 | 230, 229, 231, 233, 231, 235, 234, 236, 635 | 237, 238, 238, 239, 241, 242, 242, 243, 636 | 246, 244, 246, 249, 247, 248, 255, 244, 637 | 12, 0, 7, 9, 7, 9, 10, 12, 638 | 11, 14, 13, 15, 16, 18, 16, 20, 639 | 20, 20, 22, 23, 23, 25, 25, 27, 640 | 28, 28, 29, 31, 32, 31, 34, 35, 641 | 34, 37, 37, 38, 40, 39, 42, 41, 642 | 44, 43, 46, 45, 48, 47, 49, 50, 643 | 50, 53, 52, 53, 56, 55, 56, 58, 644 | 59, 59, 60, 62, 62, 64, 64, 65, 645 | 66, 646 | }; 647 | const prog_uint8_t wav_bandlimited_saw_1[] PROGMEM = { 648 | 69, 68, 72, 71, 72, 74, 74, 75, 649 | 76, 77, 78, 78, 81, 81, 80, 84, 650 | 83, 85, 85, 86, 87, 89, 89, 89, 651 | 92, 92, 92, 94, 95, 96, 96, 97, 652 | 99, 100, 99, 102, 102, 103, 104, 105, 653 | 105, 108, 107, 109, 110, 109, 113, 112, 654 | 113, 114, 116, 115, 118, 117, 120, 119, 655 | 121, 122, 122, 124, 124, 126, 126, 127, 656 | 129, 129, 129, 132, 132, 132, 134, 135, 657 | 136, 136, 138, 138, 139, 141, 141, 142, 658 | 144, 143, 145, 146, 147, 148, 148, 150, 659 | 150, 152, 153, 152, 155, 155, 156, 158, 660 | 157, 159, 160, 161, 161, 164, 162, 165, 661 | 166, 166, 167, 169, 168, 171, 171, 172, 662 | 172, 174, 176, 174, 177, 178, 179, 178, 663 | 182, 180, 182, 184, 184, 184, 187, 186, 664 | 188, 189, 190, 189, 193, 192, 193, 195, 665 | 194, 197, 197, 198, 199, 200, 201, 201, 666 | 203, 203, 206, 204, 207, 207, 209, 208, 667 | 211, 211, 212, 213, 213, 215, 216, 217, 668 | 217, 219, 219, 220, 222, 222, 222, 225, 669 | 226, 225, 226, 229, 229, 228, 232, 232, 670 | 231, 234, 234, 235, 237, 238, 235, 241, 671 | 241, 238, 243, 245, 240, 244, 254, 232, 672 | 23, 2, 12, 14, 11, 14, 16, 14, 673 | 16, 19, 18, 18, 22, 21, 21, 23, 674 | 25, 24, 26, 27, 28, 28, 30, 30, 675 | 31, 33, 33, 34, 36, 35, 37, 39, 676 | 38, 40, 40, 43, 41, 45, 44, 45, 677 | 46, 48, 47, 50, 50, 51, 51, 54, 678 | 53, 55, 55, 57, 57, 59, 59, 61, 679 | 60, 63, 62, 65, 65, 65, 67, 68, 680 | 68, 681 | }; 682 | const prog_uint8_t wav_bandlimited_saw_2[] PROGMEM = { 683 | 67, 69, 69, 70, 72, 72, 73, 74, 684 | 75, 76, 77, 78, 79, 80, 81, 80, 685 | 85, 82, 85, 85, 88, 86, 89, 88, 686 | 92, 90, 93, 93, 93, 96, 96, 96, 687 | 99, 98, 100, 101, 101, 103, 103, 105, 688 | 105, 107, 107, 109, 108, 111, 111, 112, 689 | 112, 115, 115, 116, 117, 117, 119, 120, 690 | 120, 122, 122, 124, 125, 124, 127, 127, 691 | 129, 128, 131, 131, 132, 132, 135, 135, 692 | 135, 137, 138, 138, 140, 141, 141, 142, 693 | 144, 144, 146, 146, 146, 150, 148, 150, 694 | 151, 153, 151, 155, 155, 155, 157, 158, 695 | 158, 160, 161, 160, 163, 164, 164, 164, 696 | 168, 166, 168, 169, 171, 170, 172, 173, 697 | 173, 176, 175, 176, 179, 177, 180, 181, 698 | 181, 182, 184, 183, 186, 186, 187, 188, 699 | 189, 190, 190, 193, 192, 194, 195, 195, 700 | 196, 199, 197, 201, 199, 202, 201, 204, 701 | 205, 204, 206, 207, 209, 208, 210, 210, 702 | 213, 212, 214, 214, 216, 216, 218, 218, 703 | 220, 219, 222, 222, 224, 223, 225, 226, 704 | 227, 229, 227, 231, 230, 232, 232, 235, 705 | 233, 237, 234, 240, 236, 242, 237, 244, 706 | 239, 247, 240, 249, 241, 252, 243, 253, 707 | 3, 12, 3, 15, 6, 16, 8, 18, 708 | 9, 20, 13, 20, 15, 22, 18, 22, 709 | 21, 24, 23, 25, 26, 26, 28, 29, 710 | 29, 30, 32, 32, 34, 34, 35, 36, 711 | 38, 37, 40, 39, 41, 42, 43, 43, 712 | 46, 45, 46, 48, 49, 49, 50, 52, 713 | 52, 54, 53, 56, 55, 58, 58, 59, 714 | 59, 61, 62, 63, 63, 66, 64, 68, 715 | 66, 716 | }; 717 | const prog_uint8_t wav_bandlimited_saw_3[] PROGMEM = { 718 | 74, 75, 75, 77, 79, 78, 79, 79, 719 | 82, 83, 82, 83, 84, 86, 86, 87, 720 | 87, 88, 91, 90, 91, 91, 93, 95, 721 | 94, 95, 95, 98, 98, 99, 99, 100, 722 | 101, 103, 103, 104, 103, 106, 106, 109, 723 | 106, 109, 109, 112, 111, 112, 112, 114, 724 | 116, 115, 116, 116, 120, 118, 120, 120, 725 | 122, 122, 123, 125, 123, 126, 127, 128, 726 | 127, 129, 129, 132, 132, 132, 132, 134, 727 | 136, 136, 136, 136, 140, 139, 139, 142, 728 | 140, 143, 144, 144, 144, 146, 147, 148, 729 | 148, 149, 149, 152, 152, 152, 153, 154, 730 | 156, 157, 155, 158, 157, 161, 161, 160, 731 | 160, 164, 164, 165, 164, 165, 168, 168, 732 | 168, 170, 169, 171, 173, 173, 173, 174, 733 | 175, 177, 178, 176, 178, 180, 182, 180, 734 | 182, 182, 184, 186, 185, 185, 187, 188, 735 | 190, 190, 188, 192, 192, 195, 192, 194, 736 | 196, 197, 197, 198, 198, 199, 202, 202, 737 | 202, 201, 204, 207, 205, 206, 205, 210, 738 | 210, 210, 209, 210, 214, 215, 213, 213, 739 | 215, 218, 219, 218, 216, 220, 222, 225, 740 | 220, 220, 224, 228, 230, 222, 223, 229, 741 | 236, 233, 224, 221, 239, 253, 237, 171, 742 | 84, 20, 0, 18, 34, 32, 22, 19, 743 | 26, 34, 32, 27, 26, 31, 37, 34, 744 | 32, 32, 35, 41, 37, 36, 38, 39, 745 | 44, 42, 40, 42, 44, 48, 45, 45, 746 | 46, 50, 50, 49, 50, 51, 53, 55, 747 | 53, 54, 55, 58, 59, 57, 58, 60, 748 | 61, 64, 61, 62, 64, 67, 66, 65, 749 | 68, 68, 70, 71, 69, 72, 73, 74, 750 | 74, 751 | }; 752 | const prog_uint8_t wav_bandlimited_saw_4[] PROGMEM = { 753 | 70, 72, 72, 73, 77, 78, 79, 81, 754 | 81, 82, 80, 82, 81, 81, 84, 84, 755 | 87, 87, 91, 91, 92, 90, 93, 91, 756 | 91, 93, 93, 96, 97, 98, 101, 101, 757 | 102, 101, 103, 101, 102, 104, 103, 107, 758 | 107, 109, 110, 113, 111, 113, 112, 112, 759 | 113, 113, 115, 116, 118, 120, 121, 122, 760 | 123, 122, 122, 124, 122, 124, 125, 128, 761 | 128, 130, 131, 133, 133, 133, 132, 134, 762 | 133, 135, 135, 137, 140, 140, 143, 142, 763 | 143, 144, 143, 143, 144, 145, 147, 147, 764 | 149, 152, 153, 152, 155, 153, 153, 155, 765 | 153, 156, 156, 159, 159, 163, 163, 164, 766 | 163, 165, 164, 163, 165, 165, 168, 168, 767 | 172, 171, 175, 174, 174, 175, 174, 174, 768 | 174, 177, 176, 181, 181, 183, 184, 186, 769 | 184, 186, 183, 185, 184, 186, 189, 189, 770 | 193, 193, 196, 196, 195, 195, 195, 193, 771 | 196, 195, 198, 202, 202, 206, 206, 207, 772 | 205, 206, 204, 203, 205, 206, 208, 212, 773 | 214, 217, 219, 217, 217, 214, 214, 212, 774 | 213, 216, 218, 223, 227, 230, 231, 231, 775 | 227, 223, 221, 217, 218, 223, 228, 239, 776 | 246, 252, 255, 248, 233, 211, 180, 146, 777 | 109, 76, 45, 21, 8, 2, 2, 9, 778 | 17, 27, 33, 38, 37, 36, 32, 28, 779 | 25, 24, 25, 29, 32, 37, 41, 42, 780 | 43, 42, 41, 38, 39, 36, 39, 41, 781 | 44, 47, 49, 52, 51, 52, 50, 49, 782 | 50, 48, 51, 52, 54, 58, 59, 61, 783 | 61, 62, 60, 60, 59, 61, 61, 63, 784 | 66, 67, 70, 71, 70, 72, 70, 71, 785 | 71, 786 | }; 787 | const prog_uint8_t wav_bandlimited_saw_5[] PROGMEM = { 788 | 73, 73, 73, 72, 72, 72, 71, 72, 789 | 72, 72, 73, 73, 75, 76, 77, 80, 790 | 81, 82, 86, 87, 89, 91, 93, 94, 791 | 95, 98, 98, 98, 99, 101, 99, 100, 792 | 100, 100, 100, 100, 100, 100, 100, 101, 793 | 102, 102, 103, 104, 107, 107, 109, 112, 794 | 112, 115, 117, 118, 121, 120, 124, 124, 795 | 125, 127, 126, 127, 127, 128, 128, 128, 796 | 127, 128, 128, 127, 129, 129, 129, 131, 797 | 130, 133, 134, 136, 136, 139, 141, 142, 798 | 145, 145, 149, 149, 151, 152, 154, 153, 799 | 155, 155, 155, 156, 155, 156, 155, 156, 800 | 155, 156, 156, 155, 157, 158, 158, 160, 801 | 161, 163, 164, 167, 168, 170, 173, 174, 802 | 177, 177, 179, 182, 181, 183, 183, 184, 803 | 183, 185, 183, 183, 184, 183, 181, 184, 804 | 181, 183, 183, 184, 184, 187, 186, 191, 805 | 190, 195, 195, 198, 202, 203, 206, 207, 806 | 210, 211, 212, 214, 213, 214, 214, 212, 807 | 213, 211, 210, 210, 207, 206, 207, 206, 808 | 206, 205, 208, 208, 212, 213, 216, 221, 809 | 223, 230, 232, 237, 242, 245, 249, 250, 810 | 254, 254, 254, 252, 249, 246, 239, 233, 811 | 223, 215, 203, 190, 179, 164, 150, 135, 812 | 120, 106, 91, 78, 64, 52, 41, 32, 813 | 23, 16, 10, 6, 3, 2, 2, 1, 814 | 5, 7, 10, 13, 20, 21, 28, 31, 815 | 35, 39, 41, 46, 46, 48, 50, 49, 816 | 50, 49, 49, 48, 46, 45, 44, 44, 817 | 42, 42, 42, 41, 42, 44, 44, 45, 818 | 49, 49, 52, 55, 57, 59, 62, 64, 819 | 66, 68, 70, 70, 72, 72, 73, 74, 820 | 72, 821 | }; 822 | const prog_uint8_t wav_bandlimited_triangle_0[] PROGMEM = { 823 | 3, 5, 6, 9, 11, 12, 15, 16, 824 | 18, 21, 23, 23, 27, 28, 30, 32, 825 | 34, 36, 38, 40, 42, 43, 46, 48, 826 | 49, 52, 53, 56, 57, 59, 62, 63, 827 | 65, 67, 69, 72, 72, 75, 77, 79, 828 | 81, 82, 85, 87, 88, 90, 94, 93, 829 | 97, 98, 101, 101, 105, 106, 108, 110, 830 | 111, 115, 116, 117, 120, 122, 123, 126, 831 | 127, 130, 132, 132, 136, 138, 138, 142, 832 | 143, 144, 148, 149, 151, 152, 155, 158, 833 | 157, 162, 162, 164, 167, 169, 170, 172, 834 | 174, 177, 178, 180, 182, 184, 186, 188, 835 | 190, 192, 193, 196, 198, 200, 201, 203, 836 | 206, 207, 210, 211, 213, 216, 217, 218, 837 | 222, 223, 224, 228, 228, 232, 231, 236, 838 | 236, 239, 240, 243, 244, 247, 248, 250, 839 | 254, 253, 249, 249, 246, 244, 242, 241, 840 | 238, 236, 235, 233, 230, 228, 227, 225, 841 | 222, 221, 218, 218, 214, 213, 211, 209, 842 | 207, 205, 203, 201, 199, 197, 195, 194, 843 | 191, 189, 187, 186, 183, 182, 179, 178, 844 | 175, 173, 173, 169, 168, 165, 164, 162, 845 | 160, 158, 156, 154, 151, 151, 148, 146, 846 | 144, 142, 140, 139, 136, 134, 133, 130, 847 | 129, 126, 124, 123, 121, 118, 117, 115, 848 | 113, 110, 109, 107, 105, 102, 102, 99, 849 | 96, 96, 93, 91, 89, 88, 85, 83, 850 | 81, 80, 77, 76, 73, 71, 71, 66, 851 | 67, 63, 62, 59, 59, 55, 54, 52, 852 | 50, 47, 47, 44, 42, 40, 39, 35, 853 | 35, 32, 30, 28, 27, 25, 22, 20, 854 | 19, 16, 15, 13, 11, 8, 7, 4, 855 | 4, 856 | }; 857 | const prog_uint8_t wav_bandlimited_triangle_1[] PROGMEM = { 858 | 3, 4, 7, 9, 10, 12, 15, 16, 859 | 18, 21, 22, 24, 26, 28, 31, 31, 860 | 34, 36, 37, 41, 41, 44, 45, 48, 861 | 49, 52, 54, 54, 58, 60, 60, 64, 862 | 65, 67, 68, 72, 73, 75, 76, 79, 863 | 81, 83, 84, 87, 89, 90, 92, 95, 864 | 96, 98, 101, 101, 105, 106, 108, 111, 865 | 111, 113, 117, 118, 119, 122, 124, 125, 866 | 128, 129, 132, 133, 136, 137, 139, 141, 867 | 144, 144, 148, 149, 151, 152, 156, 156, 868 | 159, 161, 162, 166, 165, 170, 169, 174, 869 | 173, 177, 178, 181, 181, 185, 186, 188, 870 | 190, 191, 195, 196, 197, 200, 202, 203, 871 | 207, 206, 210, 212, 213, 215, 218, 219, 872 | 221, 223, 226, 226, 230, 230, 234, 234, 873 | 237, 239, 241, 242, 245, 246, 249, 251, 874 | 253, 253, 250, 249, 245, 245, 242, 241, 875 | 238, 236, 235, 233, 230, 229, 226, 225, 876 | 223, 220, 219, 217, 215, 213, 211, 208, 877 | 208, 204, 204, 201, 199, 197, 195, 194, 878 | 191, 189, 187, 186, 183, 182, 179, 178, 879 | 175, 174, 172, 169, 168, 165, 165, 161, 880 | 160, 158, 156, 154, 151, 151, 148, 146, 881 | 144, 142, 141, 137, 137, 134, 133, 130, 882 | 128, 127, 124, 123, 120, 119, 117, 114, 883 | 113, 111, 108, 107, 105, 103, 101, 99, 884 | 97, 94, 94, 91, 89, 87, 86, 82, 885 | 82, 79, 78, 74, 75, 70, 70, 68, 886 | 65, 64, 62, 59, 57, 57, 53, 52, 887 | 50, 48, 45, 45, 41, 41, 38, 35, 888 | 35, 32, 30, 28, 27, 24, 22, 21, 889 | 18, 16, 15, 12, 11, 9, 6, 5, 890 | 2, 891 | }; 892 | const prog_uint8_t wav_bandlimited_triangle_2[] PROGMEM = { 893 | 3, 5, 7, 9, 10, 14, 14, 16, 894 | 19, 21, 22, 25, 26, 28, 31, 32, 895 | 34, 36, 38, 40, 42, 43, 47, 47, 896 | 50, 52, 53, 56, 57, 60, 61, 63, 897 | 66, 67, 69, 71, 73, 75, 77, 78, 898 | 82, 82, 85, 87, 88, 91, 92, 95, 899 | 96, 99, 99, 103, 104, 106, 108, 110, 900 | 112, 114, 115, 119, 119, 122, 123, 126, 901 | 127, 130, 131, 133, 136, 137, 139, 141, 902 | 143, 145, 147, 149, 150, 154, 154, 156, 903 | 160, 160, 162, 165, 166, 168, 171, 172, 904 | 174, 176, 178, 180, 182, 184, 185, 189, 905 | 189, 192, 193, 196, 197, 200, 201, 204, 906 | 204, 208, 209, 211, 213, 215, 218, 217, 907 | 222, 223, 224, 226, 230, 230, 232, 235, 908 | 236, 238, 241, 242, 244, 246, 248, 251, 909 | 253, 252, 250, 248, 246, 245, 241, 241, 910 | 238, 237, 234, 232, 231, 228, 226, 225, 911 | 223, 220, 220, 215, 216, 212, 211, 209, 912 | 207, 205, 203, 200, 200, 197, 196, 192, 913 | 191, 190, 187, 186, 183, 181, 180, 177, 914 | 176, 173, 172, 169, 169, 165, 164, 161, 915 | 161, 157, 157, 153, 152, 151, 147, 147, 916 | 144, 142, 140, 138, 137, 134, 133, 130, 917 | 129, 126, 125, 122, 121, 119, 116, 115, 918 | 113, 111, 109, 106, 106, 103, 100, 100, 919 | 97, 95, 93, 92, 88, 88, 86, 83, 920 | 81, 80, 77, 76, 73, 73, 68, 69, 921 | 65, 64, 62, 60, 58, 55, 55, 52, 922 | 49, 49, 46, 45, 41, 41, 38, 36, 923 | 35, 32, 31, 28, 27, 25, 22, 20, 924 | 20, 16, 15, 13, 10, 10, 6, 6, 925 | 2, 926 | }; 927 | const prog_uint8_t wav_bandlimited_triangle_3[] PROGMEM = { 928 | 2, 4, 5, 7, 10, 11, 14, 14, 929 | 19, 18, 22, 23, 25, 27, 30, 30, 930 | 34, 35, 36, 40, 41, 43, 44, 48, 931 | 48, 52, 52, 55, 56, 60, 60, 63, 932 | 64, 67, 69, 70, 73, 74, 77, 78, 933 | 81, 82, 85, 85, 90, 89, 93, 94, 934 | 96, 99, 99, 103, 104, 106, 108, 110, 935 | 112, 114, 116, 118, 120, 121, 125, 125, 936 | 128, 130, 132, 133, 136, 138, 139, 142, 937 | 143, 146, 148, 149, 151, 154, 155, 158, 938 | 159, 161, 164, 165, 167, 170, 170, 174, 939 | 175, 177, 179, 181, 183, 185, 187, 189, 940 | 191, 193, 195, 196, 200, 200, 203, 204, 941 | 208, 208, 210, 214, 214, 216, 219, 220, 942 | 223, 225, 226, 228, 230, 233, 235, 235, 943 | 239, 240, 242, 245, 246, 247, 251, 252, 944 | 255, 252, 252, 249, 246, 245, 243, 242, 945 | 238, 238, 234, 233, 232, 229, 227, 225, 946 | 223, 221, 220, 217, 215, 213, 212, 209, 947 | 208, 204, 204, 202, 199, 198, 195, 193, 948 | 192, 189, 189, 184, 184, 182, 180, 177, 949 | 176, 173, 173, 169, 168, 165, 165, 161, 950 | 160, 158, 156, 154, 151, 151, 148, 145, 951 | 145, 142, 139, 139, 136, 134, 132, 131, 952 | 127, 127, 124, 122, 121, 118, 116, 114, 953 | 113, 110, 109, 106, 105, 101, 102, 98, 954 | 96, 95, 93, 90, 88, 88, 84, 82, 955 | 82, 78, 77, 74, 74, 70, 69, 66, 956 | 66, 62, 62, 58, 57, 56, 52, 51, 957 | 49, 47, 46, 42, 42, 38, 38, 35, 958 | 33, 32, 28, 28, 25, 24, 20, 21, 959 | 16, 16, 13, 12, 9, 8, 5, 3, 960 | 2, 961 | }; 962 | const prog_uint8_t wav_bandlimited_triangle_4[] PROGMEM = { 963 | 2, 3, 3, 5, 7, 10, 11, 14, 964 | 16, 19, 20, 22, 24, 25, 29, 28, 965 | 33, 33, 36, 38, 40, 42, 44, 46, 966 | 48, 50, 51, 54, 56, 58, 59, 63, 967 | 63, 67, 68, 69, 72, 75, 74, 79, 968 | 79, 83, 83, 86, 88, 91, 91, 94, 969 | 96, 97, 101, 101, 104, 106, 108, 110, 970 | 112, 114, 116, 117, 121, 121, 124, 126, 971 | 128, 130, 132, 134, 136, 137, 141, 142, 972 | 143, 146, 148, 150, 152, 153, 157, 158, 973 | 160, 162, 163, 167, 167, 170, 172, 174, 974 | 176, 178, 180, 182, 184, 185, 189, 190, 975 | 191, 194, 196, 198, 200, 202, 204, 207, 976 | 207, 210, 212, 214, 215, 218, 220, 223, 977 | 223, 226, 229, 230, 232, 233, 237, 236, 978 | 240, 242, 245, 245, 250, 250, 253, 252, 979 | 255, 254, 252, 251, 250, 246, 245, 241, 980 | 241, 238, 235, 235, 231, 232, 227, 228, 981 | 223, 223, 221, 217, 216, 215, 211, 211, 982 | 208, 206, 204, 203, 201, 197, 197, 194, 983 | 191, 191, 188, 186, 184, 183, 180, 178, 984 | 177, 173, 173, 169, 169, 166, 164, 162, 985 | 160, 158, 157, 154, 152, 150, 148, 146, 986 | 144, 142, 141, 137, 137, 133, 133, 130, 987 | 128, 127, 123, 122, 120, 118, 116, 114, 988 | 113, 109, 109, 106, 103, 103, 100, 97, 989 | 97, 93, 92, 91, 88, 86, 84, 81, 990 | 81, 78, 76, 73, 72, 71, 67, 66, 991 | 65, 62, 59, 59, 55, 55, 51, 49, 992 | 49, 46, 43, 43, 40, 38, 36, 34, 993 | 32, 29, 27, 27, 23, 23, 19, 19, 994 | 17, 13, 12, 9, 7, 6, 3, 2, 995 | 3, 996 | }; 997 | const prog_uint8_t wav_bandlimited_triangle_5[] PROGMEM = { 998 | 2, 2, 3, 3, 4, 5, 7, 8, 999 | 10, 11, 14, 16, 18, 21, 22, 26, 1000 | 28, 30, 32, 36, 37, 40, 42, 44, 1001 | 46, 49, 50, 53, 54, 57, 58, 60, 1002 | 61, 65, 65, 68, 69, 71, 73, 76, 1003 | 77, 80, 81, 83, 87, 87, 91, 92, 1004 | 95, 97, 99, 102, 103, 106, 108, 110, 1005 | 113, 113, 117, 118, 121, 121, 125, 126, 1006 | 128, 129, 133, 133, 136, 137, 140, 142, 1007 | 144, 145, 148, 150, 153, 154, 156, 160, 1008 | 161, 163, 166, 168, 169, 174, 173, 177, 1009 | 179, 180, 183, 185, 186, 189, 190, 192, 1010 | 194, 196, 198, 199, 202, 203, 205, 209, 1011 | 208, 212, 215, 215, 219, 220, 224, 226, 1012 | 228, 230, 233, 236, 238, 239, 243, 244, 1013 | 246, 248, 249, 251, 252, 253, 253, 253, 1014 | 255, 253, 254, 253, 252, 251, 249, 248, 1015 | 246, 245, 242, 240, 238, 236, 232, 232, 1016 | 227, 227, 223, 220, 220, 215, 215, 211, 1017 | 210, 208, 205, 204, 201, 200, 198, 195, 1018 | 195, 192, 191, 188, 186, 186, 182, 181, 1019 | 178, 177, 175, 172, 170, 168, 166, 164, 1020 | 160, 160, 157, 154, 152, 151, 147, 147, 1021 | 143, 142, 139, 139, 135, 134, 131, 131, 1022 | 127, 127, 124, 122, 121, 118, 116, 114, 1023 | 113, 110, 108, 106, 104, 101, 99, 98, 1024 | 94, 92, 91, 89, 84, 85, 81, 80, 1025 | 77, 75, 73, 72, 70, 66, 67, 63, 1026 | 62, 60, 59, 56, 54, 53, 50, 49, 1027 | 46, 44, 43, 39, 38, 35, 32, 31, 1028 | 27, 26, 23, 20, 19, 15, 14, 12, 1029 | 9, 9, 6, 6, 3, 4, 2, 3, 1030 | 1, 1031 | }; 1032 | 1033 | 1034 | const prog_uint8_t* const waveform_table[] = { 1035 | wav_sine, 1036 | wav_bandlimited_square_0, 1037 | wav_bandlimited_square_1, 1038 | wav_bandlimited_square_2, 1039 | wav_bandlimited_square_3, 1040 | wav_bandlimited_square_4, 1041 | wav_bandlimited_square_5, 1042 | wav_sine, 1043 | wav_bandlimited_saw_0, 1044 | wav_bandlimited_saw_1, 1045 | wav_bandlimited_saw_2, 1046 | wav_bandlimited_saw_3, 1047 | wav_bandlimited_saw_4, 1048 | wav_bandlimited_saw_5, 1049 | wav_sine, 1050 | wav_bandlimited_triangle_0, 1051 | wav_bandlimited_triangle_1, 1052 | wav_bandlimited_triangle_2, 1053 | wav_bandlimited_triangle_3, 1054 | wav_bandlimited_triangle_4, 1055 | wav_bandlimited_triangle_5, 1056 | wav_sine, 1057 | }; 1058 | 1059 | 1060 | } // namespace module_tester 1061 | -------------------------------------------------------------------------------- /module_tester/resources.h: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Emilie Gillet.. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | // ----------------------------------------------------------------------------- 17 | // 18 | // Resources definitions. 19 | // 20 | // Automatically generated with: 21 | // make resources 22 | 23 | 24 | #ifndef MODULE_TESTER_RESOURCES_H_ 25 | #define MODULE_TESTER_RESOURCES_H_ 26 | 27 | 28 | #include "avrlib/base.h" 29 | 30 | #include 31 | 32 | 33 | #include "avrlib/resources_manager.h" 34 | 35 | namespace module_tester { 36 | 37 | typedef uint8_t ResourceId; 38 | 39 | extern const prog_char* const string_table[]; 40 | 41 | extern const prog_uint16_t* const lookup_table_table[]; 42 | 43 | extern const prog_uint32_t* const lookup_table_32_table[]; 44 | 45 | extern const prog_uint8_t* const character_table[]; 46 | 47 | extern const prog_uint8_t* const waveform_table[]; 48 | 49 | extern const prog_uint16_t lut_audio_midi_note[] PROGMEM; 50 | extern const prog_uint16_t lut_envelope[] PROGMEM; 51 | extern const prog_uint16_t lut_pulse_duration[] PROGMEM; 52 | extern const prog_uint16_t lut_pulse_ratio[] PROGMEM; 53 | extern const prog_uint16_t lut_clock_prescaler[] PROGMEM; 54 | extern const prog_uint16_t lut_sine[] PROGMEM; 55 | extern const prog_uint32_t lut_tempo_phase_increment[] PROGMEM; 56 | extern const prog_uint32_t lut_gate_phase_increment[] PROGMEM; 57 | extern const prog_uint32_t lut_cv_phase_increment[] PROGMEM; 58 | extern const prog_uint32_t lut_audio_phase_increment[] PROGMEM; 59 | extern const prog_uint8_t chr_special_characters[] PROGMEM; 60 | extern const prog_uint8_t wav_sine[] PROGMEM; 61 | extern const prog_uint8_t wav_bandlimited_square_0[] PROGMEM; 62 | extern const prog_uint8_t wav_bandlimited_square_1[] PROGMEM; 63 | extern const prog_uint8_t wav_bandlimited_square_2[] PROGMEM; 64 | extern const prog_uint8_t wav_bandlimited_square_3[] PROGMEM; 65 | extern const prog_uint8_t wav_bandlimited_square_4[] PROGMEM; 66 | extern const prog_uint8_t wav_bandlimited_square_5[] PROGMEM; 67 | extern const prog_uint8_t wav_bandlimited_saw_0[] PROGMEM; 68 | extern const prog_uint8_t wav_bandlimited_saw_1[] PROGMEM; 69 | extern const prog_uint8_t wav_bandlimited_saw_2[] PROGMEM; 70 | extern const prog_uint8_t wav_bandlimited_saw_3[] PROGMEM; 71 | extern const prog_uint8_t wav_bandlimited_saw_4[] PROGMEM; 72 | extern const prog_uint8_t wav_bandlimited_saw_5[] PROGMEM; 73 | extern const prog_uint8_t wav_bandlimited_triangle_0[] PROGMEM; 74 | extern const prog_uint8_t wav_bandlimited_triangle_1[] PROGMEM; 75 | extern const prog_uint8_t wav_bandlimited_triangle_2[] PROGMEM; 76 | extern const prog_uint8_t wav_bandlimited_triangle_3[] PROGMEM; 77 | extern const prog_uint8_t wav_bandlimited_triangle_4[] PROGMEM; 78 | extern const prog_uint8_t wav_bandlimited_triangle_5[] PROGMEM; 79 | #define STR_TEMPO 0 // tempo 80 | #define STR_RESOL_ 1 // resol. 81 | #define STR_PULSE 2 // pulse 82 | #define STR_MIDI 3 // midi 83 | #define STR_PERIOD 4 // period 84 | #define STR_MODE 5 // mode 85 | #define STR_SHAPE 6 // shape 86 | #define STR_RANGE 7 // range 87 | #define STR_FREQ_ 8 // freq. 88 | #define STR_ENVEL_ 9 // envel. 89 | #define STR_OFF 10 // off 90 | #define STR_ON 11 // on 91 | #define STR_NOTE 12 // note 92 | #define STR_GATE 13 // gate 93 | #define STR_1_PPQN 14 // 1 ppqn 94 | #define STR_4_PPQN 15 // 4 ppqn 95 | #define STR_8_PPQN 16 // 8 ppqn 96 | #define STR_24_PPQN 17 // 24 ppqn 97 | #define STR_1MS 18 // 1ms 98 | #define STR_2MS 19 // 2ms 99 | #define STR_5MS 20 // 5ms 100 | #define STR_10MS 21 // 10ms 101 | #define STR_5_ 22 // 5% 102 | #define STR_10_ 23 // 10% 103 | #define STR_20_ 24 // 20% 104 | #define STR_50_ 25 // 50% 105 | #define STR__10MS 26 // 10ms 106 | #define STR_20MS 27 // 20ms 107 | #define STR_50MS 28 // 50ms 108 | #define STR_100MS 29 // 100ms 109 | #define STR_200MS 30 // 200ms 110 | #define STR_500MS 31 // 500ms 111 | #define STR_1S 32 // 1s 112 | #define STR_2S 33 // 2s 113 | #define STR_5S 34 // 5s 114 | #define STR_10S 35 // 10s 115 | #define STR_TRIANGLE 36 // triangle 116 | #define STR_SQUARE 37 // square 117 | #define STR_RAMP_UP 38 // ramp up 118 | #define STR_RAMP_DN 39 // ramp dn 119 | #define STR_SINE 40 // sine 120 | #define STR_1OCT_ARP 41 // 1oct arp 121 | #define STR_2OCT_ARP 42 // 2oct arp 122 | #define STR_CHRM_SCL 43 // chrm.scl 123 | #define STR_C1_NOTE 44 // c1 note 124 | #define STR_C3_NOTE 45 // c3 note 125 | #define STR__OFF 46 // off 126 | #define STR__GATE 47 // gate 127 | #define STR_TRIGGER 48 // trigger 128 | #define STR___OFF 49 // off 129 | #define STR__NOTE 50 // note 130 | #define STR_VELOCITY 51 // velocity 131 | #define STR_PTCHBEND 52 // ptchbend 132 | #define STR_MODWHEEL 53 // modwheel 133 | #define STR_SAW 54 // saw 134 | #define STR__SQUARE 55 // square 135 | #define STR__TRIANGLE 56 // triangle 136 | #define STR__SINE 57 // sine 137 | #define STR_NOISE 58 // noise 138 | #define STR_10HZ 59 // 10Hz 139 | #define STR_20HZ 60 // 20Hz 140 | #define STR_50HZ 61 // 50Hz 141 | #define STR_100HZ 62 // 100Hz 142 | #define STR_200HZ 63 // 200Hz 143 | #define STR_500HZ 64 // 500Hz 144 | #define STR_1_0KHZ 65 // 1.0kHz 145 | #define STR_2_0KHZ 66 // 2.0kHz 146 | #define STR_5_0KHZ 67 // 5.0kHz 147 | #define STR_11V 68 // 1V 148 | #define STR_12V 69 // 2V 149 | #define STR_15V 70 // 5V 150 | #define STR_S1V 71 // +1V 151 | #define STR_S2V 72 // +2V 152 | #define STR_S5V 73 // +5V 153 | #define STR____OFF 74 // off 154 | #define STR___NOTE 75 // note 155 | #define STR___GATE 76 // gate 156 | #define STR__ON 77 // on 157 | #define LUT_AUDIO_MIDI_NOTE 0 158 | #define LUT_AUDIO_MIDI_NOTE_SIZE 137 159 | #define LUT_ENVELOPE 1 160 | #define LUT_ENVELOPE_SIZE 257 161 | #define LUT_PULSE_DURATION 2 162 | #define LUT_PULSE_DURATION_SIZE 8 163 | #define LUT_PULSE_RATIO 3 164 | #define LUT_PULSE_RATIO_SIZE 8 165 | #define LUT_CLOCK_PRESCALER 4 166 | #define LUT_CLOCK_PRESCALER_SIZE 4 167 | #define LUT_SINE 5 168 | #define LUT_SINE_SIZE 257 169 | #define LUT_TEMPO_PHASE_INCREMENT 0 170 | #define LUT_TEMPO_PHASE_INCREMENT_SIZE 201 171 | #define LUT_GATE_PHASE_INCREMENT 1 172 | #define LUT_GATE_PHASE_INCREMENT_SIZE 10 173 | #define LUT_CV_PHASE_INCREMENT 2 174 | #define LUT_CV_PHASE_INCREMENT_SIZE 11 175 | #define LUT_AUDIO_PHASE_INCREMENT 3 176 | #define LUT_AUDIO_PHASE_INCREMENT_SIZE 137 177 | #define CHR_SPECIAL_CHARACTERS 0 178 | #define CHR_SPECIAL_CHARACTERS_SIZE 56 179 | #define WAV_SINE 0 180 | #define WAV_SINE_SIZE 257 181 | #define WAV_BANDLIMITED_SQUARE_0 1 182 | #define WAV_BANDLIMITED_SQUARE_0_SIZE 257 183 | #define WAV_BANDLIMITED_SQUARE_1 2 184 | #define WAV_BANDLIMITED_SQUARE_1_SIZE 257 185 | #define WAV_BANDLIMITED_SQUARE_2 3 186 | #define WAV_BANDLIMITED_SQUARE_2_SIZE 257 187 | #define WAV_BANDLIMITED_SQUARE_3 4 188 | #define WAV_BANDLIMITED_SQUARE_3_SIZE 257 189 | #define WAV_BANDLIMITED_SQUARE_4 5 190 | #define WAV_BANDLIMITED_SQUARE_4_SIZE 257 191 | #define WAV_BANDLIMITED_SQUARE_5 6 192 | #define WAV_BANDLIMITED_SQUARE_5_SIZE 257 193 | #define WAV_BANDLIMITED_SQUARE_6 7 194 | #define WAV_BANDLIMITED_SQUARE_6_SIZE 257 195 | #define WAV_BANDLIMITED_SAW_0 8 196 | #define WAV_BANDLIMITED_SAW_0_SIZE 257 197 | #define WAV_BANDLIMITED_SAW_1 9 198 | #define WAV_BANDLIMITED_SAW_1_SIZE 257 199 | #define WAV_BANDLIMITED_SAW_2 10 200 | #define WAV_BANDLIMITED_SAW_2_SIZE 257 201 | #define WAV_BANDLIMITED_SAW_3 11 202 | #define WAV_BANDLIMITED_SAW_3_SIZE 257 203 | #define WAV_BANDLIMITED_SAW_4 12 204 | #define WAV_BANDLIMITED_SAW_4_SIZE 257 205 | #define WAV_BANDLIMITED_SAW_5 13 206 | #define WAV_BANDLIMITED_SAW_5_SIZE 257 207 | #define WAV_BANDLIMITED_SAW_6 14 208 | #define WAV_BANDLIMITED_SAW_6_SIZE 257 209 | #define WAV_BANDLIMITED_TRIANGLE_0 15 210 | #define WAV_BANDLIMITED_TRIANGLE_0_SIZE 257 211 | #define WAV_BANDLIMITED_TRIANGLE_1 16 212 | #define WAV_BANDLIMITED_TRIANGLE_1_SIZE 257 213 | #define WAV_BANDLIMITED_TRIANGLE_2 17 214 | #define WAV_BANDLIMITED_TRIANGLE_2_SIZE 257 215 | #define WAV_BANDLIMITED_TRIANGLE_3 18 216 | #define WAV_BANDLIMITED_TRIANGLE_3_SIZE 257 217 | #define WAV_BANDLIMITED_TRIANGLE_4 19 218 | #define WAV_BANDLIMITED_TRIANGLE_4_SIZE 257 219 | #define WAV_BANDLIMITED_TRIANGLE_5 20 220 | #define WAV_BANDLIMITED_TRIANGLE_5_SIZE 257 221 | #define WAV_BANDLIMITED_TRIANGLE_6 21 222 | #define WAV_BANDLIMITED_TRIANGLE_6_SIZE 257 223 | typedef avrlib::ResourcesManager< 224 | ResourceId, 225 | avrlib::ResourcesTables< 226 | string_table, 227 | lookup_table_table> > ResourcesManager; 228 | 229 | } // namespace module_tester 230 | 231 | #endif // MODULE_TESTER_RESOURCES_H_ 232 | -------------------------------------------------------------------------------- /module_tester/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pichenettes/module_tester/65ca98c42e6f65bd73fa91d14427bf81404dbf05/module_tester/resources/__init__.py -------------------------------------------------------------------------------- /module_tester/resources/characters.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2.5 2 | # 3 | # Copyright 2013 Emilie Gillet.. 4 | # 5 | # Author: Emilie Gillet (emilie.o.gillet@gmail.com) 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | # ----------------------------------------------------------------------------- 19 | # 20 | # Custom characters definitions. 21 | 22 | def StringImageToBytes(string_image): 23 | """Converts an "ASCII art" image into character definition data. 24 | 25 | The output is a list of bytes, and the image is made of spaces or . and X. 26 | """ 27 | rows = [row.strip() for row in string_image.split('\n') if row.strip()] 28 | assert len(rows) == 8 29 | bytes = [] 30 | for row in rows: 31 | int8 = 0 32 | assert len(row) == 5 33 | for char in row: 34 | int8 *= 2 35 | if char == 'X': 36 | int8 += 1 37 | int8 += 0 # 128 + 64 + 32 38 | bytes.append(int8) 39 | return bytes 40 | 41 | 42 | special_characters = [ 43 | """ 44 | ..X.. 45 | ..X.. 46 | XXXXX 47 | ..X.. 48 | ..X.. 49 | ..... 50 | XXXXX 51 | ..... 52 | """, 53 | """ 54 | ..... 55 | ..... 56 | ..X.. 57 | ..X.. 58 | .XXX. 59 | XXXXX 60 | XXXXX 61 | ..... 62 | """, 63 | """ 64 | ..... 65 | ..... 66 | .X... 67 | X.X.X 68 | ...X. 69 | ..... 70 | ..... 71 | ..... 72 | """, 73 | """ 74 | ....X 75 | ....X 76 | ....X 77 | ...XX 78 | ...XX 79 | ..... 80 | X..X. 81 | .XX.. 82 | """, 83 | """ 84 | ..... 85 | XX..X 86 | XX.XX 87 | ..XX. 88 | ...X. 89 | ..X.. 90 | ..X.. 91 | .X... 92 | """, 93 | """ 94 | ..... 95 | X.... 96 | .X... 97 | ..X.. 98 | ...X. 99 | ....X 100 | ..... 101 | ..... 102 | """, 103 | """ 104 | ..X.. 105 | ..... 106 | ..X.. 107 | ..... 108 | ..X.. 109 | ..... 110 | ..X.. 111 | ..... 112 | """] 113 | 114 | characters = [ 115 | ('special_characters', 116 | sum([StringImageToBytes(image) for image in special_characters], [])), 117 | ] 118 | -------------------------------------------------------------------------------- /module_tester/resources/lookup_tables.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2.5 2 | # 3 | # Copyright 2013 Emilie Gillet.. 4 | # 5 | # Author: Emilie Gillet (emilie.o.gillet@gmail.com) 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | # ----------------------------------------------------------------------------- 19 | # 20 | # Lookup table definitions. 21 | 22 | import numpy 23 | 24 | lookup_tables = [] 25 | lookup_tables_32 = [] 26 | sample_rate = 20000000 / 510.0 27 | control_rate = sample_rate / 4 28 | 29 | 30 | 31 | """---------------------------------------------------------------------------- 32 | Phase increment for tempo. 33 | ----------------------------------------------------------------------------""" 34 | 35 | width = 1 << 32 36 | tempo_values = numpy.arange(40, 241.0) 37 | lookup_tables_32.append( 38 | ('tempo_phase_increment', width * tempo_values * 8 / (60 * control_rate)) 39 | ) 40 | 41 | 42 | 43 | """---------------------------------------------------------------------------- 44 | Phase increment for gate. 45 | ----------------------------------------------------------------------------""" 46 | 47 | period_values = numpy.array( 48 | [0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1.0, 2.0, 5.0, 10.0]) 49 | lookup_tables_32.append( 50 | ('gate_phase_increment', width / (control_rate * period_values)) 51 | ) 52 | 53 | 54 | 55 | """---------------------------------------------------------------------------- 56 | Phase increment for CV source. 57 | ----------------------------------------------------------------------------""" 58 | 59 | period_values = numpy.array( 60 | [0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1.0, 2.0, 5.0, 10.0, 20.0]) 61 | lookup_tables_32.append( 62 | ('cv_phase_increment', width / (control_rate * period_values)) 63 | ) 64 | 65 | 66 | 67 | """---------------------------------------------------------------------------- 68 | Phase increment for audio source. 69 | ----------------------------------------------------------------------------""" 70 | 71 | frequencies = 440 * 2 ** ((12 + numpy.arange(0, 128.0) - 69) / 12.0) 72 | frequencies = [10, 20, 50, 100, 200, 500, 1000, 2000, 5000] + list(frequencies) 73 | frequencies = numpy.array(frequencies) 74 | lookup_tables_32.append( 75 | ('audio_phase_increment', width * frequencies / sample_rate) 76 | ) 77 | lookup_tables.append( 78 | ('audio_midi_note', 69 + 12 * numpy.log2(frequencies / 440.0)) 79 | ) 80 | 81 | envelope = numpy.arange(0, 257) / 256.0 82 | envelope[-1] = 1.0 83 | envelope = (1 - envelope) ** 4 84 | lookup_tables.append( 85 | ('envelope', 4095 * envelope) 86 | ) 87 | 88 | 89 | 90 | """---------------------------------------------------------------------------- 91 | Pulse durations 92 | ----------------------------------------------------------------------------""" 93 | 94 | durations = numpy.array([0.001, 0.002, 0.005, 0.01, 0.0, 0.0, 0.0, 0.0]) 95 | lookup_tables.append( 96 | ('pulse_duration', durations * control_rate) 97 | ) 98 | 99 | pulse_ratios = numpy.array([0, 0, 0, 0, 0.05, 0.10, 0.20, 0.5]) * 65536 100 | lookup_tables.append( 101 | ('pulse_ratio', pulse_ratios) 102 | ) 103 | 104 | lookup_tables.append( 105 | ('clock_prescaler', [24, 6, 3, 1]) 106 | ) 107 | 108 | 109 | 110 | """---------------------------------------------------------------------------- 111 | Sine wave for CV source. 112 | ----------------------------------------------------------------------------""" 113 | 114 | t = numpy.arange(0, 257) / 256.0 115 | sine = numpy.sin(2 * numpy.pi * t) 116 | 117 | lookup_tables.append( 118 | ('sine', (sine + 1.0) * 32767) 119 | ) 120 | -------------------------------------------------------------------------------- /module_tester/resources/resources.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2.5 2 | # 3 | # Copyright 2013 Emilie Gillet.. 4 | # 5 | # Author: Emilie Gillet (emilie.o.gillet@gmail.com) 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | # ----------------------------------------------------------------------------- 19 | # 20 | # Master resources file. 21 | 22 | header = """// Copyright 2013 Emilie Gillet.. 23 | // 24 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 25 | // 26 | // This program is free software: you can redistribute it and/or modify 27 | // it under the terms of the GNU General Public License as published by 28 | // the Free Software Foundation, either version 3 of the License, or 29 | // (at your option) any later version. 30 | // This program is distributed in the hope that it will be useful, 31 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 32 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 33 | // GNU General Public License for more details. 34 | // You should have received a copy of the GNU General Public License 35 | // along with this program. If not, see . 36 | // 37 | // ----------------------------------------------------------------------------- 38 | // 39 | // Resources definitions. 40 | // 41 | // Automatically generated with: 42 | // make resources 43 | """ 44 | 45 | namespace = 'module_tester' 46 | target = 'module_tester' 47 | modifier = 'PROGMEM' 48 | types = ['uint8_t', 'uint16_t'] 49 | includes = """ 50 | #include "avrlib/base.h" 51 | 52 | #include 53 | """ 54 | create_specialized_manager = True 55 | 56 | import numpy 57 | 58 | import characters 59 | import lookup_tables 60 | import strings 61 | import waveforms 62 | 63 | resources = [ 64 | (strings.strings, 65 | 'string', 'STR', 'prog_char', str, False), 66 | (lookup_tables.lookup_tables, 67 | 'lookup_table', 'LUT', 'prog_uint16_t', int, False), 68 | (lookup_tables.lookup_tables_32, 69 | 'lookup_table_32', 'LUT', 'prog_uint32_t', int, False), 70 | (characters.characters, 'character', 'CHR', 'prog_uint8_t', int, True), 71 | (waveforms.waveforms, 'waveform', 'WAV', 'prog_uint8_t', int, True), 72 | ] 73 | -------------------------------------------------------------------------------- /module_tester/resources/strings.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2.5 2 | # 3 | # Copyright 2013 Emilie Gillet.. 4 | # 5 | # Author: Emilie Gillet (emilie.o.gillet@gmail.com) 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | # ----------------------------------------------------------------------------- 19 | # 20 | # Resource strings for the motherboard/controller. 21 | 22 | strings = """ 23 | tempo 24 | resol. 25 | pulse 26 | midi 27 | period 28 | mode 29 | shape 30 | range 31 | freq. 32 | envel. 33 | 34 | off 35 | on 36 | note 37 | gate 38 | 39 | 1 ppqn 40 | 4 ppqn 41 | 8 ppqn 42 | 24 ppqn 43 | 44 | 1ms 45 | 2ms 46 | 5ms 47 | 10ms 48 | 5% 49 | 10% 50 | 20% 51 | 50% 52 | 53 | 10ms 54 | 20ms 55 | 50ms 56 | 100ms 57 | 200ms 58 | 500ms 59 | 1s 60 | 2s 61 | 5s 62 | 10s 63 | 64 | triangle 65 | square 66 | ramp up 67 | ramp dn 68 | sine 69 | 1oct arp 70 | 2oct arp 71 | chrm.scl 72 | c1 note 73 | c3 note 74 | 75 | off 76 | gate 77 | trigger 78 | 79 | off 80 | note 81 | velocity 82 | ptchbend 83 | modwheel 84 | 85 | saw 86 | square 87 | triangle 88 | sine 89 | noise 90 | 91 | 10Hz 92 | 20Hz 93 | 50Hz 94 | 100Hz 95 | 200Hz 96 | 500Hz 97 | 1.0kHz 98 | 2.0kHz 99 | 5.0kHz 100 | 101 | \x011V 102 | \x012V 103 | \x015V 104 | +1V 105 | +2V 106 | +5V 107 | 108 | off 109 | note 110 | gate 111 | on 112 | """ 113 | -------------------------------------------------------------------------------- /module_tester/resources/waveforms.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2.5 2 | # 3 | # Copyright 2013 Emilie Gillet.. 4 | # 5 | # Author: Emilie Gillet (emilie.o.gillet@gmail.com) 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | # ----------------------------------------------------------------------------- 19 | # 20 | # Band-limited wavetables. 21 | 22 | import numpy 23 | 24 | SAMPLE_RATE = 20000000.0 / 510 25 | 26 | waveforms = [] 27 | 28 | """---------------------------------------------------------------------------- 29 | Band-limited waveforms 30 | ----------------------------------------------------------------------------""" 31 | 32 | WAVETABLE_SIZE = 256 33 | 34 | def Dither(x, order=0, type=numpy.uint8): 35 | for i in xrange(order): 36 | x = numpy.hstack((numpy.zeros(1,), numpy.cumsum(x))) 37 | x = numpy.round(x) 38 | for i in xrange(order): 39 | x = numpy.diff(x) 40 | if any(x < numpy.iinfo(type).min) or any(x > numpy.iinfo(type).max): 41 | print 'Clipping occurred!' 42 | x[x < numpy.iinfo(type).min] = numpy.iinfo(type).min 43 | x[x > numpy.iinfo(type).max] = numpy.iinfo(type).max 44 | return x.astype(type) 45 | 46 | 47 | def Scale(array, min=1, max=254, center=True, dither=2): 48 | if center: 49 | array -= array.mean() 50 | mx = numpy.abs(array).max() 51 | array = (array + mx) / (2 * mx) 52 | array = array * (max - min) + min 53 | return Dither(array, order=dither) 54 | 55 | 56 | # Sine wave. 57 | numpy.random.seed(21) 58 | sine = -numpy.sin(numpy.arange(WAVETABLE_SIZE + 1) / float(WAVETABLE_SIZE) * 2 * numpy.pi) * 127.5 + 127.5 59 | 60 | # Band limited waveforms. 61 | num_zones = (107 - 24) / 16 + 2 62 | bl_pulse_tables = [] 63 | bl_square_tables = [] 64 | bl_saw_tables = [] 65 | bl_tri_tables = [] 66 | 67 | wrap = numpy.fmod( 68 | numpy.arange(WAVETABLE_SIZE + 1) + WAVETABLE_SIZE / 2, WAVETABLE_SIZE) 69 | quadrature = numpy.fmod( 70 | numpy.arange(WAVETABLE_SIZE + 1) + WAVETABLE_SIZE / 4, WAVETABLE_SIZE) 71 | fill = numpy.fmod( 72 | numpy.arange(WAVETABLE_SIZE + 1), WAVETABLE_SIZE) 73 | 74 | waveforms.append(('sine', Scale(sine[quadrature]))) 75 | 76 | for zone in range(num_zones): 77 | f0 = 440.0 * 2.0 ** ((18 + 16 * zone - 69) / 12.0) 78 | period = SAMPLE_RATE / f0 79 | m = 2 * numpy.floor(period / 2) + 1.0 80 | i = numpy.arange(-WAVETABLE_SIZE / 2, WAVETABLE_SIZE / 2) / float(WAVETABLE_SIZE) 81 | pulse = numpy.sin(numpy.pi * i * m) / (m * numpy.sin(numpy.pi * i) + 1e-9) 82 | pulse[WAVETABLE_SIZE / 2] = 1.0 83 | pulse = pulse[fill] 84 | 85 | square = numpy.cumsum(pulse - pulse[wrap]) 86 | triangle = -numpy.cumsum(square[::-1] - square.mean()) / WAVETABLE_SIZE 87 | 88 | if zone == num_zones - 1: 89 | square = sine 90 | bl_square_tables.append(('bandlimited_square_%d' % zone, 91 | Scale(square[quadrature]))) 92 | 93 | triangle = triangle[quadrature] 94 | if zone == num_zones - 1: 95 | triangle = sine 96 | bl_tri_tables.append(('bandlimited_triangle_%d' % zone, 97 | Scale(triangle[quadrature]))) 98 | 99 | saw = -numpy.cumsum(pulse[wrap] - pulse.mean()) 100 | if zone == num_zones - 1: 101 | saw = sine 102 | bl_saw_tables.append(('bandlimited_saw_%d' % zone, 103 | Scale(saw[quadrature]))) 104 | 105 | 106 | waveforms.extend(bl_pulse_tables) 107 | waveforms.extend(bl_square_tables) 108 | waveforms.extend(bl_saw_tables) 109 | waveforms.extend(bl_tri_tables) 110 | -------------------------------------------------------------------------------- /module_tester/signal_generator.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Emilie Gillet.. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | // ----------------------------------------------------------------------------- 17 | // 18 | // Signal generator. 19 | 20 | #include "module_tester/signal_generator.h" 21 | 22 | #include 23 | 24 | #include "avrlib/gpio.h" 25 | #include "avrlib/op.h" 26 | 27 | #include "module_tester/resources.h" 28 | 29 | namespace module_tester { 30 | 31 | using namespace avrlib; 32 | 33 | /* static */ 34 | SignalGeneratorSettings SignalGenerator::data_; 35 | 36 | /* static */ 37 | SignalGeneratorState SignalGenerator::state_; 38 | 39 | /* static */ 40 | uint16_t SignalGenerator::cv_samples_[kCvBlockSize]; 41 | 42 | /* static */ 43 | avrlib::RingBuffer SignalGenerator::audio_buffer_; 44 | 45 | /* static */ 46 | avrlib::RingBuffer SignalGenerator::cv_buffer_; 47 | 48 | /* static */ 49 | uint16_t SignalGenerator::pitch_bend_; 50 | 51 | /* static */ 52 | uint8_t SignalGenerator::modulation_; 53 | 54 | /* static */ 55 | uint16_t SignalGenerator::last_note_value_; 56 | 57 | /* static */ 58 | NoteStack<10> SignalGenerator::note_stack_; 59 | 60 | /* extern */ 61 | SignalGenerator signal_generator; 62 | 63 | typedef SignalGeneratorSettings PROGMEM prog_SignalGeneratorSettings; 64 | 65 | const prog_SignalGeneratorSettings init_settings PROGMEM = { 66 | 120, CLOCK_RESOLUTION_24_PPQN, PULSE_DURATION_5_MS, false, 67 | PERIOD_1_S, PULSE_DURATION_50_PERCENT, GATE_MIDI_MODE_OFF, 0, 68 | CV_MODE_TRIANGLE_LFO, PERIOD_1_S, CV_RANGE_PM_5V, CV_MIDI_MODE_OFF, 69 | AUDIO_MODE_SINE, 69, AUDIO_ENVELOPE_MODE_OFF, AUDIO_MIDI_MODE_OFF, 70 | }; 71 | 72 | /* static */ 73 | void SignalGenerator::Init() { 74 | memcpy_P(&data_, &init_settings, sizeof(SignalGeneratorSettings)); 75 | note_stack_.Init(); 76 | pitch_bend_ = 8192; 77 | modulation_ = 0; 78 | } 79 | 80 | /* static */ 81 | void SignalGenerator::Render() { 82 | Gpio::set_mode(DIGITAL_OUTPUT); 83 | 84 | // Render some audio samples. 85 | while (audio_buffer_.writable() >= kAudioBlockSize) { 86 | Gpio::High(); 87 | uint8_t frequency = data_.audio.frequency; 88 | if (data_.audio.midi_mode == AUDIO_MIDI_MODE_NOTE || 89 | data_.audio.midi_mode == AUDIO_MIDI_MODE_ON) { 90 | if (note_stack_.size()) { 91 | state_.last_midi_note = note_stack_.most_recent_note().note; 92 | if (state_.last_midi_note < 12) { 93 | state_.last_midi_note = 12; 94 | } else if (state_.last_midi_note > 108) { 95 | state_.last_midi_note = 108; 96 | } 97 | } 98 | frequency = state_.last_midi_note + 9 - 12; 99 | } 100 | state_.audio_phase_increment = pgm_read_dword( 101 | lut_audio_phase_increment + frequency); 102 | state_.audio_midi_note = pgm_read_word(lut_audio_midi_note + frequency); 103 | AudioRenderFn render_fn; 104 | memcpy_P(&render_fn, fn_table_ + data_.audio.mode, sizeof(AudioRenderFn)); 105 | (*render_fn)(); 106 | Gpio::Low(); 107 | } 108 | 109 | // Render some CV samples. 110 | while (cv_buffer_.writable() >= kCvBlockSize) { 111 | UpdateIncrements(); 112 | RenderCv(); 113 | RenderClock(); 114 | RenderGate(); 115 | for (uint8_t i = 0; i < kCvBlockSize; ++i) { 116 | cv_buffer_.Overwrite(cv_samples_[i]); 117 | } 118 | } 119 | } 120 | 121 | /* static */ 122 | void SignalGenerator::UpdateIncrements() { 123 | // Clock. 124 | uint32_t increment = pgm_read_dword( 125 | lut_tempo_phase_increment + data_.clock.tempo - 40); 126 | state_.clock_pulse_duration = pgm_read_word( 127 | lut_pulse_duration + data_.clock.pulse_duration); 128 | state_.clock_pulse_ratio = pgm_read_word( 129 | lut_pulse_ratio + data_.clock.pulse_duration); 130 | switch (data_.clock.resolution) { 131 | case CLOCK_RESOLUTION_1_PPQN: 132 | state_.clock_phase_increment = increment >> 3; 133 | break; 134 | case CLOCK_RESOLUTION_4_PPQN: 135 | state_.clock_phase_increment = increment >> 1; 136 | break; 137 | case CLOCK_RESOLUTION_8_PPQN: 138 | state_.clock_phase_increment = increment; 139 | break; 140 | case CLOCK_RESOLUTION_24_PPQN: 141 | state_.clock_phase_increment = (increment << 1) + increment; 142 | break; 143 | } 144 | if (data_.clock.midi_mode) { 145 | if (!state_.clock_pulse_duration) { 146 | state_.clock_pulse_duration = pgm_read_word(lut_pulse_duration); 147 | } 148 | state_.clock_prescaler = pgm_read_word( 149 | lut_clock_prescaler + data_.clock.resolution); 150 | } 151 | 152 | // Gate. 153 | state_.gate_phase_increment = pgm_read_dword( 154 | lut_gate_phase_increment + data_.gate.period); 155 | state_.gate_pulse_duration = pgm_read_word( 156 | lut_pulse_duration + data_.gate.pulse_duration); 157 | state_.gate_pulse_ratio = pgm_read_word( 158 | lut_pulse_ratio + data_.gate.pulse_duration); 159 | if (data_.gate.midi_mode && !state_.gate_pulse_duration) { 160 | state_.gate_pulse_duration = pgm_read_word(lut_pulse_duration); 161 | } 162 | 163 | // CV. 164 | state_.cv_phase_increment = pgm_read_dword( 165 | lut_cv_phase_increment + data_.cv.period); 166 | } 167 | 168 | /* static */ 169 | void SignalGenerator::RenderClock() { 170 | if (data_.clock.midi_mode) { 171 | return; 172 | } 173 | LongWord clock_phase; 174 | clock_phase.value = state_.clock_phase; 175 | uint32_t increment = state_.clock_phase_increment; 176 | for (uint8_t i = 0; i < kCvBlockSize; i++) { 177 | clock_phase.value += increment; 178 | if (clock_phase.value < increment) { 179 | RaiseClockPulse(); 180 | } 181 | if (DecreaseClockPulse()) { 182 | cv_samples_[i] |= 0x8000; 183 | } 184 | if (clock_phase.words[1] < state_.clock_pulse_ratio) { 185 | cv_samples_[i] |= 0x8000; 186 | } 187 | } 188 | state_.clock_phase = clock_phase.value; 189 | } 190 | 191 | /* static */ 192 | void SignalGenerator::RenderGate() { 193 | LongWord gate_phase; 194 | gate_phase.value = state_.gate_phase; 195 | 196 | for (uint8_t i = 0; i < kCvBlockSize; ++i) { 197 | if (!data_.gate.midi_mode) { 198 | gate_phase.value += state_.gate_phase_increment; 199 | if (gate_phase.value < state_.gate_phase_increment) { 200 | state_.gate_pulse = state_.gate_pulse_duration; 201 | } 202 | if (gate_phase.words[1] < state_.gate_pulse_ratio) { 203 | cv_samples_[i] |= 0x4000; 204 | } 205 | } else if (data_.gate.midi_mode == GATE_MIDI_MODE_GATE) { 206 | if (note_stack_.size()) { 207 | cv_samples_[i] |= 0x4000; 208 | } 209 | } 210 | if (state_.gate_pulse) { 211 | cv_samples_[i] |= 0x4000; 212 | --state_.gate_pulse; 213 | } 214 | } 215 | state_.gate_phase = gate_phase.value; 216 | } 217 | 218 | const prog_uint16_t cv_range_scale[] PROGMEM = { 667, 1333, 3333, 333, 667, 1667 }; 219 | const prog_uint16_t cv_range_offset[] PROGMEM = { 2048, 2048, 2048, 1881, 1715, 1215 }; 220 | const prog_uint16_t chromatic_scale[] PROGMEM = { 221 | 1715, 1687, 1659, 1631, 1604, 1576, 222 | 1548, 1520, 1492, 1465, 1437, 1409, 1381 }; 223 | 224 | /* static */ 225 | void SignalGenerator::RenderCv() { 226 | uint16_t scale = pgm_read_word(cv_range_scale + data_.cv.range); 227 | uint16_t offset = pgm_read_word(cv_range_offset + data_.cv.range); 228 | 229 | if (data_.cv.midi_mode) { 230 | uint16_t value = 0; 231 | switch (data_.cv.midi_mode) { 232 | case CV_MIDI_MODE_NOTE: 233 | { 234 | if (note_stack_.size()) { 235 | int16_t note = note_stack_.most_recent_note().note; 236 | value = 2048 - S16U16MulShift16((note - 36) << 8, 7111); 237 | last_note_value_ = value; 238 | } else { 239 | value = last_note_value_; 240 | } 241 | } 242 | break; 243 | case CV_MIDI_MODE_VELOCITY: 244 | if (note_stack_.size()) { 245 | value = note_stack_.most_recent_note().velocity << 9; 246 | } else { 247 | value = 0; 248 | } 249 | break; 250 | case CV_MIDI_MODE_PITCH_BEND: 251 | value = pitch_bend_ << 2; 252 | break; 253 | case CV_MIDI_MODE_MOD_WHEEL: 254 | value = modulation_ << 9; 255 | break; 256 | } 257 | if (data_.cv.midi_mode != CV_MIDI_MODE_NOTE) { 258 | value = S16U16MulShift16(32767 - value, scale) + offset; 259 | } 260 | for (uint8_t i = 0; i < kCvBlockSize; ++i) { 261 | cv_samples_[i] = value; 262 | } 263 | } else { 264 | LongWord cv_phase; 265 | cv_phase.value = state_.cv_phase; 266 | for (uint8_t i = 0; i < kCvBlockSize; ++i) { 267 | cv_phase.value += state_.cv_phase_increment; 268 | uint16_t value = cv_phase.words[1]; 269 | switch (data_.cv.mode) { 270 | case CV_MODE_TRIANGLE_LFO: 271 | value = (value & 0x8000) ? 65535 - (value << 1) : (value << 1); 272 | break; 273 | case CV_MODE_SQUARE_LFO: 274 | value = (value & 0x8000) ? 0xffff : 0x000; 275 | break; 276 | case CV_MODE_RAMP_UP_LFO: 277 | break; 278 | case CV_MODE_RAMP_DOWN_LFO: 279 | value = (~value); 280 | break; 281 | case CV_MODE_SINE_LFO: 282 | { 283 | uint8_t integral = cv_phase.bytes[3]; 284 | uint8_t fractional = cv_phase.bytes[2]; 285 | uint16_t a = pgm_read_word(lut_sine + integral); 286 | uint16_t b = pgm_read_word(lut_sine + integral + 1); 287 | value = a + S16U8MulShift8(b - a, fractional); 288 | } 289 | break; 290 | case CV_MODE_1_OCTAVE_ARP: 291 | { 292 | value = (value & 0x8000) ? 1715 : 1381; 293 | } 294 | break; 295 | case CV_MODE_2_OCTAVE_ARP: 296 | { 297 | value = (value & 0x8000) ? 1715 : 1048; 298 | } 299 | break; 300 | case CV_MODE_CHROMATIC_SCALE_ARP: 301 | { 302 | uint8_t index = U8U8MulShift8(cv_phase.bytes[3], 13); 303 | value = pgm_read_word(chromatic_scale + index); 304 | } 305 | break; 306 | case CV_MODE_C1_NOTE: 307 | value = 1715; 308 | break; 309 | case CV_MODE_C3_NOTE: 310 | value = 1048; 311 | break; 312 | } 313 | if (data_.cv.mode < CV_MODE_1_OCTAVE_ARP) { 314 | value = S16U16MulShift16(32767 - value, scale) + offset; 315 | } 316 | cv_samples_[i] = value; 317 | } 318 | state_.cv_phase = cv_phase.value; 319 | } 320 | } 321 | 322 | /* static */ 323 | void SignalGenerator::RenderAudioBandLimited() { 324 | int8_t note = state_.audio_midi_note; 325 | if (note < 12) { 326 | note = 12; 327 | } 328 | uint8_t balance_index = U8Swap4(note - 12); 329 | uint8_t gain_2 = balance_index & 0xf0; 330 | uint8_t gain_1 = ~gain_2; 331 | uint8_t wave_index = balance_index & 0xf; 332 | uint8_t base_resource_id = data_.audio.mode == AUDIO_MODE_SAW ? 333 | WAV_BANDLIMITED_SAW_0 : 334 | (data_.audio.mode == AUDIO_MODE_SQUARE ? WAV_BANDLIMITED_SQUARE_0 : 335 | WAV_BANDLIMITED_TRIANGLE_0); 336 | 337 | const prog_uint8_t* wave_1 = waveform_table[base_resource_id + wave_index]; 338 | wave_index = U8AddClip(wave_index, 1, 6); 339 | const prog_uint8_t* wave_2 = waveform_table[base_resource_id + wave_index]; 340 | 341 | uint32_t phase = state_.audio_phase; 342 | uint32_t increment = state_.audio_phase_increment; 343 | uint16_t envelope_phase = state_.audio_envelope_phase; 344 | uint8_t count = kAudioBlockSize; 345 | 346 | if (state_.trigger_count) { 347 | state_.trigger_count = 0; 348 | envelope_phase = 0; 349 | } 350 | uint16_t amplitude = 4095; 351 | if (data_.audio.envelope_mode == AUDIO_ENVELOPE_MODE_GATE 352 | && !state_.gate_state) { 353 | amplitude = 0; 354 | } 355 | if (data_.audio.midi_mode == AUDIO_MIDI_MODE_ON || 356 | data_.audio.midi_mode == AUDIO_MIDI_MODE_GATE) { 357 | amplitude = note_stack_.size() ? 4095 : 0; 358 | } 359 | 360 | while (count--) { 361 | phase += increment; 362 | LongWord phi; 363 | phi.value = phase; 364 | 365 | envelope_phase += 4; 366 | if (envelope_phase < 4) { 367 | envelope_phase = 0xffff; 368 | } 369 | 370 | uint16_t sample = U8U8Mul(InterpolateSample(wave_1, phi.words[1]), gain_1); 371 | sample += U8U8Mul(InterpolateSample(wave_2, phi.words[1]), gain_2); 372 | 373 | if (data_.audio.envelope_mode == AUDIO_ENVELOPE_MODE_TRIGGER) { 374 | Word envelope_phi; 375 | envelope_phi.value = envelope_phase; 376 | 377 | uint16_t a = pgm_read_word(lut_envelope + envelope_phi.bytes[1]); 378 | uint16_t b = pgm_read_word(lut_envelope + envelope_phi.bytes[1] + 1); 379 | amplitude = a + S16U8MulShift8(b - a, envelope_phi.bytes[0]); 380 | } 381 | audio_buffer_.Overwrite(2048 + S16U16MulShift16(sample + 32768, amplitude)); 382 | } 383 | state_.audio_phase = phase; 384 | state_.audio_envelope_phase = envelope_phase; 385 | } 386 | 387 | /* static */ 388 | void SignalGenerator::RenderAudioNoise() { 389 | uint32_t phase = state_.audio_phase; 390 | uint8_t count = kAudioBlockSize; 391 | uint16_t envelope_phase = state_.audio_envelope_phase; 392 | 393 | if (state_.trigger_count) { 394 | state_.trigger_count = 0; 395 | envelope_phase = 0; 396 | } 397 | 398 | uint16_t amplitude = 512; 399 | if (data_.audio.envelope_mode == AUDIO_ENVELOPE_MODE_GATE 400 | && !state_.gate_state) { 401 | amplitude = 0; 402 | } 403 | if (data_.audio.midi_mode == AUDIO_MIDI_MODE_ON || 404 | data_.audio.midi_mode == AUDIO_MIDI_MODE_GATE) { 405 | amplitude = note_stack_.size() ? 512 : 0; 406 | } 407 | 408 | while (count--) { 409 | phase = phase * 1664525L + 1013904223L; 410 | envelope_phase += 4; 411 | if (envelope_phase < 4) { 412 | envelope_phase = 0xffff; 413 | } 414 | if (data_.audio.envelope_mode == AUDIO_ENVELOPE_MODE_TRIGGER) { 415 | Word envelope_phi; 416 | envelope_phi.value = envelope_phase; 417 | uint16_t a = pgm_read_word(lut_envelope + envelope_phi.bytes[1]); 418 | uint16_t b = pgm_read_word(lut_envelope + envelope_phi.bytes[1] + 1); 419 | amplitude = a + S16U8MulShift8(b - a, envelope_phi.bytes[0]); 420 | } 421 | audio_buffer_.Overwrite(2048 + S16U16MulShift16(phase, amplitude)); 422 | } 423 | state_.audio_phase = phase; 424 | state_.audio_envelope_phase = envelope_phase; 425 | } 426 | 427 | /* static */ 428 | void SignalGenerator::RenderAudioSine() { 429 | uint32_t phase = state_.audio_phase; 430 | uint32_t increment = state_.audio_phase_increment; 431 | uint8_t count = kAudioBlockSize; 432 | uint16_t envelope_phase = state_.audio_envelope_phase; 433 | 434 | if (state_.trigger_count) { 435 | state_.trigger_count = 0; 436 | envelope_phase = 0; 437 | } 438 | 439 | uint16_t amplitude = 4095; 440 | if (data_.audio.envelope_mode == AUDIO_ENVELOPE_MODE_GATE 441 | && !state_.gate_state) { 442 | amplitude = 0; 443 | } 444 | if (data_.audio.midi_mode == AUDIO_MIDI_MODE_ON || 445 | data_.audio.midi_mode == AUDIO_MIDI_MODE_GATE) { 446 | amplitude = note_stack_.size() ? 4095 : 0; 447 | } 448 | 449 | while (count--) { 450 | phase += increment; 451 | LongWord phi; 452 | phi.value = phase; 453 | 454 | envelope_phase += 4; 455 | if (envelope_phase < 4) { 456 | envelope_phase = 0xffff; 457 | } 458 | 459 | uint16_t a = pgm_read_word(lut_sine + phi.bytes[3]); 460 | uint16_t b = pgm_read_word(lut_sine + phi.bytes[3] + 1); 461 | uint16_t sample = a + S16U8MulShift8(b - a, phi.bytes[2]); 462 | 463 | if (data_.audio.envelope_mode == AUDIO_ENVELOPE_MODE_TRIGGER) { 464 | Word envelope_phi; 465 | envelope_phi.value = envelope_phase; 466 | 467 | a = pgm_read_word(lut_envelope + envelope_phi.bytes[1]); 468 | b = pgm_read_word(lut_envelope + envelope_phi.bytes[1] + 1); 469 | amplitude = a + S16U8MulShift8(b - a, envelope_phi.bytes[0]); 470 | } 471 | audio_buffer_.Overwrite(2048 + S16U16MulShift16(sample + 32768, amplitude)); 472 | } 473 | state_.audio_phase = phase; 474 | state_.audio_envelope_phase = envelope_phase; 475 | } 476 | 477 | /* static */ 478 | void SignalGenerator::NoteOn(uint8_t note, uint8_t velocity) { 479 | note_stack_.NoteOn(note, velocity); 480 | if (data_.gate.midi_mode == GATE_MIDI_MODE_TRIGGER) { 481 | state_.gate_pulse = state_.gate_pulse_duration; 482 | } 483 | } 484 | 485 | /* static */ 486 | void SignalGenerator::NoteOff(uint8_t note) { 487 | note_stack_.NoteOff(note); 488 | } 489 | 490 | 491 | /* static */ 492 | const SignalGenerator::AudioRenderFn SignalGenerator::fn_table_[] PROGMEM = { 493 | &SignalGenerator::RenderAudioBandLimited, 494 | &SignalGenerator::RenderAudioBandLimited, 495 | &SignalGenerator::RenderAudioBandLimited, 496 | &SignalGenerator::RenderAudioSine, 497 | &SignalGenerator::RenderAudioNoise, 498 | &SignalGenerator::RenderAudioSine, 499 | }; 500 | 501 | } // namespace module_tester 502 | -------------------------------------------------------------------------------- /module_tester/signal_generator.h: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Emilie Gillet.. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | // ----------------------------------------------------------------------------- 17 | // 18 | // Signal generator. 19 | 20 | #ifndef MODULE_TESTER_SIGNAL_GENERATOR_H_ 21 | #define MODULE_TESTER_SIGNAL_GENERATOR_H_ 22 | 23 | #include "avrlib/base.h" 24 | #include "avrlib/ring_buffer.h" 25 | 26 | #include "module_tester/note_stack.h" 27 | 28 | namespace module_tester { 29 | 30 | enum ClockResolution { 31 | CLOCK_RESOLUTION_1_PPQN, 32 | CLOCK_RESOLUTION_4_PPQN, 33 | CLOCK_RESOLUTION_8_PPQN, 34 | CLOCK_RESOLUTION_24_PPQN 35 | }; 36 | 37 | enum PulseDuration { 38 | PULSE_DURATION_1_MS, 39 | PULSE_DURATION_2_MS, 40 | PULSE_DURATION_5_MS, 41 | PULSE_DURATION_10_MS, 42 | PULSE_DURATION_5_PERCENT, 43 | PULSE_DURATION_10_PERCENT, 44 | PULSE_DURATION_20_PERCENT, 45 | PULSE_DURATION_50_PERCENT, 46 | }; 47 | 48 | enum Period { 49 | PERIOD_10_MS, 50 | PERIOD_20_MS, 51 | PERIOD_50_MS, 52 | PERIOD_100_MS, 53 | PERIOD_200_MS, 54 | PERIOD_500_MS, 55 | PERIOD_1_S, 56 | PERIOD_2_S, 57 | PERIOD_5_S, 58 | PERIOD_10_S 59 | }; 60 | 61 | enum GateMidiMode { 62 | GATE_MIDI_MODE_OFF, 63 | GATE_MIDI_MODE_GATE, 64 | GATE_MIDI_MODE_TRIGGER, 65 | }; 66 | 67 | enum CvMode { 68 | CV_MODE_TRIANGLE_LFO, 69 | CV_MODE_SQUARE_LFO, 70 | CV_MODE_RAMP_UP_LFO, 71 | CV_MODE_RAMP_DOWN_LFO, 72 | CV_MODE_SINE_LFO, 73 | CV_MODE_1_OCTAVE_ARP, 74 | CV_MODE_2_OCTAVE_ARP, 75 | CV_MODE_CHROMATIC_SCALE_ARP, 76 | CV_MODE_C1_NOTE, 77 | CV_MODE_C3_NOTE 78 | }; 79 | 80 | enum CvRange { 81 | CV_RANGE_PM_1V, 82 | CV_RANGE_PM_2V, 83 | CV_RANGE_PM_5V, 84 | CV_RANGE_0_1V, 85 | CV_RANGE_0_2V, 86 | CV_RANGE_0_5V 87 | }; 88 | 89 | enum CvMidiMode { 90 | CV_MIDI_MODE_OFF, 91 | CV_MIDI_MODE_NOTE, 92 | CV_MIDI_MODE_VELOCITY, 93 | CV_MIDI_MODE_PITCH_BEND, 94 | CV_MIDI_MODE_MOD_WHEEL, 95 | }; 96 | 97 | enum AudioMode { 98 | AUDIO_MODE_SAW, 99 | AUDIO_MODE_SQUARE, 100 | AUDIO_MODE_TRIANGLE, 101 | AUDIO_MODE_SINE, 102 | AUDIO_MODE_NOISE 103 | }; 104 | 105 | enum AudioEnvelopeMode { 106 | AUDIO_ENVELOPE_MODE_OFF, 107 | AUDIO_ENVELOPE_MODE_GATE, 108 | AUDIO_ENVELOPE_MODE_TRIGGER, 109 | }; 110 | 111 | enum AudioMidiMode { 112 | AUDIO_MIDI_MODE_OFF, 113 | AUDIO_MIDI_MODE_NOTE, 114 | AUDIO_MIDI_MODE_GATE, 115 | AUDIO_MIDI_MODE_ON, 116 | }; 117 | 118 | struct ClockSettings { 119 | uint8_t tempo; 120 | ClockResolution resolution; 121 | PulseDuration pulse_duration; 122 | bool midi_mode; 123 | }; 124 | 125 | struct GateSettings { 126 | Period period; 127 | PulseDuration pulse_duration; 128 | GateMidiMode midi_mode; 129 | uint8_t padding; 130 | }; 131 | 132 | struct CvSettings { 133 | CvMode mode; 134 | Period period; 135 | CvRange range; 136 | CvMidiMode midi_mode; 137 | }; 138 | 139 | struct AudioSettings { 140 | AudioMode mode; 141 | uint8_t frequency; 142 | AudioEnvelopeMode envelope_mode; 143 | AudioMidiMode midi_mode; 144 | }; 145 | 146 | enum SignalGeneratorParameter { 147 | SG_PRM_CLOCK_TEMPO, 148 | SG_PRM_CLOCK_RESOLUTION, 149 | SG_PRM_CLOCK_PULSE_DURATION, 150 | SG_PRM_CLOCK_MIDI_MODE, 151 | 152 | SG_PRM_GATE_PERIOD, 153 | SG_PRM_GATE_PULSE_DURATION, 154 | SG_PRM_GATE_MIDI_MODE, 155 | SG_PRM_GATE_PADDING, 156 | 157 | SG_PRM_CV_MODE, 158 | SG_PRM_CV_PERIOD, 159 | SG_PRM_CV_RANGE, 160 | SG_PRM_CV_MIDI_MODE, 161 | 162 | SG_PRM_AUDIO_MODE, 163 | SG_PRM_AUDIO_FREQUENCY, 164 | SG_PRM_AUDIO_ENVELOPE_MODE, 165 | SG_PRM_AUDIO_MIDI_MODE, 166 | }; 167 | 168 | struct AudioBufferSpecs { 169 | typedef uint16_t Value; 170 | enum { 171 | buffer_size = 128, 172 | data_size = 16, 173 | }; 174 | }; 175 | 176 | struct CvBufferSpecs { 177 | typedef uint16_t Value; 178 | enum { 179 | buffer_size = 32, 180 | data_size = 16, 181 | }; 182 | }; 183 | 184 | 185 | static const uint8_t kAudioBlockSize = 40; 186 | static const uint8_t kCvBlockSize = 10; 187 | 188 | struct SignalGeneratorSettings { 189 | ClockSettings clock; 190 | GateSettings gate; 191 | CvSettings cv; 192 | AudioSettings audio; 193 | }; 194 | 195 | struct SignalGeneratorState { 196 | uint32_t clock_phase; 197 | uint32_t clock_phase_increment; 198 | 199 | uint32_t gate_phase; 200 | uint32_t gate_phase_increment; 201 | 202 | uint32_t cv_phase; 203 | uint32_t cv_phase_increment; 204 | 205 | uint32_t audio_phase; 206 | uint32_t audio_phase_increment; 207 | uint16_t audio_midi_note; 208 | 209 | uint16_t clock_pulse; 210 | uint16_t clock_pulse_duration; 211 | uint16_t clock_pulse_ratio; 212 | 213 | uint16_t gate_pulse; 214 | uint16_t gate_pulse_duration; 215 | uint16_t gate_pulse_ratio; 216 | 217 | uint16_t audio_envelope_phase; 218 | 219 | uint16_t clock_prescaler; 220 | uint8_t trigger_count; 221 | uint8_t gate_state; 222 | uint8_t last_midi_note; 223 | }; 224 | 225 | class SignalGenerator { 226 | public: 227 | typedef void (*AudioRenderFn)(); 228 | 229 | SignalGenerator() { } 230 | ~SignalGenerator() { } 231 | 232 | static void Init(); 233 | static void Render(); 234 | 235 | static uint16_t ReadAudioSample() { 236 | return audio_buffer_.ImmediateRead(); 237 | } 238 | 239 | static uint16_t ReadCvSample() { 240 | return cv_buffer_.ImmediateRead(); 241 | } 242 | 243 | static const SignalGeneratorSettings& data() { return data_; } 244 | static SignalGeneratorSettings* mutable_data() { return &data_; } 245 | 246 | static inline void RaiseClockPulse() { 247 | state_.clock_pulse = state_.clock_pulse_duration; 248 | } 249 | static inline bool DecreaseClockPulse() { 250 | bool result = false; 251 | if (state_.clock_pulse) { 252 | result = true; 253 | --state_.clock_pulse; 254 | } 255 | return result; 256 | } 257 | static inline uint8_t clock_prescaler() { return state_.clock_prescaler; } 258 | 259 | static void NoteOn(uint8_t note, uint8_t velocity); 260 | static void NoteOff(uint8_t note); 261 | static inline void PitchBend(uint16_t pitch_bend) { 262 | pitch_bend_ = pitch_bend; 263 | } 264 | static inline void Modulation(uint8_t modulation) { 265 | modulation_ = modulation; 266 | } 267 | static void Trigger() { 268 | ++state_.trigger_count; 269 | } 270 | static void set_gate_state(uint8_t gate_state) { 271 | state_.gate_state = gate_state; 272 | } 273 | 274 | private: 275 | static void UpdateIncrements(); 276 | 277 | static void RenderClock(); 278 | static void RenderGate(); 279 | static void RenderCv(); 280 | 281 | static void RenderAudioBandLimited(); 282 | static void RenderAudioSine(); 283 | static void RenderAudioNoise(); 284 | 285 | static uint16_t cv_samples_[kCvBlockSize]; 286 | static uint16_t last_note_value_; 287 | 288 | static SignalGeneratorSettings data_; 289 | static SignalGeneratorState state_; 290 | 291 | static avrlib::RingBuffer audio_buffer_; 292 | static avrlib::RingBuffer cv_buffer_; 293 | 294 | static uint16_t pitch_bend_; 295 | static uint8_t modulation_; 296 | 297 | static const AudioRenderFn fn_table_[]; 298 | 299 | static NoteStack<10> note_stack_; 300 | 301 | DISALLOW_COPY_AND_ASSIGN(SignalGenerator); 302 | }; 303 | 304 | extern SignalGenerator signal_generator; 305 | 306 | } // namespace module_tester 307 | 308 | #endif // MODULE_TESTER_SIGNAL_GENERATOR_H_ 309 | -------------------------------------------------------------------------------- /module_tester/ui.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Emilie Gillet.. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | // ----------------------------------------------------------------------------- 17 | // 18 | // User interface. 19 | 20 | #include "module_tester/ui.h" 21 | 22 | #include "avrlib/string.h" 23 | 24 | #include "module_tester/display.h" 25 | #include "module_tester/frequency_counter.h" 26 | #include "module_tester/parameter.h" 27 | #include "module_tester/pulse_counter.h" 28 | #include "module_tester/signal_generator.h" 29 | 30 | namespace module_tester { 31 | 32 | const prog_PageInfo page_registry[] PROGMEM = { 33 | // Clock output 34 | { 35 | 4, 36 | PRM_CLOCK_TEMPO 37 | }, 38 | // Gate output 39 | { 40 | 3, 41 | PRM_GATE_PERIOD 42 | }, 43 | // CV output 44 | { 45 | 4, 46 | PRM_CV_MODE 47 | }, 48 | // Audio output 49 | { 50 | 4, 51 | PRM_SYNTH_MODE 52 | }, 53 | // Audio input 54 | { 55 | 1, 56 | PRM_FREQUENCY_COUNTER_UNIT 57 | }, 58 | // Gate input 59 | { 60 | 2, 61 | PRM_PULSE_COUNTER_UNIT 62 | } 63 | }; 64 | 65 | /* */ 66 | uint8_t Ui::cycle_; 67 | bool Ui::editing_; 68 | bool Ui::gate_; 69 | UiPage Ui::page_; 70 | PageInfo Ui::page_info_; 71 | int8_t Ui::active_control_; 72 | int8_t Ui::scrolling_; 73 | Encoder Ui::encoder_; 74 | Switches Ui::switches_; 75 | Leds Ui::leds_; 76 | EventQueue<8> Ui::queue_; 77 | /* */ 78 | 79 | /* extern */ 80 | Ui ui; 81 | 82 | /* static */ 83 | void Ui::Init() { 84 | encoder_.Init(); 85 | switches_.Init(); 86 | leds_.Init(); 87 | lcd.Init(); 88 | display.Init(); 89 | ChangePage(UI_PAGE_CLOCK_OUT); 90 | display.Clear(); 91 | lcd.SetCustomCharMapRes(&chr_special_characters[0], 7, 1); 92 | RedrawScreen(); 93 | } 94 | 95 | /* static */ 96 | void Ui::OnClick() { 97 | editing_ = !editing_; 98 | } 99 | 100 | /* static */ 101 | void Ui::OnIncrement(int8_t increment) { 102 | if (!editing_) { 103 | // Scroll to the next control. 104 | active_control_ += increment; 105 | if (active_control_ < 0) { 106 | active_control_ = 0; 107 | } else if (active_control_ >= page_info_.num_parameters) { 108 | active_control_ = page_info_.num_parameters - 1; 109 | } 110 | // Scroll the screen if necessary. 111 | if (active_control_ >= scrolling_ + kLcdHeight) { 112 | scrolling_ = active_control_ - kLcdHeight + 1; 113 | } else if (active_control_ < scrolling_) { 114 | scrolling_ = active_control_; 115 | } 116 | } else { 117 | const Parameter& p = parameter_manager.parameter( 118 | page_info_.first_parameter + active_control_); 119 | parameter_manager.Increment(p, increment); 120 | } 121 | } 122 | 123 | /* static */ 124 | void Ui::RedrawScreen() { 125 | if (page_ < UI_PAGE_AUDIO_IN) { 126 | RedrawScreenOutput(); 127 | } else if (page_ == UI_PAGE_AUDIO_IN) { 128 | RedrawScreenAudioIn(); 129 | } else if (page_ == UI_PAGE_GATE_IN) { 130 | RedrawScreenGateIn(); 131 | } 132 | } 133 | 134 | /* static */ 135 | void Ui::RedrawScreenOutput() { 136 | char line_buffer[kLcdWidth + 1]; 137 | 138 | for (uint8_t i = 0; i < kLcdHeight; ++i) { 139 | uint8_t control_id = i + scrolling_; 140 | 141 | memset(line_buffer, ' ', sizeof(line_buffer)); 142 | line_buffer[kLcdWidth] = '\0'; 143 | if (control_id == active_control_ && !editing_) { 144 | line_buffer[0] = '>'; 145 | } 146 | if (page_ <= UI_PAGE_AUDIO_OUT) { 147 | const Parameter& p = parameter_manager.parameter( 148 | page_info_.first_parameter + control_id); 149 | p.Format( 150 | parameter_manager.GetValue(p), 151 | editing_ && control_id == active_control_ ? '>' : ' ', 152 | line_buffer + 1, 153 | kLcdWidth - 1); 154 | } 155 | display.Print(i, line_buffer); 156 | } 157 | } 158 | 159 | /* static */ 160 | void Ui::RedrawScreenAudioIn() { 161 | char line_buffer[kLcdWidth + 1]; 162 | line_buffer[0] = (active_control_ == 0 && !editing_) ? '>' : ' '; 163 | frequency_counter.Print(line_buffer + 1, kLcdWidth - 1); 164 | display.Print(0, line_buffer); 165 | } 166 | 167 | /* static */ 168 | void Ui::RedrawScreenGateIn() { 169 | char line_buffer[kLcdWidth + 1]; 170 | line_buffer[0] = (active_control_ == 0 && !editing_) ? '>' : ' '; 171 | pulse_counter.PrintPeriod(line_buffer + 1, kLcdWidth - 1); 172 | display.Print(0, line_buffer); 173 | 174 | memset(line_buffer, ' ', sizeof(line_buffer)); 175 | line_buffer[0] = (active_control_ == 1 && !editing_) ? '>' : ' '; 176 | pulse_counter.PrintDuration(line_buffer + 1, kLcdWidth - 1); 177 | display.Print(1, line_buffer); 178 | } 179 | 180 | /* static */ 181 | void Ui::ChangePage(UiPage page) { 182 | page_ = page; 183 | memcpy_P(&page_info_, &page_registry[page], sizeof(PageInfo)); 184 | scrolling_ = 0; 185 | active_control_ = 0; 186 | editing_ = false; 187 | } 188 | 189 | /* static */ 190 | void Ui::Poll() { 191 | ++cycle_; 192 | // I 193 | int8_t increment = encoder_.Read(); 194 | if (increment != 0) { 195 | queue_.AddEvent(CONTROL_ENCODER, 0, increment); 196 | } 197 | if (encoder_.clicked()) { 198 | queue_.AddEvent(CONTROL_ENCODER_CLICK, 0, 1); 199 | } 200 | 201 | if ((cycle_ & 0x3) == 0) { 202 | switches_.Read(); 203 | for (uint8_t i = 0; i < 6; ++i) { 204 | if (switches_.raised(i)) { 205 | queue_.AddEvent(CONTROL_SWITCH, 5 - i, 1); 206 | } 207 | } 208 | uint8_t leds_byte = 1 << page_; 209 | if (gate_) { 210 | leds_byte |= 1 << UI_PAGE_GATE_IN; 211 | gate_ = false; 212 | } 213 | leds_.Write(leds_byte); 214 | } 215 | lcd.Tick(); 216 | } 217 | 218 | /* static */ 219 | void Ui::DoEvents() { 220 | display.Tick(); 221 | 222 | bool redraw = false; 223 | while (queue_.available()) { 224 | Event e = queue_.PullEvent(); 225 | queue_.Touch(); 226 | 227 | switch (e.control_type) { 228 | case CONTROL_ENCODER_CLICK: 229 | OnClick(); 230 | break; 231 | 232 | case CONTROL_ENCODER: 233 | OnIncrement(e.value); 234 | break; 235 | 236 | case CONTROL_SWITCH: 237 | ChangePage(static_cast(e.control_id)); 238 | break; 239 | } 240 | redraw = true; 241 | } 242 | 243 | if (queue_.idle_time_ms() > 1000) { 244 | queue_.Touch(); 245 | redraw = true; 246 | } 247 | 248 | if (queue_.idle_time_ms() > 100 && page_ >= UI_PAGE_AUDIO_IN) { 249 | // Redraw measurement page at 10 Hz. 250 | queue_.Touch(); 251 | redraw = true; 252 | } 253 | 254 | if (redraw) { 255 | display.Clear(); 256 | RedrawScreen(); 257 | } 258 | } 259 | 260 | } // namespace ambika 261 | -------------------------------------------------------------------------------- /module_tester/ui.h: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Emilie Gillet.. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | // ----------------------------------------------------------------------------- 17 | // 18 | // User interface. 19 | 20 | #ifndef MODULE_TESTER_UI_H_ 21 | #define MODULE_TESTER_UI_H_ 22 | 23 | #include "avrlib/base.h" 24 | 25 | #include "avrlib/devices/pot_scanner.h" 26 | #include "avrlib/devices/rotary_encoder.h" 27 | #include "avrlib/devices/switch.h" 28 | #include "avrlib/ui/event_queue.h" 29 | 30 | #include "module_tester/hardware_config.h" 31 | // #include "controller/resources.h" 32 | 33 | namespace module_tester { 34 | 35 | using namespace avrlib; 36 | 37 | typedef RotaryEncoder Encoder; 38 | typedef DebouncedSwitches Switches; 39 | typedef ShiftRegisterOutput Leds; 40 | 41 | enum UiPage { 42 | UI_PAGE_CLOCK_OUT, 43 | UI_PAGE_GATE_OUT, 44 | UI_PAGE_CV_OUT, 45 | UI_PAGE_AUDIO_OUT, 46 | UI_PAGE_AUDIO_IN, 47 | UI_PAGE_GATE_IN 48 | }; 49 | 50 | struct PageInfo { 51 | uint8_t num_parameters; 52 | uint8_t first_parameter; 53 | }; 54 | 55 | typedef PageInfo PROGMEM prog_PageInfo; 56 | 57 | class Ui { 58 | public: 59 | Ui() { } 60 | 61 | static void Init(); 62 | static void Poll(); 63 | static void DoEvents(); 64 | static void FlushEvents() { 65 | queue_.Flush(); 66 | } 67 | static void OnClick(); 68 | static void OnIncrement(int8_t increment); 69 | 70 | static void ChangePage(UiPage page); 71 | static void RedrawScreen(); 72 | 73 | static uint8_t has_events() { 74 | return queue_.available(); 75 | } 76 | 77 | static void set_gate(bool gate) { 78 | if (!gate_) { 79 | gate_ = gate; 80 | } 81 | } 82 | 83 | private: 84 | static void RedrawScreenOutput(); 85 | static void RedrawScreenAudioIn(); 86 | static void RedrawScreenGateIn(); 87 | 88 | static uint8_t cycle_; 89 | 90 | static bool editing_; 91 | static bool gate_; 92 | 93 | static int8_t scrolling_; 94 | static int8_t active_control_; 95 | static PageInfo page_info_; 96 | static UiPage page_; 97 | static Encoder encoder_; 98 | static Switches switches_; 99 | static Leds leds_; 100 | static EventQueue<8> queue_; 101 | 102 | DISALLOW_COPY_AND_ASSIGN(Ui); 103 | }; 104 | 105 | extern Ui ui; 106 | 107 | } // namespace module_tester 108 | 109 | #endif // MODULE_TESTER_UI_H_ 110 | --------------------------------------------------------------------------------