honeylisp/link/tapegen.fnl

101 lines
3 KiB
Fennel

(local util (require :lib.util))
(local tape (require :asm.tape))
; https://www.apple.asimov.net/documentation/hardware/io/The%20Apple%20II%20Cassette%20Interface.txt
; header
; ---+ +-------------+ +-----+
; | | | | |
; +-------------+ +----+ |
; | 1300 microseconds | 200| 250 |
; header tone | synchronous bit |
; 0 and 1 bits
; +-----+ +----------+ +
; | | | | |
; + +-----+ +----------+
; | 500 usec | 1000 usec |
(local samplerate 48000)
(fn us-to-s [us] (* (/ 1 1000000) us))
(fn samplecount [us] (* samplerate (us-to-s us)))
(fn checksum [bytes]
(var sum 0xff)
(for [ibyte 1 (bytes:len)]
(set sum (bit.bxor sum (: (bytes:sub ibyte ibyte) :byte))))
sum)
(fn data []
{:cycles []
:samples 0
:cycle
(fn [self us count]
(local samples (samplecount us))
(local prev-cycle (. self.cycles (length self.cycles)))
(local prev-samples (-?> prev-cycle (. 1)))
(if (= samples prev-samples) (tset prev-cycle 2 (+ (. prev-cycle 2) count))
(table.insert self.cycles [samples count]))
(set self.samples (+ self.samples (* samples count))))
:header
(fn [self ?s]
(self:cycle (/ 1300 2) (math.floor (/ (or ?s 5) (us-to-s (/ 1300 2)))))
(self:cycle 200 1)
(self:cycle 250 1))
:bit (fn [self on] (self:cycle (if on 500 250) 2))
:byte
(fn [self byte]
(for [ibit 7 0 -1]
(self:bit (not= (bit.band byte (bit.lshift 1 ibit)) 0))))
:bytes
(fn [self bytes]
(for [ibyte 1 (bytes:len)]
(self:byte (: (bytes:sub ibyte ibyte) :byte))))
:bytes-with-checksum
(fn [self bytes]
(self:bytes bytes)
(self:byte (checksum bytes)))
:to-sound
(fn [self]
(local sound (love.sound.newSoundData (+ self.samples (samplecount 8500)) samplerate 16 1))
(var isample 0)
(var err 0)
(var on true)
(each [_ [samples count] (ipairs self.cycles)]
(for [_ 1 count]
(set err (+ err (% samples 1)))
(for [_ 1 (+ samples (math.floor err))]
(sound:setSample isample (if on 0.75 -0.75))
(set isample (+ isample 1)))
(set err (% err 1))
(set on (not on))))
; finish last bit
(set on (not on))
(for [_ 1 (samplecount 500)]
(sound:setSample isample (if on 0.75 -0.75))
(set isample (+ isample 1)))
; add a little pad of zeros
(for [_ 1 (samplecount 8000)]
(sound:setSample isample 0)
(set isample (+ isample 1)))
sound)})
(fn gen-bin [bytes ?s-header]
(local dat (data))
(dat:header ?s-header)
(dat:bytes-with-checksum bytes)
(dat:to-sound))
(fn gen-basic [bytes ?s-header]
(local dat (data))
(dat:header ?s-header)
(dat:bytes-with-checksum
(.. (util.int16-to-bytes (+ (bytes:len) 1)) (util.int8-to-bytes 0xd5)))
(dat:header ?s-header)
(dat:bytes-with-checksum bytes)
(dat:to-sound))
{: gen-bin : gen-basic : checksum : data : samplerate : samplecount}