Advertisement
1lann

juroku

Sep 3rd, 2019
177
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 9.05 KB | None | 0 0
  1. local hex = {"0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"}
  2.  
  3. Decoder = {}
  4.  
  5. local typeImage = 1
  6. local typeVideo = 2
  7. local typeVideoNoAudio = 3
  8. local typeAudio = 4
  9.  
  10. local frameRate = 20
  11. local dataRate = 48000 / 8
  12. local startDelay = 1.25 * dataRate
  13.  
  14. function Decoder.new(monitors, file, driveA, driveB)
  15.     local self = {monitors = monitors, file = file, frame = 0, driveA = driveA,
  16.         driveB = driveB, writingDrive = driveA, playingDrive = driveA,
  17.         audioBuffer = {}, transDuration = 3, transitionBuffer = {},
  18.         loadedRows = {}, loadedColors = {}}
  19.     local magic = string.char(file.read()) .. string.char(file.read()) .. string.char(file.read())
  20.     if magic ~= "JUF" then
  21.         error("juroku: not a valid JUF file")
  22.     end
  23.  
  24.     local version = file.read()
  25.     if version ~= 1 then
  26.         error("juroku: JUF file version " .. version .. " not supported")
  27.     end
  28.  
  29.     self.type = file.read()
  30.  
  31.     if self.type ~= typeImage and self.type ~= typeVideo then
  32.         error("juroku: this decoder does not support this JUF media type")
  33.     end
  34.  
  35.     if self.type == typeVideo and ((not driveA) or (not driveB)) then
  36.         error("juroku: two tape drives as arguments required for audio output")
  37.     end
  38.  
  39.     local numMonitors = file.read()
  40.     if numMonitors ~= #monitors then
  41.         -- error("juroku: file requires " .. numMonitors ..
  42.             -- " monitors, but only " .. #monitors .. " given")
  43.     end
  44.  
  45.     for i = 1, numMonitors do
  46.         self.loadedRows[i] = {}
  47.         self.loadedColors[i] = {}
  48.     end
  49.  
  50.     setmetatable(self, {__index = Decoder})
  51.     return self
  52. end
  53.  
  54. function Decoder:read(size)
  55.     if size <= 0 then
  56.         return ""
  57.     elseif size < 16 * 1023 then
  58.         return self.file.read(size)
  59.     elseif size then
  60.         local result = ""
  61.         while true do
  62.             local nextRead = 16 * 1023
  63.             if size < nextRead then
  64.                 nextRead = size
  65.             end
  66.  
  67.             result = result .. self.file.read(nextRead)
  68.             size = size - nextRead
  69.  
  70.             if size == 0 then
  71.                 return result
  72.             end
  73.         end
  74.     else
  75.         return self.file.read()
  76.     end
  77. end
  78.  
  79. function Decoder:hasAudio()
  80.     return self.type == typeVideo or self.type == typeAudio
  81. end
  82.  
  83. function Decoder:parseAudio(shouldBuffer)
  84.     local f = self.file
  85.     local sizeArr = f.read(4)
  86.     local size = sizeArr:byte(1) * 0x1000000 + sizeArr:byte(2) * 0x10000 + sizeArr:byte(3) * 0x100 + sizeArr:byte(4)
  87.     local data = self:read(size)
  88.  
  89.     if self.writingDrive == nil then
  90.         -- if shouldBuffer then
  91.             for i = 1, size do
  92.                 table.insert(self.transitionBuffer, data:byte(i))
  93.             end
  94.         -- else
  95.         --  for i = 1, size do
  96.         --      local result = f.read()
  97.         --      table.insert(self.audioBuffer, result)
  98.         --  end
  99.         -- end
  100.         return
  101.     end
  102.  
  103.     -- if #self.audioBuffer > 0 then
  104.     --  for i = 1, #self.audioBuffer do
  105.     --      self.writingDrive.write(self.audioBuffer[i])
  106.     --  end
  107.     --  self.audioBuffer = {}
  108.     -- end
  109.  
  110.     -- if shouldBuffer then
  111.     --  table.concat(self.transitionBuffer, data)
  112.     -- end
  113.  
  114.     for i = 1, size do
  115.         local b = data:byte(i)
  116.         self.writingDrive.write(b)
  117.         table.insert(self.transitionBuffer, b)
  118.  
  119.         if i % (60 * dataRate) == 0 then
  120.             os.queueEvent("juroku_audio_yield")
  121.             coroutine.yield()
  122.         end
  123.     end
  124. end
  125.  
  126. function Decoder:loadFrame(frame, shouldBuffer)
  127.     local f = self.file
  128.  
  129.     if #self.loadedColors > 0 then
  130.         self.loadedColors[1] = {}
  131.     end
  132.  
  133.     if frame < 0 then
  134.         return true
  135.     end
  136.  
  137.     -- print("skipping...")
  138.     for i = self.frame, frame - 1 do
  139.         for m = 1, #self.monitors do
  140.             local first = f.read()
  141.             if first == nil then
  142.                 return false
  143.             end
  144.  
  145.             local width = first * 0x100 + f.read()
  146.             local height = f.read() * 0x100 + f.read()
  147.  
  148.             self:read(width * (height * 3) + (16 * 3))
  149.         end
  150.  
  151.         if self:hasAudio() then
  152.             self:parseAudio(shouldBuffer)
  153.         end
  154.     end
  155.  
  156.     -- print("reading...")
  157.     for m, t in pairs(self.monitors) do
  158.         local first = f.read()
  159.         if first == nil then
  160.             return false
  161.         end
  162.  
  163.         local width = first * 0x100 + f.read()
  164.         local height = f.read() * 0x100 + f.read()
  165.         local monitorRows = self.loadedRows[m]
  166.         local monitorColors = self.loadedColors[m]
  167.  
  168.         -- local image = self:read(width * (height * 2))
  169.  
  170.         for row = 1, height do
  171.             -- t.setCursorPos(1, row)
  172.             -- Use file.read here to improve performance
  173.             -- t.blit(f.read(width), f.read(width), f.read(width))
  174.             -- local fg = ""
  175.             -- local bg = ""
  176.             -- local txt = ""
  177.             -- for col = 1, width do
  178.             --  local pos = ((row - 1) * width + col) * 2 - 1
  179.             --  local color = string.byte(image:sub(pos, pos))
  180.             --  fg = fg .. hex[math.floor(color / 0x10) + 1]
  181.             --  bg = bg .. hex[bit.band(color, 0xF) + 1]
  182.             --  txt = txt .. image:sub(pos+1, pos+1)
  183.             -- end
  184.  
  185.             -- local text = f.read(width)
  186.             -- local cols = f.read(width)
  187.  
  188.             -- local fg = ""
  189.             -- local bg = ""
  190.             -- for i = 1, width do
  191.             --  local color = cols:byte(i)
  192.             --  fg = fg .. hex[math.floor(color / 0x10) + 1]
  193.             --  bg = bg .. hex[bit.band(color, 0xF) + 1]
  194.             --  -- textColors = textColors .. cols[0]
  195.             -- end
  196.  
  197.             monitorRows[row] = {f.read(width), f.read(width), f.read(width)}
  198.         end
  199.  
  200.         local paletteData = f.read(16 * 3)
  201.  
  202.         for i = 1, 16 do
  203.             local offset = (3 * i)
  204.             monitorColors[i] = paletteData:byte(offset - 2) * 0x10000 +
  205.                 paletteData:byte(offset - 1) * 0x100 + paletteData:byte(offset)
  206.         end
  207.     end
  208.  
  209.     -- print("writing audio...")
  210.     if self:hasAudio() then
  211.         self:parseAudio(shouldBuffer)
  212.     end
  213.  
  214.     -- print("done!")
  215.  
  216.     return true
  217. end
  218.  
  219. function Decoder:drawFrame()
  220.     if #self.loadedColors == 0 or #self.loadedColors[1] == 0 then
  221.         return
  222.     end
  223.  
  224.     -- os.queueEvent("juroku_frame")
  225.     -- coroutine.yield()
  226.  
  227.     for m, t in pairs(self.monitors) do
  228.         local monitorColors = self.loadedColors[m]
  229.         for i = 1, 16 do
  230.             t("setPaletteColor", 2^(i-1), monitorColors[i])
  231.         end
  232.  
  233.         for row, data in pairs(self.loadedRows[m]) do
  234.             t("setCursorPos", 1, row)
  235.             t("blit", data[1], data[2], data[3])
  236.         end
  237.  
  238.  
  239.     end
  240.  
  241.     -- os.queueEvent("juroku_frame")
  242.     -- coroutine.yield()
  243. end
  244.  
  245. function Decoder:writeTransitionBuffer()
  246.     if #self.transitionBuffer < startDelay then
  247.         return
  248.     end
  249.  
  250.     for i = #self.transitionBuffer - startDelay + 1, #self.transitionBuffer do
  251.         self.writingDrive.write(self.transitionBuffer[i])
  252.     end
  253.  
  254.     self.transitionBuffer = {}
  255. end
  256.  
  257. function Decoder:playVideo()
  258.     self.driveA.setSpeed(1)
  259.     self.driveB.setSpeed(1)
  260.     self.driveA.stop()
  261.     self.driveB.stop()
  262.     self.driveA.seek(-self.driveA.getSize())
  263.     self.driveB.seek(-self.driveB.getSize())
  264.     self:parseAudio(true)
  265.     for i = 1, 3000 do
  266.         self.driveA.write(0xAA)
  267.     end
  268.     self.driveA.seek(-self.driveA.getSize())
  269.  
  270.     local bufferLength = #self.transitionBuffer * dataRate
  271.     local tapeEndThreshold = #self.transitionBuffer - startDelay
  272.     local tapeEndCanary = #self.transitionBuffer - (startDelay * 2)
  273.     sleep(0)
  274.  
  275.     self.writingDrive = self.driveB
  276.     self:writeTransitionBuffer()
  277.  
  278.     self.playingDrive.play()
  279.     local tapeOffset = -startDelay
  280.     local currentFrame = -1
  281.     local nextPlaying = nil
  282.     local endTransition = 0
  283.     local lastPos = -1
  284.     local targetFrame = -1
  285.     local nextOffset = 0
  286.     local interpolatePos = 0
  287.  
  288.     while true do
  289.         local playPos = self.playingDrive.getPosition()
  290.  
  291.         local totalSamples = tapeOffset + playPos
  292.  
  293.         if playPos ~= lastPos then
  294.             targetFrame = math.floor((totalSamples / dataRate) * frameRate + 0.5)
  295.             interpolatePos = playPos
  296.             print(tapeOffset .. " + " .. playPos .. " = " .. totalSamples .. " | frame: " .. targetFrame)
  297.             lastPos = playPos
  298.         else
  299.             targetFrame = targetFrame + 1
  300.             interpolatePos = interpolatePos + (dataRate / frameRate)
  301.         end
  302.  
  303.         if not self:loadFrame(targetFrame - currentFrame - 1, false) then
  304.             self.playingDrive.stop()
  305.             return
  306.         end
  307.  
  308.         if targetFrame > currentFrame then
  309.             currentFrame = targetFrame
  310.         end
  311.  
  312.         if interpolatePos >= tapeEndThreshold then
  313.             if nextPlaying == nil and self.writingDrive.getPosition() > startDelay then
  314.                 -- Write 0.5 seconds of silence
  315.                 for i = 1, 3000 do
  316.                     self.writingDrive.write(0xAA)
  317.                 end
  318.  
  319.                 nextPlaying = self.writingDrive
  320.                 self.writingDrive = nil
  321.  
  322.                 os.queueEvent("juroku_audio_yield")
  323.                 coroutine.yield()
  324.  
  325.                 nextPlaying.seek(-nextPlaying.getSize())
  326.                 nextPlaying.play()
  327.                 print("playing transition")
  328.                 endTransition = tapeEndThreshold + startDelay
  329.                 nextOffset = interpolatePos
  330.             elseif nextPlaying ~= nil and interpolatePos >= endTransition then
  331.                 print("transitioning...")
  332.  
  333.                 os.queueEvent("juroku_audio_yield")
  334.                 coroutine.yield()
  335.  
  336.                 self.playingDrive.stop()
  337.                 self.playingDrive.seek(-self.playingDrive.getSize())
  338.                 self.writingDrive = self.playingDrive
  339.                 self:writeTransitionBuffer()
  340.                 self.playingDrive = nextPlaying
  341.                 print("done!")
  342.                 endTransition = 0
  343.                 nextPlaying = nil
  344.                 tapeOffset = tapeOffset + nextOffset
  345.                 playPos = self.playingDrive.getPosition()
  346.             end
  347.         end
  348.  
  349.  
  350.         -- os.queueEvent("juroku_frame")
  351.         -- coroutine.yield()
  352.  
  353.  
  354.         -- print("drawing frame")
  355.         -- sleep(0)
  356.  
  357.         self:drawFrame()
  358.         sleep(0)
  359.  
  360.         -- os.queueEvent("juroku_frame")
  361.         -- coroutine.yield()
  362.         -- sleep(0.2 - (os.clock() - start))
  363.     end
  364. end
  365.  
  366. function Decoder:render()
  367.     if self.type == typeImage then
  368.         self:loadFrame(0, false)
  369.         self:drawFrame()
  370.         return
  371.     elseif self.type == typeVideo then
  372.         self:playVideo()
  373.     end
  374. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement