|
|
|
10-10-2021, 06:10 PM
|
#1
|
Human being with feelings
Join Date: Dec 2015
Posts: 172
|
load WAV into LUA array (faster) - how?
Hi, guys! Please help me
I need to load wav file into the table (self.chan[1] for left, and self.chan[2] for right channels). I'm making this based on EUGEN27771's awesome script "Sample Editor". Here'what I got (not optimized yet):
Code:
WAVE = {}
function WAVE:setFile(filename)
if not filename then
--local Item, Take, Source, Source_type
------------------------------------------------------------------
self.Item = reaper.GetSelectedMediaItem(0, 0)
if self.Item then
self.Take = reaper.GetActiveTake( self.Item)
else
return "No Selected Items!"
end
if self.Take then
self.Source = reaper.GetMediaItemTake_Source( self.Take)
else
return "Empty item!"
end
if self.Source then
self.Source_type = reaper.GetMediaSourceType( self.Source, "")
end
------------------------------------------------------------------
if self.Source_type ~= "WAVE" then
return "Invalide Type - " .. self.Source_type
end
self.filePath = reaper.GetMediaSourceFileName( self.Source, "") -- Source FilePath
else
self.filePath = filename
end
return 'Loaded OK!'
end
function WAVE:load(filename)
if self:setFile(filename)~='Loaded OK!' then return false end
local file = io.open(self.filePath, "rb") -- open file, rb mode
if not file then return "File not available!" end -- if not available
--------------------------------------------------------
-- RIFF ------------------------------------------------
--------------------------------------------------------
local Data = file:read(12) -- read(12)
----------
local RIFF_ChunkID, RIFF_chunkSize, RIFF_Type
RIFF_ChunkID, -- "RIFF"
RIFF_chunkSize, -- FileSize - 8
RIFF_Type = -- Source Type(for WAV = "WAVE")
string.unpack("<c4 I4 c4", Data) -------------------------------->>>> unpack
--- FileSize ---------------------
local FileSize = RIFF_chunkSize + 8
--- find "fmt ","data" section ---
local find_lim = math.min(FileSize, 1024 ^ 2)
file:seek("set")
Data = file:read(find_lim)
local start_fmt = Data:find("fmt ")
local start_data = Data:find("data")
----------------------------------
if not start_fmt or not start_data then
return "'data' or 'fmt ' not found !"
end
if start_fmt > start_data then
return "Section 'data' before section 'fmt ' !"
end
-------------------------------------
-- "fmt ", "data" sub-chunks --------
-------------------------------------
---- fmt ----
file:seek("set", start_fmt - 1) -- set position
Data = file:read(24) -- read(24)
local fmt_ChunkID, fmt_ChunkDataSize, audioFormat, nchans, srate, byterate,
blockalign, bitspersample
fmt_ChunkID, -- "fmt "
fmt_ChunkDataSize, -- fmt Size (16 for pcm)
audioFormat, -- Compression_code(1 or 3 need)
nchans, -- Number_of_channels
srate, -- Sample_rate
byterate, -- Averagebytes_per_second
blockalign, -- block_align
bitspersample = -- Significant_bits_per_sample
string.unpack("< c4 I4 I2 I2 I4 I4 I2 I2", Data) ------------->>>> unpack
---- data ----
file:seek("set", start_data - 1) -- set position
Data = file:read(8) -- read(8)
local data_ChunkID, data_ChunkDataSize
data_ChunkID, -- "data"
data_ChunkDataSize = -- data Size
string.unpack("< c4 I4", Data) ------------------------------->>>> unpack
-------------------------------------
local Datablock, Datasize, Datalenght
Datablock = start_data + 8 -- Datablock start - 1-based !!!
Datalenght = data_ChunkDataSize // blockalign -- Datalenght to samples(all channels)
local D1 = Datablock -- Start_Byte
local D2 = D1 + Datalenght * blockalign
-- local D1 = Datablock + (sel_start_in_smpls-1) * blockalign -- Start_Byte
-- local D2 = D1 + sel_range_in_smpls * blockalign
----------
file:seek("set", D1 - 1)
local SMPLS = file:read(Datalenght * blockalign)
file:close() --------------- CLOSE FILE
-----------------------------------------------------------------------------------
-- Get samples --------------------------------------------------------------------
-----------------------------------------------------------------------------------
local buf = {} -- Its sample buffer!
for i = 1, nchans do
buf[i]={}
end
local Pfmt, Bps -- Pack format, BYTES per sample ------
if bitspersample == 16 then
Pfmt = "<i2";
Bps = 2
end
if bitspersample == 24 then
Pfmt = "<i3";
Bps = 3
end
if bitspersample == 32 then
Pfmt = "<f";
Bps = 4
end
if bitspersample == 64 then
Pfmt = "<d";
Bps = 8
end
local b = 1
local Srng
if Pfmt == "<f" or Pfmt == "<d" then
Srng = 1
else
Srng = 2 ^ (bitspersample - 1)
end
------------------------------------------------------
-- Unpuck values(samples) ----------------------------
------------------------------------------------------
for i = 1, Datalenght do
for c = 1, nchans do
buf[c][i] = string.unpack(Pfmt, SMPLS:sub(b, b + Bps - 1)) /Srng -- val to buffer
b = b + Bps
end
end
---------------------------------------------------------------------
-- Define self values ----------------------------------------------
---------------------------------------------------------------------
self.nchans = nchans
self.srate = srate
self.len = Datalenght
self.bitspersample = bitspersample
self.chan={}
self.pfmt = Pfmt
for c=1, nchans do
self.chan[c] = buf[c]
end
self.Bps = Bps
--------------------------------
-- Info For Editing and Undo --
--------------------------------
self.D1 = D1 -- D1 point
self.D2 = D2 -- D2 point
self.SMPLS = SMPLS -- Original SMPLS part
return true
end
It loads binary file to a string SMPLS and then unpacks it to the array.
So, at the "UNPUCK the values" it goes slow for 40-50 MB Wave Files.
String.unpack(Pfmt, SMPLS:gsub ... slows the process
How can we make it faster? Is there a way to unpack binary file to a table faster than this?
Last edited by kartalex; 10-10-2021 at 06:27 PM.
|
|
|
10-10-2021, 09:00 PM
|
#2
|
Human being with feelings
Join Date: Oct 2013
Location: Moscow, Russia
Posts: 3,984
|
sunpack = string.unpack in the start
then use sunpack on the script
probably will be a little bit faster.
Why not use project loaded media and then loop with Audio accessor?
|
|
|
10-13-2021, 02:24 AM
|
#3
|
Human being with feelings
Join Date: Dec 2015
Posts: 172
|
Hi!
I tried to make it with AudioAccessor(based on yours "Align Takes")
It loads file into array chan[], but still feels slow (I try to load 3 min stereo wav file)
Is there something to optimize here?
Code:
local item, take
item = reaper.GetSelectedMediaItem(0, 0)
if item then
take = reaper.GetActiveTake(item)
else
return false
end
if take == nil then return false end
local item_len = reaper.GetMediaItemInfo_Value(item, 'D_LENGTH')
local accessor = reaper.CreateTakeAudioAccessor(take)
local src = reaper.GetMediaItemTake_Source(take)
local numch = reaper.GetMediaSourceNumChannels(src)
local rate = reaper.GetMediaSourceSampleRate(src)
local size = 4096
chan = {} -- channel data is collected to this table
-- Initialize channel_data table
for i = 1, numch do chan[i] = {} end
local window_sec = size / rate -- ms
local ct = 1
buffer = reaper.new_array(size * numch)
for read_pos = 0, item_len, window_sec do
reaper.GetAudioAccessorSamples(accessor, -- AudioAccessor
rate, -- samplerate
numch, -- numch, -- numchannels
read_pos, -- starttime_sec
size, -- numsamplesperchannel
buffer) -- samplebuffer
for i = 0, size-1, numch do
for ch = 1, numch do
chan[ch][ct] = buffer[i + ch]
end
ct = ct + 1
end
end
buffer.clear()
reaper.DestroyAudioAccessor(accessor)
Last edited by kartalex; 10-13-2021 at 04:53 AM.
|
|
|
10-13-2021, 02:45 AM
|
#4
|
Human being with feelings
Join Date: Oct 2013
Location: Moscow, Russia
Posts: 3,984
|
yes, rewrite it in EEL
|
|
|
10-13-2021, 03:08 AM
|
#5
|
Human being with feelings
Join Date: Dec 2015
Posts: 172
|
Quote:
Originally Posted by mpl
yes, rewrite it in EEL
|
I like Lua for it's readability..
But can we make a function in EEL and pass results into LUA script? I think ExtState is not gonna work because of the size of the array
|
|
|
10-13-2021, 03:29 AM
|
#6
|
Human being with feelings
Join Date: Dec 2015
Posts: 172
|
So, I made chan[] local
It sped up the operation x3 times. (1.7 sec vs 4.8 sec on my laptop)
The AudioAccessor itself works pretty fast.
if I bypass this part
Code:
-- for i = 0, size-1, numch do
-- for ch = 1, numch do
-- chan[ch][ct] = buffer[i + ch]
-- end
-- ct = ct + 1
-- end
it loads file into buffer in 0.05 sec
So, this part is slow.. Making array local made it faster, but still it is not fast enough.
How can we get the data from buffer faster than this?
Last edited by kartalex; 10-13-2021 at 04:53 AM.
|
|
|
10-13-2021, 04:58 AM
|
#7
|
Human being with feelings
Join Date: Dec 2017
Location: Sunny Siberian Islands
Posts: 962
|
I guess EEL is the only solution. I've tried every possible way to speed up audio in my script. Many of them either gave a very small increase, or did not give an increase at all.
Take a look at this thread, there are several interesting tests, including a comparison of EEL and LUA: https://forums.cockos.com/showthread.php?p=2291045
Also, there are recommendations for optimizing code with an audio assessor (with useful github links): https://forum.cockos.com/showpost.ph...&postcount=216
Good luck!
|
|
|
Thread Tools |
|
Display Modes |
Linear Mode
|
Posting Rules
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is Off
|
|
|
All times are GMT -7. The time now is 02:45 PM.
|