\ sheepsint control module \ CONTROL \ OSCILLATOR PERIOD \ Convention: All words starting with underscore consume and/or \ produce 16bit values. \ simple synth configs. note: \ - need to disable interrupts during period update. \ - the single byte access uses 'dup' to obtain #x101 * \ - before writing, periods are negated, so we can work with unsigned values \ - dup/drop trick to keep interrupts enabled one more cycle \ need to limit period size to acceptable value to avoid rollovers or \ simply to fast interrupt rates that cause lockups. require the period \ to be > #xFF, meaning the high byte cannot be zero. : clamp/inv 0 xor z? if drop drop 0 1 then \ fall into 2inv : 2inv INDF0 comf d=reg #xff xor ; macro : per! | var | clamp/inv cli var 1 + ! dup var ! sti drop ; \ >m m-dup \ clamp/inv cli \ m> 1 + ! dup \ m> ! sti drop ; : per@ | var | var @ var 1 + @ 2inv ; \ >m m-dup \ m> @ m> 1 + @ \ 2inv ; forth : p0 dup : _p0 posc0l per! ; : p1 dup : _p1 posc1l per! ; : p2 dup : _p2 posc2l per! ; : _p0@ posc0l per@ ; : _p1@ posc1l per@ ; : _p2@ posc2l per@ ; \ PRESET SYNTH PATCHES \ The way to construct a synth byte is to start with the mixer number, \ and then set bits using the 'bit' word. macro : bit 1 swap <<< or ; \ inbyte n -- outbyte | set bit number n forth : _square : square mix:xmod sync:0->1 bit sync:0->2 bit synth ! -1 p1 -1 p2 ; \ disabled. : _xmod2 : xmod2 mix:xmod sync:0->2 bit synth ! -1 p2 ; \ disabled : _xmod3 : xmod3 mix:xmod synth ! ; : _rxmod : rxmod randbyte p0 randbyte p1 randbyte p2 xmod3 ; \ what about chord? 0->1->2 ? : _>x >x >x ; : _x> x> x> ; : pwm dup : _pwm xmod2 _>x _p0@ _x> _- _p1 ; \ add modulation frequency : _noise : noise mix:osc1 osc1:noise bit synth ! -1 p0 \ disabled -1 p2 1 p1 ; \ +- 8 kHz load double-math.f \ 16 bit math routines load double-hacks.f \ some optimized ones : reso mix:reso sync:0->1 bit sync:0->2 bit synth ! _p0@ _>> _dup _p2 \ half max reso _>> _p1 ; \ reso freq 4x : silence 0 synth ! ; \ NOTE \ A4 above [middle] C4 is 440Hz \ this makes C5 = 440 2 sqrt sqrt * \ the lowest frequency that can be obtained at 2 MIPS with 2^16 \ subdivision is 30.517578 Hz, which is a little lower than C1 at \ 32.703194 so the magic numbers are based on \ C1 = 61156 \ there are 2 words used here. 'note' computes a note, while 'octave' \ sets the current octave. the word 'bass-note' computes the lowest \ octave (starting from C1) \ load metaprogramming stuff in badnop namespace \ [ badnop (synth meta) use ] \ require pic18/geo.ss \ geo-seq macro : bass->period 23 min _table \ 2 octaves, more convenient.. 61156 \ start dup 4 / \ endx 24 \ nb of items geo-seq \ compile geometric sequence variable oct : _note drop \ drop high byte : note bass->period oct @ nz? if for _>> next else drop then ; : _octave drop \ ignore high byte : octave oct ! ; \ bound to some oscillator : note0 note _p0 ; : note1 note _p1 ; : note2 note _p2 ; \ 8 bit fetch for simple effects : p0@ _p0@ nip ; : p1@ _p1@ nip ; : p2@ _p2@ nip ; \ CONTROL TIMER \ the fixed rate will increment a 32bit 'tick' counter once every \ cycle. this counter can be used as a timer for the control rate \ synchronization. \ the fixed interrupt (with cpu at 2 MIPS) runs at 7812 Hz, and \ increments a counter that can be used for longer time \ intervals. From this we'll derive 2 standard sync timers: control \ rate and note rate, at about 200 Hz and 8 Hz \ to sync, first wait for a bit to go low, then wait for it to go \ high. the different tick bits give \ tick0 0 3906 Hz \ 4 244 Hz \ tick1 1 7.6 Hz \ FIXME: rewrite with lex params macro : tickbit | bit | tick0 bit 3 >>> + \ tick + byte address bit 7 and ; \ bit address : sync-tick | bit | begin bit tickbit low? until begin bit tickbit high? until ; forth \ these have changed + it's better to use tasks \ : wait-note 9 sync-tick ; \ 7.8 Hz \ : wait-control 4 sync-tick ; \ 244 Hz \ TRANSIENTS + EFFECTS \ words here will sync to control rate. transients that use \ oscillators should save the frequencies in addition to synth config. : s>x synth @ >x ; : x>s x> synth ! ; : p0>x _p0@ _>x ; : x>p0 _x> _p0 ; : p1>x _p1@ _>x ; : x>p1 _x> _p1 ; : p2>x _p2@ _>x ; : x>p2 _x> _p2 ; \ transient noise burst, used for hihat / snare : burst s>x noise for 2 sync-tick next x>s ; \ PANEL load ad.f load synth-panel.f macro : andor! | reg | reg and! reg or! ; \ >m m-dup m> and! m> or! ; forth : param \ first 3 pots set period 0 ad@ p0 1 ad@ p1 2 ad@ p2 \ pot nb 3 is [ synth:2 noise:1 ignored:5 ] 3 ad@ #b11100000 and rot<< rot<< #b01111100 synth andor! \ pot nb 4 sets sync bits 4 ad@ #b11100000 and rot<< #b10001111 synth andor! ;