use integer math only for tone generation
This commit is contained in:
parent
31aa8beefb
commit
6ed0f3ae4b
4
dac.hpp
4
dac.hpp
|
@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
};
|
||||||
|
|
33
eeprom.cpp
33
eeprom.cpp
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
10
eeprom.hpp
10
eeprom.hpp
|
@ -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);
|
|
||||||
};
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
if(timing::at >= scheduler::timestamp) {
|
||||||
scheduler::do_all();
|
scheduler::do_all();
|
||||||
indicator::update();
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
29
tone.hpp
29
tone.hpp
|
@ -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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue