use integer math only for tone generation

This commit is contained in:
Jay Robson 2024-08-26 13:02:17 +10:00
parent 31aa8beefb
commit 6ed0f3ae4b
11 changed files with 168 additions and 85 deletions

View File

@ -9,8 +9,8 @@ namespace dac {
DDRD = 0xff; DDRD = 0xff;
} }
inline void set(float v_f) { inline void set(int v) {
PORTD = clamp((int)(v_f * -128) + 127, 0, 255); PORTD = clamp(v, 0, 255);
} }
}; };

112
data.cpp Normal file
View File

@ -0,0 +1,112 @@
#include <Arduino.h>
#include <SoftwareSerial.h>
#include <avr/pgmspace.h>
#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) {
}

12
data.hpp Normal file
View File

@ -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);
};

View File

@ -1,33 +0,0 @@
#include <avr/pgmspace.h>
#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;
}

View File

@ -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);
};

View File

@ -42,7 +42,7 @@ namespace entry {
struct Config { struct Config {
uint32_t us_per_tick; uint32_t us_per_tick;
float amplitude; uint8_t amplitude;
}; };
struct Generic { struct Generic {
@ -68,7 +68,7 @@ namespace entry {
case Type::clear_ts: case Type::clear_ts:
return 4; return 4;
case Type::config: case Type::config:
return 8; return 5;
case Type::stop: case Type::stop:
default: default:
return 1; return 1;

View File

@ -10,10 +10,10 @@ void indicator::init() {
} }
void indicator::update() { void indicator::update() {
float v = 0; unsigned v = 0;
for(int i = 0; i < tones::active; i++) { for(int i = 0; i < tones::active; i++) {
v += tones::all[i].amplitude; v += tones::all[i].amplitude;
} }
analogWrite(LED_INDICATOR, min((int)(v * 256), 255)); analogWrite(LED_INDICATOR, min(v, 255));
} }

View File

@ -2,7 +2,7 @@
#include <Arduino.h> #include <Arduino.h>
#include "timing.hpp" #include "timing.hpp"
#include "scheduler.hpp" #include "scheduler.hpp"
#include "eeprom.hpp" #include "data.hpp"
#include "entry.hpp" #include "entry.hpp"
#include "tones.hpp" #include "tones.hpp"
#include "util.hpp" #include "util.hpp"
@ -10,13 +10,13 @@
static uint32_t ts_init = 0; static uint32_t ts_init = 0;
static uint32_t ts_last = 0; static uint32_t ts_last = 0;
struct { struct {
float amplitude = 1; uint8_t amplitude = 1;
uint32_t us_per_tick = 1; uint32_t us_per_tick = 1;
} config; } config;
struct { struct {
uint8_t index; uint8_t index;
uint8_t amplitude;
uint16_t frequency; uint16_t frequency;
float amplitude;
bool active = false; bool active = false;
} next; } next;
@ -25,7 +25,7 @@ constexpr uint64_t bm(int n) {
} }
void scheduler::init() { void scheduler::init() {
eeprom::jump(0); data::jump(0);
tones::clear_all(); tones::clear_all();
ts_init = micros(); ts_init = micros();
running = true; running = true;
@ -46,11 +46,11 @@ void scheduler::do_next() {
uint64_t v = 0; uint64_t v = 0;
uint8_t buff[8]; uint8_t buff[8];
eeprom::read(buff, 1); data::read(buff, 1);
entry::Type type = buff[0] >> 5; entry::Type type = buff[0] >> 5;
int entry_size = entry::get_size(type); 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++) { for(int i = 0; i < entry_size; i++) {
v = (v << 8) | buff[i]; v = (v << 8) | buff[i];
@ -58,8 +58,8 @@ void scheduler::do_next() {
switch(type) { switch(type) {
case entry::Type::config: { case entry::Type::config: {
config.amplitude = *(float*)(buff + 4); config.amplitude = v & bm(8);
v >>= 32; v >>= 8;
config.us_per_tick = v & bm(29); config.us_per_tick = v & bm(29);
ts_init = micros(); ts_init = micros();
break; break;
@ -118,7 +118,7 @@ void scheduler::do_all() {
} }
void scheduler::clear() { void scheduler::clear() {
eeprom::jump(0); data::jump(0);
ts_last = 0; ts_last = 0;
} }

View File

@ -6,6 +6,7 @@
#include "util.hpp" #include "util.hpp"
#include "scheduler.hpp" #include "scheduler.hpp"
#include "indicator.hpp" #include "indicator.hpp"
#include "data.hpp"
static unsigned long int micros_at = 0; static unsigned long int micros_at = 0;
static bool led_state = true; static bool led_state = true;
@ -18,21 +19,27 @@ inline unsigned long micros_diff() {
} }
void setup() { void setup() {
Tone::init();
dac::init(); dac::init();
data::init();
tones::init(); tones::init();
scheduler::init(); scheduler::init();
indicator::init(); indicator::init();
} }
void loop() { void loop() {
if(scheduler::running && timing::at >= scheduler::timestamp) { if(scheduler::running) {
scheduler::do_all(); if(timing::at >= scheduler::timestamp) {
indicator::update(); scheduler::do_all();
indicator::update();
}
} else {
data::check();
} }
timing::update(); timing::update();
float value = 0; int32_t value = 0;
uint32_t passed = timing::diff; uint32_t passed = timing::diff;
for(int i = 0; i < tones::active; i++) { for(int i = 0; i < tones::active; i++) {
@ -41,6 +48,6 @@ void loop() {
value += t.get(); value += t.get();
} }
dac::set(value); dac::set((value >> 8) + 127);
} }

10
tone.cpp Normal file
View File

@ -0,0 +1,10 @@
#include <math.h>
#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);
}
}

View File

@ -4,29 +4,14 @@
#include <inttypes.h> #include <inttypes.h>
#include <avr/pgmspace.h> #include <avr/pgmspace.h>
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 { struct Tone {
uint32_t phase; uint32_t phase;
uint16_t frequency; uint16_t frequency;
float amplitude; uint8_t amplitude;
inline static int8_t sin_lookup[256];
static void init();
inline bool active() const { inline bool active() const {
return amplitude > 0; return amplitude > 0;
@ -36,9 +21,9 @@ struct Tone {
phase += t * frequency; phase += t * frequency;
} }
inline float get() const { inline int get() const {
uint8_t id = phase >> 12; uint8_t id = phase >> 12;
return pgm_read_float(&SIN[id]) * amplitude; return (int)sin_lookup[id] * amplitude;
} }
}; };