├── .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 |
--------------------------------------------------------------------------------