(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}