Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local _conf = {
- pal = true,
- rate = 1,
- }
- local args = {...}
- if #args ~= 1 then
- print("Usage: songplayer.lua filename")
- return
- end
- local datfile, err = io.open(args[1],"rb")
- if not datfile then
- print("Failed to open file: " .. err)
- return
- end
- local framems, freqdiv
- if _conf.pal then
- framems = 1000/50
- freqdiv = 17734475/(18*2^24)
- else
- framems = 1000/60
- freqdiv = 14318182/(14*2^24)
- end
- local attackrates = {[0]=2,8,16,24,38,56,68,80,100,250,500,800,1000,3000,5000,8000}
- local decayrates = {[0]=6,24,48,72,114,168,204,240,300,750,1500,2400,3000,9000,15000,24000}
- local bit = require("bit32")
- local component = require("component")
- local sound = component.sound
- sound.clear()
- sound.setTotalVolume(1)
- for i = 1,8 do
- sound.resetEnvelope(i)
- sound.resetAM(i)
- sound.resetFM(i)
- sound.setVolume(i,1)
- sound.close(i)
- end
- sound.process()
- os.sleep(0.05)
- local sid = {
- c1 = {
- freq=0,
- waveform=0,
- attack=0,
- decay=0,
- sustain=0,
- release=0,
- pulse=0,
- stat = {
- mode=0,
- pos=0,
- },
- },
- c2 = {
- freq=0,
- waveform=0,
- attack=0,
- decay=0,
- sustain=0,
- release=0,
- pulse=0,
- stat = {
- mode=0,
- pos=0,
- },
- },
- c3 = {
- freq=0,
- waveform=0,
- attack=0,
- decay=0,
- sustain=0,
- release=0,
- pulse=0,
- stat = {
- mode=0,
- pos=0,
- },
- },
- cutoff=0,
- kind=0,
- volume=0,
- }
- local function getFreq(x)
- return x*freqdiv
- end
- local function moveTowards(cur,targ,amt)
- if cur == targ then
- return targ, true
- elseif cur < targ then
- cur = cur + amt
- if cur >= targ then
- return targ, true
- end
- elseif cur > targ then
- cur = cur - amt
- if cur <= targ then
- return targ, true
- end
- end
- return cur, false
- end
- local lastchannel=0
- local chadata={}
- local function emulateADSR(chan)
- local key = "c" .. chan
- local freq = getFreq(sid[key].freq)
- local mvol = (sid.volume/15)
- local tenab = math.floor(sid[key].waveform/2^0)%2 == 1
- local senab = math.floor(sid[key].waveform/2^1)%2 == 1
- local penab = math.floor(sid[key].waveform/2^2)%2 == 1
- local nenab = math.floor(sid[key].waveform/2^3)%2 == 1
- local sus = sid[key].sustain/15
- for i = 1,framems/_conf.rate do
- local tv,ep
- if sid[key].stat.mode == 0 then
- tv,ep = 1,attackrates[sid[key].attack]
- elseif sid[key].stat.mode == 1 then
- tv,ep = sus,decayrates[sid[key].decay]
- elseif sid[key].stat.mode == 2 then
- tv,ep = sus,math.huge
- elseif sid[key].stat.mode == 3 then
- tv,ep = 0,decayrates[sid[key].release]
- else
- tv,ep = 0,math.huge
- end
- local nextv
- sid[key].stat.pos, nextv = moveTowards(sid[key].stat.pos, tv, 1/ep)
- if nextv and sid[key].stat.mode < 2 then
- sid[key].stat.mode = sid[key].stat.mode + 1
- end
- end
- local vol = sid[key].stat.pos*mvol
- if not (nenab and (tenab or senab or penab)) then
- if tenab then
- lastchannel = lastchannel + 1
- sound.setWave(lastchannel,sound.modes.triangle,freq)
- sound.setVolume(lastchannel,vol)
- chadata[lastchannel] = true
- end
- if senab then
- lastchannel = lastchannel + 1
- sound.setWave(lastchannel,sound.modes.sawtooth,freq)
- sound.setVolume(lastchannel,vol)
- chadata[lastchannel] = true
- end
- if penab then
- lastchannel = lastchannel + 1
- sound.setWave(lastchannel,sound.modes.square,freq)
- sound.setVolume(lastchannel,vol)
- chadata[lastchannel] = true
- end
- if nenab then
- lastchannel = lastchannel + 1
- sound.setWave(lastchannel,sound.modes.noise,freq*8)
- sound.setVolume(lastchannel,vol)
- chadata[lastchannel] = true
- end
- end
- end
- local stat = 0
- while true do
- local line = datfile:read("*l")
- if not line then
- break
- end
- if #line ~= 47 then
- _conf.rate = line/(_conf.pal and 50 or 60)
- goto continue
- end
- local c = {{},{},{}}
- c[1].freq = line:sub(1,4)
- c[1].wf = line:sub(5,6)
- c[1].adsr = line:sub(7,10)
- c[1].pulse = line:sub(11,13)
- c[2].freq = line:sub(14,17)
- c[2].wf = line:sub(18,19)
- c[2].adsr = line:sub(20,23)
- c[2].pulse = line:sub(24,26)
- c[3].freq = line:sub(27,30)
- c[3].wf = line:sub(31,32)
- c[3].adsr = line:sub(33,36)
- c[3].pulse = line:sub(37,39)
- local cut = line:sub(40,43)
- local ft = line:sub(44,46)
- local volume = line:sub(47,47)
- for i = 1,3 do
- local key = "c" .. i
- if c[i].freq ~= "...." then sid[key].freq = tonumber(c[i].freq,16) end
- if c[i].wf ~= ".." then
- sid[key].waveform = math.floor(tonumber(c[i].wf,16)/16)
- local gate = math.floor(tonumber(c[i].wf,16)/2^0)%2 == 1
- sid[key].sync = math.floor(tonumber(c[i].wf,16)/2^1)%2 == 1
- sid[key].ring = math.floor(tonumber(c[i].wf,16)/2^2)%2 == 1
- sid[key].test = math.floor(tonumber(c[i].wf,16)/2^3)%2 == 1
- if gate and sid[key].stat.mode >= 3 then
- sid[key].stat.mode = 0
- elseif not gate and sid[key].stat.mode < 3 then
- sid[key].stat.mode = 3
- end
- end
- if c[i].pulse ~= "..." then sid[key].pulse = tonumber(c[i].pulse,16) end
- if c[i].adsr ~= "...." then
- sid[key].attack = math.floor(tonumber(c[i].adsr,16)/2^12)%16
- sid[key].decay = math.floor(tonumber(c[i].adsr,16)/2^8)%16
- sid[key].sustain = math.floor(tonumber(c[i].adsr,16)/2^4)%16
- sid[key].release = math.floor(tonumber(c[i].adsr,16)/2^0)%16
- end
- end
- if volume ~= "." then sid.volume = tonumber(volume,16) end
- lastchannel = 0
- local lcs = #chadata
- for i = 1,3 do
- local key = "c" .. i
- if not sid[key].test then
- emulateADSR(i)
- end
- end
- if lcs < lastchannel then
- for i = lcs + 1,lastchannel do
- sound.open(i)
- end
- elseif lcs > lastchannel then
- for i = lastchannel + 1,lcs do
- sound.close(i)
- chadata[i]=nil
- end
- end
- sound.delay(framems/_conf.rate)
- stat = stat + 1
- if stat >= 50/2*_conf.rate then
- stat = 0
- while not sound.process() do
- os.sleep(0.05)
- end
- os.sleep(0.05)
- end
- ::continue::
- end
- for i = 1,8 do
- sound.resetEnvelope(i)
- sound.resetAM(i)
- sound.resetFM(i)
- sound.setVolume(i, 1)
- sound.close(i)
- end
- sound.delay(1000)
- while not sound.process() do
- os.sleep(0.05)
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement