-----------------------
-- Name: DMC Capture
-- Author: upsilandre
-----------------------
-- Displays information about the DMC samples: [Sample Address] [Sample Size (bytes)] [Sample Frequency]
-- Every new sample encountered will be logged in the script's log window.
-- Right-clicking on the screen turns on DMC sample recording. While recording, any sample played
-- will be recorded on the disk in .wav format in the LuaScriptData folder
-----------------------

function Main()
  dmc = emu.getState().apu.dmc
  SelectRecord()
  if dmc.bytesRemaining > 0 then
    freq = math.floor(dmc.sampleRate)
    new = true
    if nbSample > 0 then
      for i = 1,nbSample do
        if (dmc.sampleAddr == sample.addr[i] and dmc.sampleLength == sample.length[i] and dmc.period == sample.period[i]) then
          new = false
          break
        end
      end
    end
    if new then
      nbSample = nbSample + 1
      sample.addr[nbSample] = dmc.sampleAddr
      sample.length[nbSample] = dmc.sampleLength
      sample.period[nbSample] = dmc.period
      emu.log(nbSample.." >>  $"..string.format("%X  ", dmc.sampleAddr)..string.format("%4d bytes  ",dmc.sampleLength)..string.format("%5d hertz ",freq)..stringR)
      if record then
        SampleRecord(dmc.sampleAddr, dmc.sampleLength, freq)
      end
    end
    if dmc.sampleAddr ~= lastSampleAddr or dmc.sampleLength ~= lastSampleLength or dmc.period ~= lastSamplePeriod then
      lastSampleAddr = dmc.sampleAddr
      lastSampleLength = dmc.sampleLength
      lastSamplePeriod = dmc.period
      emu.drawString(4, 7, " $"..string.format("%X  ", dmc.sampleAddr)..string.format("%4d bytes  ",dmc.sampleLength)..string.format("%5d hertz ",freq), 0xffffff, 0x40ffffff, 2)
    else
      emu.drawString(4, 7, " $"..string.format("%X  ", dmc.sampleAddr)..string.format("%4d bytes  ",dmc.sampleLength)..string.format("%5d hertz ",freq), 0xc0c0c0, 0x40202020, 1)
    end
  end
end


function SelectRecord()
  if emu.getMouseState().right then
    if not hold then
      hold = true
      record = not record
      nbSample = 0
      emu.log("______________________________________________________________________\n")
      if record then
        emu.log("File path = "..emu.getScriptDataFolder().."\\\n")
      end
    end
  else
    hold = false
  end
  if record then
    stringR = "  recorded"
    emu.drawRectangle(193, 5, 42, 11, 0x404040, true, 1)
    emu.drawRectangle(192, 4, 44, 13, 0x808080, false, 1)
    emu.drawString(196, 7, "RECORD", 0xFF0000, 0xFF000000, 1)
  else
    stringR = ""
  end
end


function SampleRecord(addr, length, freq)
  dataLen = length * 8 + 1
  header[5] = (dataLen + 36) & 255
  header[6] = (dataLen + 36) >> 8
  header[25] = freq & 255
  header[26] = freq >> 8
  header[29] = header[25]
  header[30] = header[26]
  header[41] = dataLen & 255
  header[42] = dataLen >> 8
  header[45] = 0x80
  fileName = string.sub(emu.getRomInfo().name, 1, #emu.getRomInfo().name - 4)..string.format("_$%X_", addr)..length.."b_"..freq.."hz.wav"
  fileOutput = io.open(emu.getScriptDataFolder().."\\"..fileName, "w+b")
  for k = 1,45 do
    fileOutput:write(string.char(header[k]))
  end
  lastSample = 0x80
  for i = 0, length - 1 do
    bitMask = 1
    byte = emu.read(addr + i, emu.memType.cpuDebug)
    for j = 1,8 do
      if byte & bitMask > 0 then
        newSample = lastSample + 4
        if newSample == 256 then
          newSample = 252
        end
      else
        newSample = lastSample - 4
        if newSample == -4 then
          newSample = 0
        end
      end
      fileOutput:write(string.char(newSample))
      lastSample = newSample
      bitMask = bitMask << 1
    end
  end
  fileOutput:close()
end


sample = {}
sample.addr = {}
sample.length = {}
sample.period = {}
nbSample = 0
record = false
hold = false
header = {0x52,0x49,0x46,0x46,0x00,0x00,0x00,0x00,
          0x57,0x41,0x56,0x45,0x66,0x6D,0x74,0x20,
          0x10,0x00,0x00,0x00,0x01,0x00,0x01,0x00,
          0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
          0x01,0x00,0x08,0x00,0x64,0x61,0x74,0x61,
          0x00,0x00,0x00,0x00}
emu.addEventCallback(Main, emu.eventType.endFrame)
emu.displayMessage("Script", "DMC Capture")
