diff --git a/LoveKaraoke/cdgreader.lua b/LoveKaraoke/cdgreader.lua index 94c0a7a..0d18fb5 100644 --- a/LoveKaraoke/cdgreader.lua +++ b/LoveKaraoke/cdgreader.lua @@ -1,4 +1,3 @@ -require("bin") require("bit") --require("luabit.bit") -- CDG Command Code @@ -16,18 +15,45 @@ CDG_INST_TILE_BLOCK_XOR = 38 -- Bitmask for all CDG fields CDG_MASK = 0x3F cdgPlayer={} +cdgPlayer.CMD={ + MEMORY_PRESET = 1, + BORDER_PRESET = 2, + TILE_BLOCK = 6, + SCROLL_PRESET = 20, + SCROLL_COPY = 24, + DEF_TRANSP_COL = 28, + LOAD_COL_TBL_0_7 = 30, + LOAD_COL_TBL_8_15 = 31, + TILE_BLOCK_XOR = 38 +} +cdgPlayer.tempdata={ + colors={}, + index=1, + data={}, + next=function(self) + local data=self.data[self.index] + if not data then return end + self.index=self.index+1 + return data + end +} +function cdgPlayer:packCommand(cmd,...) +--~ print("Packing: "..cmd) + table.insert(self.tempdata.data,{cmd,...}) +end function cdgPlayer:init(cdgFileName) self.FileName = cdgFileName -- Check the CDG file exists - if not io.fileExists(self.FileName) then + if not love.filesystem.exists(self.FileName) then ErrorString = "No such file: ".. self.FileName error(ErrorString) end self:decode() + return self.tempdata end function cdgPlayer:decode() -- Open the cdg file - self.cdgFile = bin.load(self.FileName) + self.cdgFile = bin.new((love.filesystem.read(self.FileName))) -- Main processing loop while true do packd = self:cdgGetNextPacket() @@ -62,7 +88,7 @@ function cdgPlayer:cdgPacketProcess(packd) elseif inst_code == CDG_INST_TILE_BLOCK_XOR then self:cdgTileBlockCommon(packd, 1) else - ErrorString = "Unknown command in CDG file: " + str(inst_code) + ErrorString = "Unknown command in CDG file: " .. tostring(inst_code) print(ErrorString) end end @@ -88,12 +114,14 @@ end function cdgPlayer:cdgMemoryPreset(packd) colour = bit.band(packd['data'][1], 0x0F) repea = bit.band(packd['data'][2], 0x0F) - print (string.format("cdgMemoryPreset [Colour=%d, Repeat=%d]", colour, repea)) +--~ print (string.format("cdgMemoryPreset [Colour=%d, Repeat=%d]", colour, repea)) + self:packCommand("MEMORY_PRESET",colour, repea) return end function cdgPlayer:cdgBorderPreset(packd) colour = bit.band(packd['data'][1], 0x0F) - print (string.format("cdgMemoryPreset [Colour=%d]", colour)) +--~ print (string.format("cdgMemoryPreset [Colour=%d]", colour)) + self:packCommand("MEMORY_PRESET",colour) return end function cdgPlayer:cdgScrollPreset(packd) @@ -116,11 +144,12 @@ function cdgPlayer:cdgScrollCommon(packd, copy) vOffset = bit.band(vScroll, 0x0F) if copy then - typeStr = "cdgScrollCopy" + typeStr = "SCROLL_COPY" else - typeStr = "cdgScrollPreset" + typeStr = "SCROLL_PRESET" end - print(string.format("%s [colour=%d, hScroll=%d, vScroll=%d]", typeStr, colour, hScroll, vScroll)) +--~ print(string.format("%s [colour=%d, hScroll=%d, vScroll=%d]", typeStr, colour, hScroll, vScroll)) + self:packCommand(typeStr, colour, hScroll, vScroll) return end function cdgPlayer:cdgTileBlockCommon(packd, xor) @@ -130,42 +159,50 @@ function cdgPlayer:cdgTileBlockCommon(packd, xor) colour1 = bit.band(data_block[2], 0x0F) column_index = bit.band(data_block[3], 0x1F) * 12 row_index = bit.band(data_block[4], 0x3F) * 6 - + titlepixels={ + bit.band(data_block[5], 0x3F), + bit.band(data_block[6], 0x3F), + bit.band(data_block[7], 0x3F), + bit.band(data_block[8], 0x3F), + bit.band(data_block[9], 0x3F), + bit.band(data_block[10], 0x3F), + bit.band(data_block[11], 0x3F), + bit.band(data_block[12], 0x3F), + bit.band(data_block[13], 0x3F), + bit.band(data_block[14], 0x3F), + bit.band(data_block[15], 0x3F), + bit.band(data_block[16], 0x3F) + } if xor then - typeStr = "cdgTileBlockXOR" + typeStr = "TILE_BLOCK_XOR" else - typeStr = "cdgTileBlockNormal" + typeStr = "TILE_BLOCK" end - print(string.format("%s [Colour0=%d, Colour1=%d, ColIndex=%d, RowIndex=%d]", typeStr, colour0, colour1, column_index, row_index)) +--~ print(string.format("%s [Colour0=%d, Colour1=%d, ColIndex=%d, RowIndex=%d]", typeStr, colour0, colour1, column_index, row_index)) + self:packCommand(typeStr, colour0, colour1, column_index, row_index, titlepixels) return end function cdgPlayer:cdgDefineTransparentColour(packd) data_block = packd['data'] colour = bit.band(data_block[1], 0x0F) - print (string.format("cdgDefineTransparentColour [Colour=%d]", colour)) +--~ print (string.format("cdgDefineTransparentColour [Colour=%d]", colour)) + self:packCommand("DEF_TRANSP_COL",colour) return end function cdgPlayer:cdgLoadColourTableCommon (packd, tab) if tab == 0 then colourTableStart = 0 - print ("cdgLoadColourTable0..7") +--~ print ("cdgLoadColourTable0..7") else colourTableStart = 8 - print ("cdgLoadColourTable8..15") +--~ print ("cdgLoadColourTable8..15") end for i=0,7 do colourEntry = bit.lshift(bit.band(packd['data'][(2 * i)+1], CDG_MASK), 8) colourEntry = colourEntry + bit.band(packd['data'][(2 * i) + 2], CDG_MASK) colourEntry = bit.bor(bit.rshift(bit.band(colourEntry, 0x3F00), 2), bit.band(colourEntry, 0x003F)) - print (string.format(" Colour %d = 0x%X", (i + 1 + colourTableStart), colourEntry)) +--~ print (string.format(" Colour %d = 0x%X", (i + colourTableStart), colourEntry)) + self.tempdata.colors[#self.tempdata.colors+1]=colourEntry end return end ---[[ - -colourEntry = ((packd['data'][2 * i] & CDG_MASK) << 8) -colourEntry = colourEntry + (packd['data'][(2 * i) + 1] & CDG_MASK) -colourEntry = ((colourEntry & 0x3F00) >> 2) | (colourEntry & 0x003F) - -]] -player=cdgPlayer:init("test.cdg") diff --git a/LoveKaraoke/conf.lua b/LoveKaraoke/conf.lua index fcbcedd..a87289f 100644 --- a/LoveKaraoke/conf.lua +++ b/LoveKaraoke/conf.lua @@ -3,10 +3,10 @@ function love.conf(t) t.version = "0.10.2" -- The LOVE version this game was made for (string) t.console = true -- Attach a console (boolean, Windows only) - t.window.title = "Recording Test" -- The window title (string) + t.window.title = "Oh God This is painful" -- The window title (string) t.window.icon = nil -- Filepath to an image to use as the window's icon (string) - t.window.width = 300 -- The window width (number) - t.window.height = 100 -- The window height (number) + t.window.width = 800 -- The window width (number) + t.window.height = 800 -- The window height (number) t.window.borderless = false -- Remove all border visuals from the window (boolean) t.window.resizable = false -- Let the window be user-resizable (boolean) t.window.minwidth = 1 -- Minimum window width if the window is resizable (number) diff --git a/LoveKaraoke/main.lua b/LoveKaraoke/main.lua index 4311e1a..98855c3 100644 --- a/LoveKaraoke/main.lua +++ b/LoveKaraoke/main.lua @@ -2,75 +2,176 @@ require("core.Library") GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library require("core.GuiManager") require("core.bin") +require("cdgreader") gui.ff.Color=Color.Black -t=gui:newTextLabel("",0,0,300,100) -t.Color=Color.purple -microphone = require("love-microphone") -function peakAmplitude(sounddata) - local peak_amp = -math.huge - for t = 0,sounddata:getSampleCount()-1 do - local amp = math.abs(sounddata:getSample(t)) -- |s(t)| - peak_amp = math.max(peak_amp, amp) - end - return peak_amp -end -function rmsAmplitude(sounddata) - local amp = 0 - for t = 0,sounddata:getSampleCount()-1 do - amp = amp + sounddata:getSample(t)^2 -- (s(t))^2 - end - return math.sqrt(amp / sounddata:getSampleCount()) -end -local device,source -local record={} -local recording=false -function love.load() - print("Opening microphone:", microphone.getDefaultDeviceName()) - device = microphone.openDevice(nil, nil, 0) - source = microphone.newQueueableSource() - device:setDataCallback(function(device, data) - if recording then - source:queue(data) - source:play() - table.insert(record,{data,os.clock()}) - test:setDualDim(nil,nil,nil,nil,nil,nil,peakAmplitude(data)) - test2:setDualDim(nil,nil,nil,nil,nil,nil,rmsAmplitude(data)) +function formatFix(str,n,cap) + if cap then + if #str==8 then + str=str:sub(3,-1) + else + str=string.rep("0",n-#str)..str end - end) - device:start() - multi:newLoop(function() device:poll() end) -end -test0=t:newTextButton("Start Recording","Start Recording",0,0,100,30) -test0:centerX() -test0:centerY() -test0:ApplyGradient{Color.Green,Color.Darken(Color.Green,.25)} -test0:OnReleased(function(b,self) - if self.text=="Start Recording" then - self.text="Stop Recording" - recording=true - elseif self.text=="Stop Recording" then - test0.text="Playing Back!" - recording=false - local step=multi:newStep(1,#record) - step:OnStep(function(self,pos) - source:queue(record[pos][1]) - test:setDualDim(nil,nil,nil,nil,nil,nil,peakAmplitude(record[pos][1])) - test2:setDualDim(nil,nil,nil,nil,nil,nil,rmsAmplitude(record[pos][1])) - if pos>1 then - self:hold(record[pos][2]-record[pos-1][2]) - end - end) - step:OnEnd(function(self) - record={} - self:Destroy() - loop2:Destroy() - test0.text="Start Recording" - end) - loop2=multi:newLoop(function() source:play() end) + else + str=string.rep("0",n-#str)..str end +--~ print(str) + return str +end +function HEX4ToRGB(HEX) + HEX=formatFix(HEX,3) + return tonumber(HEX:sub(1,1),16)*(255)/15, tonumber(HEX:sub(2,2),16)*(255)/15, tonumber(HEX:sub(3,3),16)*(255)/15 +end +function RGBToHEX(r,g,b) + local HEX,L={},{[0]="0",[1]="1",[2]="2",[3]="3",[4]="4",[5]="5",[6]="6",[7]="7",[8]="8",[9]="9",[10]="A",[11]="B",[12]="C",[13]="D",[14]="E",[15]="F"} + HEX[1]=L[(r*15)/255];HEX[2]=L[(g*15)/255];HEX[3]=L[(b*15)/255] + return table.concat(HEX) +end +music=love.audio.newSource("test2.mp3", "stream") +player=cdgPlayer:init("test2.cdg") +COLORS={} +print(#player.colors) +for i=1,#player.colors do + COLORS[i-1]=Color.new(HEX4ToRGB(bin.NumtoHEX(player.colors[i]))) + print(i-1,COLORS[i-1]) +end +BGCOLOR=0 +ImageData=love.image.newImageData(300,216) +Image=love.graphics.newImage(ImageData) +screen=gui:newImageLabel(Image,"SCREEN",0,0,300*2,216*2) +updateFunc=multi:newFunction(function(mself,self) + local data=player:next() + if not data then return end + local cmd=table.remove(data,1) + if cmd=="MEMORY_PRESET" then + local r,g,b=unpack(COLORS[data[1]]) + BGCOLOR=data[1] + for x=0,299 do + for y=0,215 do + ImageData:setPixel(x, y, r, g, b, 255) + end + end + elseif cmd=="TILE_BLOCK_XOR" then + --print(data[1],data[2],COLORS[data[2]]) + local r1,g1,b1=unpack(COLORS[data[1]]) + local r2,g2,b2=unpack(COLORS[data[2]]) + local y,x=data[3],data[4] + local tile={ + formatFix(tostring(bits.new(data[5][1])),6,true), + formatFix(tostring(bits.new(data[5][2])),6,true), + formatFix(tostring(bits.new(data[5][3])),6,true), + formatFix(tostring(bits.new(data[5][4])),6,true), + formatFix(tostring(bits.new(data[5][5])),6,true), + formatFix(tostring(bits.new(data[5][6])),6,true), + formatFix(tostring(bits.new(data[5][7])),6,true), + formatFix(tostring(bits.new(data[5][8])),6,true), + formatFix(tostring(bits.new(data[5][9])),6,true), + formatFix(tostring(bits.new(data[5][10])),6,true), + formatFix(tostring(bits.new(data[5][11])),6,true), + formatFix(tostring(bits.new(data[5][12])),6,true) + } + for yy=1,#tile do + for xx=1,#tile[yy] do + --print(tile[yy]:sub(xx,xx)) + local rc,gc,bc=ImageData:getPixel(x+(xx-1), y+(yy-1)) + local cc=tonumber(RGBToHEX(rc,gc,bc),16) + if tile[yy]:sub(xx,xx)=="0" then + local c1=tonumber(RGBToHEX(r1,g1,b1),16) + local r, g, b = HEX4ToRGB(bin.NumtoHEX(bit.bxor(cc,c1))) + ImageData:setPixel(x+(xx-1), y+(yy-1), r, g, b, 255) + else + local c2=tonumber(RGBToHEX(r2,g2,b2),16) + local r, g, b = HEX4ToRGB(bin.NumtoHEX(bit.bxor(cc,c2))) + ImageData:setPixel(x+(xx-1), y+(yy-1), r, g, b, 255) + end + end + end + mself:hold(.01) + end + -- Update the "Screen" + self:SetImage(love.graphics.newImage(ImageData)) + print(os.clock()) end) -test=t:newFrame("BAR",0,0,0,30,0,0,0) -test:ApplyGradient{Color.Green,Color.Darken(Color.Green,.25)} -test2=t:newFrame("BAR",0,70,0,30,0,0,0) -test2:ApplyGradient{Color.Green,Color.Darken(Color.Green,.25)} ---test:centerY() +music:play() +screen:OnUpdate(updateFunc) +--[[ +test=bin.stream("test.dat",false) +local cmd=player:next() +for i=1,#player.colors do + test:tackE("Color"..(i-1).."|"..bin.NumtoHEX(player.colors[i-1]).."\n") +end +while cmd do + test:tackE(cmd.."\n") + cmd,dat=player:next() +end +test:close() +]] +--~ t=gui:newTextLabel("",0,0,300,100) +--~ t.Color=Color.purple +--~ microphone = require("love-microphone") +--~ function peakAmplitude(sounddata) +--~ local peak_amp = -math.huge +--~ for t = 0,sounddata:getSampleCount()-1 do +--~ local amp = math.abs(sounddata:getSample(t)) -- |s(t)| +--~ peak_amp = math.max(peak_amp, amp) +--~ end +--~ return peak_amp +--~ end +--~ function rmsAmplitude(sounddata) +--~ local amp = 0 +--~ for t = 0,sounddata:getSampleCount()-1 do +--~ amp = amp + sounddata:getSample(t)^2 -- (s(t))^2 +--~ end +--~ return math.sqrt(amp / sounddata:getSampleCount()) +--~ end +--~ local device,source +--~ local record={} +--~ local recording=false +--~ function love.load() +--~ print("Opening microphone:", microphone.getDefaultDeviceName()) +--~ device = microphone.openDevice(nil, nil, 0) +--~ source = microphone.newQueueableSource() +--~ device:setDataCallback(function(device, data) +--~ if recording then +--~ source:queue(data) +--~ source:play() +--~ table.insert(record,{data,os.clock()}) +--~ test:setDualDim(nil,nil,nil,nil,nil,nil,peakAmplitude(data)) +--~ test2:setDualDim(nil,nil,nil,nil,nil,nil,rmsAmplitude(data)) +--~ end +--~ end) +--~ device:start() +--~ multi:newLoop(function() device:poll() end) +--~ end +--~ test0=t:newTextButton("Start Recording","Start Recording",0,0,100,30) +--~ test0:centerX() +--~ test0:centerY() +--~ test0:ApplyGradient{Color.Green,Color.Darken(Color.Green,.25)} +--~ test0:OnReleased(function(b,self) +--~ if self.text=="Start Recording" then +--~ self.text="Stop Recording" +--~ recording=true +--~ elseif self.text=="Stop Recording" then +--~ test0.text="Playing Back!" +--~ recording=false +--~ local step=multi:newStep(1,#record) +--~ step:OnStep(function(self,pos) +--~ source:queue(record[pos][1]) +--~ test:setDualDim(nil,nil,nil,nil,nil,nil,peakAmplitude(record[pos][1])) +--~ test2:setDualDim(nil,nil,nil,nil,nil,nil,rmsAmplitude(record[pos][1])) +--~ if pos>1 then +--~ self:hold(record[pos][2]-record[pos-1][2]) +--~ end +--~ end) +--~ step:OnEnd(function(self) +--~ record={} +--~ self:Destroy() +--~ loop2:Destroy() +--~ test0.text="Start Recording" +--~ end) +--~ loop2=multi:newLoop(function() source:play() end) +--~ end +--~ end) +--~ test=t:newFrame("BAR",0,0,0,30,0,0,0) +--~ test:ApplyGradient{Color.Green,Color.Darken(Color.Green,.25)} +--~ test2=t:newFrame("BAR",0,70,0,30,0,0,0) +--~ test2:ApplyGradient{Color.Green,Color.Darken(Color.Green,.25)} diff --git a/LoveKaraoke/test.cdg b/LoveKaraoke/test.cdg deleted file mode 100644 index 28907dc..0000000 Binary files a/LoveKaraoke/test.cdg and /dev/null differ diff --git a/LoveKaraoke/test2.cdg b/LoveKaraoke/test2.cdg new file mode 100644 index 0000000..23d2753 Binary files /dev/null and b/LoveKaraoke/test2.cdg differ diff --git a/LoveKaraoke/test2.mp3 b/LoveKaraoke/test2.mp3 new file mode 100644 index 0000000..efbf163 Binary files /dev/null and b/LoveKaraoke/test2.mp3 differ diff --git a/ReadMe.md b/ReadMe.md index 680f02f..fd59423 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -4,4 +4,5 @@ - [x] ~~Add decoding for cdg files~~ See cdgreader.lua for that - [ ] Design an easy to use config script for adding your own songs - [ ] Design an interface for the mobile app and PC app +- [ ] Figure out how to save recorded audio to the disk in any format... - [ ] Think of other great ideas \ No newline at end of file