From 6ed0f3ae4b2ac6b73420513f3b111d1a04fe913f Mon Sep 17 00:00:00 2001 From: Jay Robson Date: Mon, 26 Aug 2024 13:02:17 +1000 Subject: [PATCH] use integer math only for tone generation --- dac.hpp | 4 +- data.cpp | 112 +++++++++++++++++++++++++++++++++++++++++++++ data.hpp | 12 +++++ eeprom.cpp | 33 ------------- eeprom.hpp | 10 ---- entry.hpp | 4 +- indicator.cpp | 4 +- scheduler.cpp | 18 ++++---- tone-generator.ino | 17 +++++-- tone.cpp | 10 ++++ tone.hpp | 29 +++--------- 11 files changed, 168 insertions(+), 85 deletions(-) create mode 100644 data.cpp create mode 100644 data.hpp delete mode 100644 eeprom.cpp delete mode 100644 eeprom.hpp create mode 100644 tone.cpp diff --git a/dac.hpp b/dac.hpp index bd28d1b..eb487a3 100644 --- a/dac.hpp +++ b/dac.hpp @@ -9,8 +9,8 @@ namespace dac { DDRD = 0xff; } - inline void set(float v_f) { - PORTD = clamp((int)(v_f * -128) + 127, 0, 255); + inline void set(int v) { + PORTD = clamp(v, 0, 255); } }; diff --git a/data.cpp b/data.cpp new file mode 100644 index 0000000..c623e48 --- /dev/null +++ b/data.cpp @@ -0,0 +1,112 @@ + +#include +#include +#include +#include "midi_data.hpp" +#include "data.hpp" + +const int SERIAL_RX = A4; +const int SERIAL_TX = A5; + +enum Mode : uint8_t { + none, + ready, + local, + serial, + read, + jump, +}; + +static char buff[64]; +static bool buff_full = false; +static unsigned buff_at = 0; +static unsigned address = 0; +static SoftwareSerial serial_dev(SERIAL_RX, SERIAL_TX); +static Mode mode = Mode::local; + +void data::init() { + pinMode(SERIAL_RX, INPUT); + pinMode(SERIAL_TX, OUTPUT); + serial_dev.begin(57600); + serial_dev.listen(); + serial_dev.write(Mode::ready); + + unsigned long ts_end = millis() + 1000; + while(ts_end > millis()) { + if(check()) { + break; + } + } +} + +bool data::check() { + if(serial_dev.available()) { + mode = serial_dev.read(); + return true; + } else { + return false; + } +} + +void data::jump(unsigned long loc) { + switch(mode) { + case Mode::local: + address = loc; + break; + case Mode::serial: + serial_dev.write(Mode::jump); + serial_dev.write(loc >> 24); + serial_dev.write(loc >> 16); + serial_dev.write(loc >> 8); + serial_dev.write(loc); + break; + } +} + +inline uint8_t proc_len(uint8_t len) { + if(address + len >= sizeof(MIDI)) { + return sizeof(MIDI) - address; + } else { + return len; + } +} + +static void fill_buffer() { + serial_dev.write(Mode::read); + serial_dev.write(sizeof(buff)); + for(int i = 0; i < sizeof(buff); i++) { + while(!serial_dev.available()) {} + buff[i] = serial_dev.read(); + } + buff_at = 0; + buff_full = true; +} + +void data::read(void* data, uint8_t len) { + char* bytes = (char*)data; + switch(mode) { + case Mode::local: + len = proc_len(len); + memcpy_P(bytes, &MIDI[address], len); + address += len; + break; + case Mode::serial: + if(!buff_full) { + fill_buffer(); + } + while(sizeof(buff) - buff_at < len) { + auto l = sizeof(buff) - buff_at; + memcpy(bytes, buff + buff_at, l); + bytes += l; + len -= l; + fill_buffer(); + } + memcpy(bytes, buff + buff_at, len); + buff_at += len; + break; + } +} + +void data::write(const void* data, uint8_t len) { +} + diff --git a/data.hpp b/data.hpp new file mode 100644 index 0000000..f68a40b --- /dev/null +++ b/data.hpp @@ -0,0 +1,12 @@ + +#pragma once + +namespace data { + + void init(); + bool check(); + void jump(unsigned long loc = 0); + void read(void* data, uint8_t len); + void write(const void* data, uint8_t len); +}; + diff --git a/eeprom.cpp b/eeprom.cpp deleted file mode 100644 index efb3111..0000000 --- a/eeprom.cpp +++ /dev/null @@ -1,33 +0,0 @@ - -#include -#include "midi_data.hpp" -#include "eeprom.hpp" - -static int address = 0; - -void eeprom::jump(int loc) { - address = loc; -} - -inline size_t proc_len(size_t len) { - if(address + len >= sizeof(MIDI)) { - return sizeof(MIDI) - address; - } else { - return len; - } -} - -void eeprom::read(void* data, size_t len) { - char* bytes = (char*)data; - len = proc_len(len); - memcpy_P(data, &MIDI[address], len); - address += len; -} - -void eeprom::write(const void* data, size_t len) { - const char* bytes = (const char*)data; - len = proc_len(len); - address += len; -} - - diff --git a/eeprom.hpp b/eeprom.hpp deleted file mode 100644 index ca6a623..0000000 --- a/eeprom.hpp +++ /dev/null @@ -1,10 +0,0 @@ - -#pragma once - -namespace eeprom { - - void jump(int loc = 0); - void read(void* data, size_t len); - void write(const void* data, size_t len); -}; - diff --git a/entry.hpp b/entry.hpp index 1ed7c1a..82e6630 100644 --- a/entry.hpp +++ b/entry.hpp @@ -42,7 +42,7 @@ namespace entry { struct Config { uint32_t us_per_tick; - float amplitude; + uint8_t amplitude; }; struct Generic { @@ -68,7 +68,7 @@ namespace entry { case Type::clear_ts: return 4; case Type::config: - return 8; + return 5; case Type::stop: default: return 1; diff --git a/indicator.cpp b/indicator.cpp index 08e4e70..53e4c91 100644 --- a/indicator.cpp +++ b/indicator.cpp @@ -10,10 +10,10 @@ void indicator::init() { } void indicator::update() { - float v = 0; + unsigned v = 0; for(int i = 0; i < tones::active; i++) { v += tones::all[i].amplitude; } - analogWrite(LED_INDICATOR, min((int)(v * 256), 255)); + analogWrite(LED_INDICATOR, min(v, 255)); } diff --git a/scheduler.cpp b/scheduler.cpp index 3e95eec..474f248 100644 --- a/scheduler.cpp +++ b/scheduler.cpp @@ -2,7 +2,7 @@ #include #include "timing.hpp" #include "scheduler.hpp" -#include "eeprom.hpp" +#include "data.hpp" #include "entry.hpp" #include "tones.hpp" #include "util.hpp" @@ -10,13 +10,13 @@ static uint32_t ts_init = 0; static uint32_t ts_last = 0; struct { - float amplitude = 1; + uint8_t amplitude = 1; uint32_t us_per_tick = 1; } config; struct { uint8_t index; + uint8_t amplitude; uint16_t frequency; - float amplitude; bool active = false; } next; @@ -25,7 +25,7 @@ constexpr uint64_t bm(int n) { } void scheduler::init() { - eeprom::jump(0); + data::jump(0); tones::clear_all(); ts_init = micros(); running = true; @@ -46,11 +46,11 @@ void scheduler::do_next() { uint64_t v = 0; uint8_t buff[8]; - eeprom::read(buff, 1); + data::read(buff, 1); entry::Type type = buff[0] >> 5; int entry_size = entry::get_size(type); - eeprom::read(buff + 1, entry_size - 1); + data::read(buff + 1, entry_size - 1); for(int i = 0; i < entry_size; i++) { v = (v << 8) | buff[i]; @@ -58,8 +58,8 @@ void scheduler::do_next() { switch(type) { case entry::Type::config: { - config.amplitude = *(float*)(buff + 4); - v >>= 32; + config.amplitude = v & bm(8); + v >>= 8; config.us_per_tick = v & bm(29); ts_init = micros(); break; @@ -118,7 +118,7 @@ void scheduler::do_all() { } void scheduler::clear() { - eeprom::jump(0); + data::jump(0); ts_last = 0; } diff --git a/tone-generator.ino b/tone-generator.ino index 18d71f8..ffdbea4 100644 --- a/tone-generator.ino +++ b/tone-generator.ino @@ -6,6 +6,7 @@ #include "util.hpp" #include "scheduler.hpp" #include "indicator.hpp" +#include "data.hpp" static unsigned long int micros_at = 0; static bool led_state = true; @@ -18,21 +19,27 @@ inline unsigned long micros_diff() { } void setup() { + Tone::init(); dac::init(); + data::init(); tones::init(); scheduler::init(); indicator::init(); } void loop() { - if(scheduler::running && timing::at >= scheduler::timestamp) { - scheduler::do_all(); - indicator::update(); + if(scheduler::running) { + if(timing::at >= scheduler::timestamp) { + scheduler::do_all(); + indicator::update(); + } + } else { + data::check(); } timing::update(); - float value = 0; + int32_t value = 0; uint32_t passed = timing::diff; for(int i = 0; i < tones::active; i++) { @@ -41,6 +48,6 @@ void loop() { value += t.get(); } - dac::set(value); + dac::set((value >> 8) + 127); } diff --git a/tone.cpp b/tone.cpp new file mode 100644 index 0000000..378c85e --- /dev/null +++ b/tone.cpp @@ -0,0 +1,10 @@ + +#include +#include "tone.hpp" + +void Tone::init() { + for(unsigned i = 0; i < sizeof(sin_lookup); i++) { + sin_lookup[i] = round(sin((float)i / sizeof(sin_lookup) * M_PI * 2) * 127); + } +} + diff --git a/tone.hpp b/tone.hpp index d347071..bee904b 100644 --- a/tone.hpp +++ b/tone.hpp @@ -4,29 +4,14 @@ #include #include -inline const PROGMEM float SIN[] = { - 0.000000, 0.024541, 0.049068, 0.073565, 0.098017, 0.122411, 0.146730, 0.170962, 0.195090, 0.219101, 0.242980, 0.266713, 0.290285, 0.313682, 0.336890, 0.359895, - 0.382683, 0.405241, 0.427555, 0.449611, 0.471397, 0.492898, 0.514103, 0.534998, 0.555570, 0.575808, 0.595699, 0.615232, 0.634393, 0.653173, 0.671559, 0.689541, - 0.707107, 0.724247, 0.740951, 0.757209, 0.773010, 0.788346, 0.803208, 0.817585, 0.831470, 0.844854, 0.857729, 0.870087, 0.881921, 0.893224, 0.903989, 0.914210, - 0.923880, 0.932993, 0.941544, 0.949528, 0.956940, 0.963776, 0.970031, 0.975702, 0.980785, 0.985278, 0.989177, 0.992480, 0.995185, 0.997290, 0.998795, 0.999699, - 1.000000, 0.999699, 0.998795, 0.997290, 0.995185, 0.992480, 0.989177, 0.985278, 0.980785, 0.975702, 0.970031, 0.963776, 0.956940, 0.949528, 0.941544, 0.932993, - 0.923880, 0.914210, 0.903989, 0.893224, 0.881921, 0.870087, 0.857729, 0.844854, 0.831470, 0.817585, 0.803208, 0.788346, 0.773010, 0.757209, 0.740951, 0.724247, - 0.707107, 0.689541, 0.671559, 0.653173, 0.634393, 0.615232, 0.595699, 0.575808, 0.555570, 0.534998, 0.514103, 0.492898, 0.471397, 0.449611, 0.427555, 0.405241, - 0.382683, 0.359895, 0.336890, 0.313682, 0.290285, 0.266713, 0.242980, 0.219101, 0.195090, 0.170962, 0.146730, 0.122411, 0.098017, 0.073565, 0.049068, 0.024541, - 0.000000, -0.024541, -0.049068, -0.073565, -0.098017, -0.122411, -0.146730, -0.170962, -0.195090, -0.219101, -0.242980, -0.266713, -0.290285, -0.313682, -0.336890, -0.359895, - -0.382683, -0.405241, -0.427555, -0.449611, -0.471397, -0.492898, -0.514103, -0.534998, -0.555570, -0.575808, -0.595699, -0.615232, -0.634393, -0.653173, -0.671559, -0.689541, - -0.707107, -0.724247, -0.740951, -0.757209, -0.773010, -0.788346, -0.803208, -0.817585, -0.831470, -0.844854, -0.857729, -0.870087, -0.881921, -0.893224, -0.903989, -0.914210, - -0.923880, -0.932993, -0.941544, -0.949528, -0.956940, -0.963776, -0.970031, -0.975702, -0.980785, -0.985278, -0.989177, -0.992480, -0.995185, -0.997290, -0.998795, -0.999699, - -1.000000, -0.999699, -0.998795, -0.997290, -0.995185, -0.992480, -0.989177, -0.985278, -0.980785, -0.975702, -0.970031, -0.963776, -0.956940, -0.949528, -0.941544, -0.932993, - -0.923880, -0.914210, -0.903989, -0.893224, -0.881921, -0.870087, -0.857729, -0.844854, -0.831470, -0.817585, -0.803208, -0.788346, -0.773010, -0.757209, -0.740951, -0.724247, - -0.707107, -0.689541, -0.671559, -0.653173, -0.634393, -0.615232, -0.595699, -0.575808, -0.555570, -0.534998, -0.514103, -0.492898, -0.471397, -0.449611, -0.427555, -0.405241, - -0.382683, -0.359895, -0.336890, -0.313682, -0.290285, -0.266713, -0.242980, -0.219101, -0.195090, -0.170962, -0.146730, -0.122411, -0.098017, -0.073565, -0.049068, -0.024541, -}; - struct Tone { uint32_t phase; uint16_t frequency; - float amplitude; + uint8_t amplitude; + + inline static int8_t sin_lookup[256]; + + static void init(); inline bool active() const { return amplitude > 0; @@ -36,9 +21,9 @@ struct Tone { phase += t * frequency; } - inline float get() const { + inline int get() const { uint8_t id = phase >> 12; - return pgm_read_float(&SIN[id]) * amplitude; + return (int)sin_lookup[id] * amplitude; } };