写了一个让Aviutl能通过midi生成PV的脚本,目前还在测试中。
只需要需要在Aviutl安装目录下的Scripts文件夹新建两个文件:
1. midi.obj
--file:
if not videoFile then
videoFile = {file}
else
obj.setfont("", "40")
obj.load("text", "OK")
end
2. midi.anm
--dialog:Import alpha channel/chk, alpha=0;MIDI File, midiPath="";MIDI Track, midiTrack="";Flip horizontal/chk, flipH=1;Flip vertical/chk, flipV=0;Other effects, effects={};
function getByteValue(str, begin)
local pos, value = begin, 0
while string.byte(str, pos, pos) > 128 do
value = value * 128 + string.byte(str, pos, pos) % 128
pos = pos + 1
end
value = value * 128 + string.byte(str, pos, pos)
pos = pos + 1
return value, pos
end
function getValue(str, begin, last)
local i, value = begin, 0
while i <= last do
value = value + string.byte(str, i, i) * 256 ^ (last-i)
i = i + 1
end
return value
end
function parseMidi(filePath)
-- Open midi file
local midiFile = io.open(filePath, "rb")
if midiFile == nil then
obj.load("text", "The file does not exist")
return false
end
-- Load content
local midiContent = midiFile:read("*all")
local midiContentLength = string.len(midiContent)
if string.sub(midiContent, 1, 4) ~= "MThd" then
obj.load("text", "This is not a midi file")
midiFile:close()
return false
end
-- Parse content
midiType = getValue(midiContent, 9, 10)
midiTracksCount = getValue(midiContent, 11, 12)
midiDeltaTime = getValue(midiContent, 13, 14)
notes = {}
-- Parse MIDI tracks
local i, trackId=15, 0
while i < midiContentLength do
if string.sub(midiContent, i, i+3) ~= "MTrk" then
break
end
local trackLength = getValue(midiContent, i+4, i+7)
local trackNotesCount = 0
trackId = trackId + 1
-- Parse MIDI Events
local j = i + 8
local extraOffset = 0
notes[trackId] = {}
while j < i + trackLength + 8 do
local eventOffset, nextPos = getByteValue(midiContent, j)
j = nextPos
local eventType = getValue(midiContent, j, j)
if math.floor(eventType / 16) == 8 then -- End of a note
extraOffset = extraOffset + eventOffset
j = j + 3
elseif math.floor(eventType / 16) == 9 then -- Beginning of a note
trackNotesCount = trackNotesCount + 1
notes[trackId][trackNotesCount] = (eventOffset+extraOffset)/midiDeltaTime
-- print(trackNotesCount, (eventOffset+extraOffset)/midiDeltaTime)
extraOffset = 0
j = j + 3
elseif math.floor(eventType / 16) == 10 then -- Key after touch
extraOffset = extraOffset + eventOffset
j = j + 3
elseif math.floor(eventType / 16) == 11 then -- Controller
extraOffset = extraOffset + eventOffset
j = j + 3
elseif math.floor(eventType / 16) == 12 then -- Change the instrument
extraOffset = extraOffset + eventOffset
j = j + 2
elseif math.floor(eventType / 16) == 13 then -- Track after touch??
extraOffset = extraOffset + eventOffset
j = j + 2
elseif math.floor(eventType / 16) == 14 then -- Portamento
extraOffset = extraOffset + eventOffset
j = j + 3
elseif eventType == 240 then -- System code?
extraOffset = extraOffset + eventOffset
local eventLength, nextPos = getByteValue(midiContent, j+1)
j = nextPos + eventLength
elseif eventType == 255 then -- Information
local informType = getValue(midiContent, j+1, j+1)
local informLength, nextPos = getByteValue(midiContent, j+2)
if informType == 3 then -- Track's name
trackName = string.sub(midiContent, nextPos, nextPos + informLength - 1)
elseif informType == 81 then
midiTempo = 60*1000000/getValue(midiContent, nextPos, nextPos+2)
elseif informType == 88 then
beatNum = getValue(midiContent, nextPos , nextPos )
beatDen = getValue(midiContent, nextPos+1, nextPos+1)
beatClk = getValue(midiContent, nextPos+2, nextPos+2)
beatCnt = getValue(midiContent, nextPos+3, nextPos+3)
end
j = nextPos + informLength
end
end
-- print(trackId,trackName, trackNotesCount)
i = i + trackLength + 8
end
-- Succeeded
midiFile:close()
return true
end
function convertFrame(frame, frameRate)
return frame/frameRate*midiTempo/60
end
function getNoteId(pos, trackId)
local totalTime = 0
for i=1,#notes[trackId]-1 do
totalTime = totalTime + notes[trackId][i]
if totalTime + notes[trackId][i+1] > pos then
return i, totalTime
end
end
return #notes[trackId], totalTime+notes[trackId][#notes[trackId]]
end
if videoFile then
if midiPath then
parseMidi(midiPath)
local id, lastTime = getNoteId(convertFrame(obj.frame+2, obj.framerate), tonumber(midiTrack))
obj.load("movie", videoFile[1], obj.time-lastTime/(midiTempo/60), alpha)
if id % 2 == 0 then
if flipH==1 then
obj.effect("Reversal", "Flip horizontal", 1)
end
if flipV==1 then
obj.effect("Reversal", "Flip vertical", 1)
end
if #effects~=0 then
for i=1,#effects do
obj.effect(unpack(effects[i]))
end
end
end
end
end
目前已知的bug:
-
多音符会出现奇怪的效果
-
更换midi后无法及时更新画面