Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --No credit here. It's a CC idiom.
- local function yield()
- os.queueEvent("fake")
- os.pullEvent("fake")
- end
- --From the lua guide.
- function newStack ()
- return {""} -- starts with an empty string
- end
- function addString (stack, s)
- table.insert(stack, s) -- push 's' into the the stack
- for i=table.getn(stack)-1, 1, -1 do
- if string.len(stack[i]) > string.len(stack[i+1]) then
- break
- end
- stack[i] = stack[i] .. table.remove(stack)
- end
- end
- --Credit for the following code should go to: Youka on Github. Ported from io to fs by me.
- local function collect_audio_data(filename)
- local seekpos=1
- -- WAVE data storage
- local wav = {channels = nil, sample_rate = nil, samples = nil, data = nil}
- -- Open file
- local file = fs.open(filename, "rb")
- --Damn whoever decided rb shouldn't have a readAll().
- local s=newStack()
- while true do
- local r = file.read()
- if r==nil then break end
- addString(s,r)
- end
- local filecontents = table.concat(s)
- if not file then
- return string.format("Filename %q isn't valid!", filename)
- end
- local seek = function(number)--only has cur mode
- seekpos=seekpos+number
- end
- local read = function(number)--number should be format, but this code only uses number so ill only implement that.
- local ret = string.sub(filecontents,seekpos,(seekpos+number)-1);
- seek(number)
- end
- -- File chunk
- if read(4) ~= "RIFF" then
- return "Isn't a 'RIFF' file"
- end
- seekpos=seekpos+4 -- Ignore file size
- if read(4) ~= "WAVE" then
- return "Isn't a 'WAVE' file"
- end
- -- Subchunks
- while true do
- yield()
- -- Chunk id and size
- local id = read(4)
- local size = read(4)
- -- No more chunks
- if not size then
- break
- -- Chunk to read
- else
- size = bton(size)
- -- Format chunk
- if id == "fmt " then
- local bytes = read(2)
- if not bytes or bton(bytes) ~= 1 then
- return "Data format must be PCM!"
- end
- bytes = read(2)
- if not bytes then
- return "Channels are missing!"
- end
- wav.channels = bton(bytes)
- bytes = read(4)
- if not bytes then
- return "Sample rate is missing!"
- end
- wav.sample_rate = bton(bytes)
- seek(4+2) -- Ignore byte rate and block align
- bytes = read(2)
- if not bytes or bton(bytes) ~= 16 then
- return "Bits per sample must be 16!"
- end
- seek(size-16) -- Ignore extra data
- -- Data chunk
- elseif id == "data" then
- if not wav.channels then
- return "Format must be before data!"
- end
- wav.samples = size / (wav.channels * 2)
- do
- local file_cur_pos = seekpos
- local file_end_pos = #filecontents
- if file_end_pos - file_cur_pos < wav.samples * wav.channels * 2 then
- return "Remaining file bytes aren't enough to collect all samples!"
- end
- end
- wav.data = {}
- for c = 1, wav.channels do
- wav.data[c] = {n = 0}
- end
- for s = 1, wav.samples do
- if (s% 100000)==0 then yield() end
- for c = 1, wav.channels do
- local sample = bton(read(2))
- wav.data[c].n = wav.data[c].n + 1
- wav.data[c][wav.data[c].n] = sample > 32767 and sample - 65536 or sample
- end
- end
- break -- Was last needed chunk
- -- Ignorable chunk
- else
- seek(size)
- end
- end
- end
- -- All WAVE data collected?
- if not wav.channels then
- return "Format is missing!"
- elseif not wav.data then
- return "Data are missing!"
- end
- -- Return no error and WAVE data
- return nil, wav
- end
- --This code was originally by the author of lionray in java, it has been ported to computercraft by me.
- local function newConverter()
- local respinc=7
- local respdec=20
- local lpfstrength=100
- local resp=0
- local level=0
- local lastbit=false
- local flastlevel=0
- local lpflevel=0;
- local myself= {
- ctx_update=function(curbit)
- local target= curbit and 127 or -128
- local nlevel=(level+math.floor((response*(target-level)+128)/256))
- if nlevel==level and level~=target then
- nlevel=nlevel+(curbit and 1 or -1)
- end
- local rtarget=nil
- local rdelta=nil
- rtarget=(curbit==lastbit) and 255 or 0
- rdelta=(curbit==lastbit) and respinc or respdec
- local nresponse=response+math.floor((rdelta*(rtarget-response)+128)/256)
- if nresponse == response and response~=rtarget then
- nresponse=nresponse+((curbit==lastbit) and 1 or -1)
- end
- response=nresponse
- lastbit=curbit
- level=nlevel
- end,
- --No need for decompress, one-way converter (tldr, im lazy)
- compress=function(dest,src,destoffs,srcoffs,len)
- for i=1,len do
- local d = 0;
- for j=1,8 do
- local inlevel = src[srcoffs]
- srcoffs=srcoffs+1
- local curbit = (inlevel>level or (inlevel==level and level==127))
- d=(curbit and ((d/2)+128) or (d/2))
- myself.ctx_update(curbit)
- end
- dest[destoffs]=d
- destoffs=destoffs+1
- end
- end
- }
- return myself
- end
- ----This part, surrounded by 8 dashes, interfaces between the top code and the bottom code.
- local function newAudioReader(wav,verboseout,isleft)
- if verboseout then verboseout.say("Converting wav data to wav samples...") end
- errorstr,wavdata=collect_audio_data(wav)
- if errorstr then
- error("Error! "..errorstr)
- end
- for c=1,2 do
- wavdata.data[c].n=nil
- newarray={}
- for i=1,#wavdata.data[c] do
- newarray[math.floor(((32768/wavdata.sample_rate)*(i-1))+1)]=math.floor(wavdata.data[c][i]/256)
- if i% 100000==0 then sleep(0.1) end
- end
- wavdata.data[c]=newarray
- end
- local index = 1
- return {
- read=function(buffertable)
- local remaining=#wavdata.data[isleft and 1 or 2].n-(index-1)
- if(not (#buffertable>remaining)) then
- local bi=1
- while true do
- buffertable[bi]=wavdata.data[isleft and 1 or 2][i]
- bi=bi+1
- index=index+1
- end
- end
- return bytesread
- end
- }
- end
- ----
- function convert(wav,out,isleft,verboseout)
- local sampleRate=32768
- --encoded samples are in litte startian
- local newArray=function(length)
- local ret = {}
- for i=1,length do
- table.insert(ret,0)
- end
- return ret
- end
- local readBuffer = newArray(1024)
- local outBuffer = newArray(1024/8)
- local c = newConverter()
- local audioin=newAudioReader(wav,verboseout,isleft)
- local fout = fs.open(out,"wb")
- if verboseout then verboseout.say("Converting wav samples to DFPWM...") end
- while true do
- local read = audioin.read(readBuffer)
- local i =0
- if read < 0 then break end
- c.compress(outBuffer,readBuffer,0,0,read/8)
- fout.write(outBuffer)
- if i%100==0 then yield() end
- i=i+1
- end
- fout.flush()
- fout.close()
- end
- --CC program to use the api above. Remove this section and loadapi to use as an api.
- print("Type the location of the wav file: ")
- local wavfileloc=read()
- print("Type a location for the DFPWM file to be written to.")
- local dfpwmout=read()
- print("Use left or right channel? Type just \"2\" for right and any other character for left.")
- local chan=read()
- chan=(chan~="2")
- convert(wavfileloc,dfpwmout,chan,{say=function(s) print(s) end})
Advertisement
Add Comment
Please, Sign In to add comment