#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/atomic.h>
#include <avr/power.h>
#include <xpal_noise.h>
Data Structures | |
struct | noise_part_s |
struct | sample_s |
Defines | |
#define | FILL(x) {x, sizeof(x)} |
Typedefs | |
typedef struct noise_part_s | noise_part_t |
Functions | |
ISR (TIMER0_OVF_vect, ISR_BLOCK) | |
void | xpal_noise_init (void) |
init noise generator | |
void | xpal_noise_gc (void) |
garbage-collect noise parts | |
void | xpal_output_noise (uint16_t duration, uint8_t value, volatile uint8_t *pdone) |
output some noise | |
Variables | |
noise_part_t *volatile | noise_playing |
currently playing sample | |
noise_part_t * | noise_last |
pointer to last sample in queue | |
noise_part_t *volatile | noises_done |
list of samples awaiting GC | |
const uint8_t sample1[] | PROGMEM |
typedef struct noise_part_s noise_part_t |
We build a singly-linked list of samples to play. Each sample has a pointer to a list of values to output to the PWM generator (in a circular way) and a duration.
When the sample has used up its timeslice (as measured by duration), the next sample will get started and the current sample will be put in a list for later garbage collection (we don't want to do the free() in the ISR).
void xpal_noise_init | ( | void | ) |
init noise generator
initialize the noise generator (Timer0 in PWM mode)
{
DDRG |= _BV(DDG5);
/* fast PWM, normal output mode on OC0B */
TCCR0A = (TCCR0A & ~(_BV(COM0B1)|_BV(COM0B0)|_BV(WGM01)|_BV(WGM00)))
| (_BV(COM0B1)|(0*_BV(COM0B0))|_BV(WGM01)|_BV(WGM00));
}
void xpal_output_noise | ( | uint16_t | duration, | |
uint8_t | value, | |||
volatile uint8_t * | pdone | |||
) |
output some noise
duration | time after the noise should stop | |
value | 0 - stop noise, otherwise play | |
pdone | pointer to a uint8_t which gets set to !=0 when playing this part has finished. Pass NULL when not wanted. |
values: 1 - 3600 Hz sine wave 2 - 7200 Hz sine wave
{ if (value) { const struct sample_s *s; uint8_t sz; noise_part_t * p = NULL; uint8_t * v = NULL; /* bounds check */ if (value >= sizeof(samples)/sizeof(samples[0])) value = 1; /* prepare new part for queue */ s = &samples[value]; sz = pgm_read_byte (&s->sz); while (1) { if (!v) v = malloc (sz); if (!p) p = malloc(sizeof(noise_part_t)); if (v && p) break; /* malloc() failed - try GC if useful */ if (!noises_done) { /* GC won't help (yet?) - free() malloced spaces and ignore sample */ free (v); free (p); if (pdone) *pdone = -1; return; } xpal_noise_gc (); } p->values = v; p->num_values = sz/sizeof(uint8_t); p->flags.malloced_struct = p->flags.malloced_values = 1; memcpy_P (v, (const uint8_t*)pgm_read_word (&s->v), sz); p->duration = duration; p->next = NULL; p->pdone = pdone; /* Link newly-prepared part at end of queue. * Block is necessary to prevent a race condition with the ISR * modifying noise_playing or *noise_playing. */ ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { if (!noise_playing) { noise_playing = p; xpal_noise_init (); xpal_noise_enable (); } else { noise_last->next = p; } noise_last = p; } } else { xpal_noise_disable (); } xpal_noise_gc (); }
const char _etext PROGMEM |
{ 128, 176, 218, 245, 255, 245, 217, 176, 127, 79, 37, 10, 0, 10, 37, 79 }