mirror of
https://github.com/sjlongland/atinysynth.git
synced 2025-09-13 10:03:15 +10:00
I left this out because I thought the idea of modulus and division on a MCU that lacks a hardware multiplier would be too much for it… and indeed it was too much with my other project. Thinking about it this afternoon, I had an idea. If I have 2^N samples, then the modulus can be optimised to: ``` mod = sample & ((2**n)-1) ``` and the segment can be figured out by: ``` segment = sample >> n ``` The segment is two bits. A function that returns the scaled sine value for a given scaled angle can be given as: ``` int8_t fp_sine(uint8_t angle) { uint8_t segment = (angle >> POLY_SINE_SZ_BITS) & 3; uint8_t offset = angle & ((1 << POLY_SINE_SZ_BITS)-1); switch (segment) { case 0: return _poly_sine[offset]; case 1: return _poly_sine[ POLY_SINE_SZ - offset]; case 2: return -_poly_sine[offset]; case 3: return -_poly_sine[ POLY_SINE_SZ - offset]; } } ``` … or something like that. If `POLY_SINE_SZ_BITS=6`, then `angle=255` represents 360°.
91 lines
2.6 KiB
Python
91 lines
2.6 KiB
Python
#!/usr/bin/env python
|
|
|
|
"""
|
|
Polyphonic synthesizer for microcontrollers: Sine look-up table generator
|
|
(C) 2017 Stuart Longland
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
|
MA 02110-1301 USA
|
|
"""
|
|
|
|
import math
|
|
import textwrap
|
|
import argparse
|
|
|
|
# Arguments
|
|
parser = argparse.ArgumentParser(
|
|
description='Generate a sine wave look-up table in C'
|
|
)
|
|
parser.add_argument('--num-samples-bits',
|
|
help='--num-samples is in bits, so # samples = 2^n, allowing a '\
|
|
'bitwise AND to be used for modulus operations and bit '\
|
|
'shifting for segment decoding.',
|
|
action='store_const', default=False, const=True)
|
|
parser.add_argument('--num-samples',
|
|
help='Number of samples to generate',
|
|
type=int, default=10)
|
|
parser.add_argument('--amplitude',
|
|
help='Amplitude of sine to generate',
|
|
type=int, default=255)
|
|
parser.add_argument('--data-type',
|
|
help='Data type of sine samples',
|
|
type=str, default='uint8_t')
|
|
args = parser.parse_args()
|
|
|
|
# Number of samples
|
|
if args.num_samples_bits:
|
|
num_samples = 2 ** args.num_samples
|
|
else:
|
|
num_samples = args.num_samples
|
|
|
|
# Step
|
|
step_angle = math.pi / (2*num_samples)
|
|
|
|
# Samples
|
|
samples = [int(args.amplitude*math.sin(sample * step_angle))
|
|
for sample in range(0, num_samples)]
|
|
|
|
# Output C file
|
|
print ("""#ifndef _GEN_SINE_C
|
|
#define _GEN_SINE_C
|
|
|
|
/* Generated sine wave */
|
|
#include <stdint.h>
|
|
#define POLY_SINE_SZ (%(N_SAMPLES)d)
|
|
#define POLY_SINE_SZ_BITS (%(N_BITS)d)
|
|
static const %(DATA_TYPE)s _poly_sine[POLY_SINE_SZ]
|
|
#ifdef __AVR_ARCH__
|
|
PROGMEM
|
|
#endif
|
|
= {
|
|
%(SAMPLES)s
|
|
};
|
|
#endif""" % {
|
|
'N_SAMPLES': num_samples,
|
|
'N_BITS': args.num_samples if args.num_samples_bits else 0,
|
|
'DATA_TYPE': args.data_type,
|
|
'SAMPLES': '\n'.join(
|
|
textwrap.TextWrapper(
|
|
width=78,
|
|
initial_indent=' ',
|
|
subsequent_indent=' '
|
|
).wrap(
|
|
', '.join([
|
|
('0x%02x' % sample) for sample in samples
|
|
])
|
|
)
|
|
)
|
|
})
|