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
|
*.o
|
||||||
*.elf
|
*.elf
|
||||||
*.hex
|
*.hex
|
||||||
|
sine.c
|
||||||
obj
|
obj
|
||||||
bin
|
bin
|
||||||
|
5
Makefile
5
Makefile
@ -38,6 +38,11 @@ $(OBJDIR)/%.o: $(PORTDIR)/%.c
|
|||||||
$(CC) -MM $(CPPFLAGS) $(INCLUDES) $< \
|
$(CC) -MM $(CPPFLAGS) $(INCLUDES) $< \
|
||||||
| sed -e '/^[^ ]\+:/ s:^:$(OBJDIR)/:g' > $@.dep
|
| 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:
|
clean:
|
||||||
-rm -fr $(OBJDIR) $(BINDIR)
|
-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