mirror of
https://github.com/sjlongland/atinysynth.git
synced 2025-09-13 10:03:15 +10:00
gensine: Add in sine wave generator.
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°.
This commit is contained in:
parent
2fe3ea766c
commit
44e2d937d6
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,5 +3,6 @@
|
||||
*.o
|
||||
*.elf
|
||||
*.hex
|
||||
sine.c
|
||||
obj
|
||||
bin
|
||||
|
5
Makefile
5
Makefile
@ -38,6 +38,11 @@ $(OBJDIR)/%.o: $(PORTDIR)/%.c
|
||||
$(CC) -MM $(CPPFLAGS) $(INCLUDES) $< \
|
||||
| sed -e '/^[^ ]\+:/ s:^:$(OBJDIR)/:g' > $@.dep
|
||||
|
||||
$(SRCDIR)/sine.c: $(SRCDIR)/gensine.py
|
||||
python $^ --amplitude 127 --num-samples-bits \
|
||||
--num-samples 6 --data-type int8_t \
|
||||
> $@
|
||||
|
||||
clean:
|
||||
-rm -fr $(OBJDIR) $(BINDIR)
|
||||
|
||||
|
90
gensine.py
Normal file
90
gensine.py
Normal file
@ -0,0 +1,90 @@
|
||||
#!/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
|
||||
])
|
||||
)
|
||||
)
|
||||
})
|
Loading…
Reference in New Issue
Block a user