Old 10-23-2020, 09:45 PM   #1
MusoBob
Human being with feelings
 
MusoBob's Avatar
 
Join Date: Sep 2014
Posts: 2,643
Default Read ACID Info from source WAV [SOLVED]

I need to read the source wav file, Reaper will read the bpm but I need to read the other info like root note.

Here's Acidizer.zip if you are using Win 10 run it in Vista compatibility mode.

Code:
** The acid chunk goes a little something like this:
**
** 4 bytes          'acid'
** 4 bytes (int)     length of chunk starting at next byte
**
** 4 bytes (int)     type of file:
**        this appears to be a bit mask,however some combinations
**        are probably impossible and/or qualified as "errors"
**
**        0x01 On: One Shot         Off: Loop
**        0x02 On: Root note is Set Off: No root
**        0x04 On: Stretch is On,   Off: Strech is OFF
**        0x08 On: Disk Based       Off: Ram based
**        0x10 On: ??????????       Off: ????????? (Acidizer puts that ON)
**
** 2 bytes (short)      root note
**        if type 0x10 is OFF : [C,C#,(...),B] -> [0x30 to 0x3B]
**        if type 0x10 is ON  : [C,C#,(...),B] -> [0x3C to 0x47]
**         (both types fit on same MIDI pitch albeit different octaves, so who cares)
**
** 2 bytes (short)      ??? always set to 0x8000
** 4 bytes (float)      ??? seems to be always 0
** 4 bytes (int)        number of beats
** 2 bytes (short)      meter denominator   //always 4 in SF/ACID
** 2 bytes (short)      meter numerator     //always 4 in SF/ACID
**                      //are we sure about the order?? usually its num/denom
** 4 bytes (float)      tempo
__________________
ReaTrakStudio Chord Track for Reaper forum
www.reatrak.com
STASH Downloads https://stash.reaper.fm/u/ReaTrak

Last edited by MusoBob; 11-29-2020 at 06:21 PM.
MusoBob is offline   Reply With Quote
Old 11-26-2020, 06:50 PM   #2
MusoBob
Human being with feelings
 
MusoBob's Avatar
 
Join Date: Sep 2014
Posts: 2,643
Default

I wrote one for you here MusoBob.
So now you can make a script to fit bass loops to the chord track.
EDIT: It to read the selected item source.

Code:
--------------------------------------------------------------------------
-- script to extract information placed by ACID in a wav file           --
--------------------------------------------------------------------------

--Information structure
-----------------------
-- 4 bytes          'acid' the signature of the start of the information block
-- 4 bytes (int)     length of chunk starting at next byte
--
-- 4 bytes (int)     type of file:
--        this appears to be a bit mask,however some combinations
--        are probably impossible and/or qualified as "errors"
--
--        0x01 On: One Shot         Off: Loop
--        0x02 On: Root note is Set Off: No root
--        0x04 On: Stretch is On,   Off: Strech is OFF
--        0x08 On: Disk Based       Off: Ram based
--        0x10 On: ??????????       Off: ????????? (Acidizer puts that ON)
--
-- 2 bytes (short)   root note
--        if type 0x10 is OFF : [C,C#,(...),B] -> [0x30 to 0x3B]
--        if type 0x10 is ON  : [C,C#,(...),B] -> [0x3C to 0x47]
--         (both types fit on same MIDI pitch albeit different octaves, so who cares)
--
-- 2 bytes (short)   ??? always set to 0x8000
-- 4 bytes (float)   ??? seems to be always 0
-- 4 bytes (int)     number of beats
-- 2 bytes (short)   meter denominator   //always 4 in SF/ACID
-- 2 bytes (short)   meter numerator     //always 4 in SF/ACID
--                   //are we sure about the order?? usually its num/denom
-- 4 bytes (float)   tempo
--

function print(value)
  
    reaper.ShowConsoleMsg(tostring(value) .. "\n")
  
end

function round(val, decimal)
  local exp = decimal and 10^decimal or 1
  return math.ceil(val * exp - 0.5) / exp
end

-- convert hex to floating point routine
----------------------------------------
tab={["0"]="0000",["1"]="0001",["2"]="0010",["3"]="0011",
         ["4"]="0100",["5"]="0101",["6"]="0110",["7"]="0111",
         ["8"]="1000",["9"]="1001",["a"]="1010",["b"]="1011",
         ["c"]="1100",["d"]="1101",["e"]="1110",["f"]="1111",
         ["A"]="1010",["B"]="1011",["C"]="1100",["D"]="1101",["E"]="1110",["F"]="1111"}
function hexToFloat(str)
 
    local str1=""
    local a,z
    for z=1,string.len(str) do
        a=string.sub(str,z,z)
        str1=str1..tab[a]
    end
    local pm=string.sub(str1,1,1)
    local exp=string.sub(str1,2,9)
    local c=tonumber(exp,2)-127
    local p=2^c
    local man="1"..string.sub(str1,10,32)
    local x=0
    for z=1,string.len(man) do
        if string.sub(man,z,z)=="1" then
            x=x+p
        end
        p=p/2
    end
    if pm=="1" then
        x= -x
    end    
    return(x)
end

--main routine
---------------------------------------------------------------------------------------------
sel_item = reaper.GetSelectedMediaItem( 0, 0 )
if not sel_item then goto finish end

item_take = reaper.GetMediaItemTake( sel_item, 0 )
      
source = reaper.GetMediaItemTake_Source( item_take )
      
filename = reaper.GetMediaSourceFileName(source, "")


local f = assert(io.open(filename, "rb"))
local byte_count = f:seek("end")
print("file has ", byte_count, " bytes")
i = 1
while true do
  f:seek("set", byte_count - i)
  read_byte4 = f:read(1)
  current_byte = string.format("%02X",string.byte(read_byte4))
  --print(current_byte)
  if current_byte == "64" then --get next byte
    f:seek("set", byte_count - i -1)
    read_byte3 = f:read(1)
    current_byte = string.format("%02X",string.byte(read_byte3))
    if current_byte == "69" then --get next byte
      f:seek("set", byte_count - i - 2)
      read_byte2 = f:read(1)
      current_byte = string.format("%02X",string.byte(read_byte2))
      if current_byte == "63" then --get next byte
        f:seek("set", byte_count - i - 3)
        read_byte1 = f:read(1)
        current_byte = string.format("%02X",string.byte(read_byte1))
        if current_byte == "61" then break
        end
      end
    end
  end
  i = i +1
  --if i >= byte_count - 5 then print("File has not been ACIDized, no 'acid' signature found") fail = true break end
  if i > 1000 then 
    print("File has not been ACIDized, no 'acid' signature found") 
    fail = true
    break goto finish end
  end
if not fail then 
data_start = byte_count - i + 1
print("data starts at byte ", data_start)

--read data bytes
-------------------------
f:seek("set", data_start)
chunk_length4 = f:read(1)
chunk_length3 = f:read(1)
chunk_length2 = f:read(1)
chunk_length1 = f:read(1)
current_data = string.format("%02X",string.byte(chunk_length1))..string.format("%02X",string.byte(chunk_length2))..string.format("%02X",string.byte(chunk_length3))..string.format("%02X",string.byte(chunk_length4))
print("ACID info chunk length (4 Bytes)= ".. current_data)
print("ACID info chunk length (decimal)= ".. tonumber(current_data, 16))
-----------------------------------

file_type4 = string.byte(f:read(1))
file_type3 = string.byte(f:read(1))
file_type2 = string.byte(f:read(1))
file_type1 = string.byte(f:read(1))
current_data = string.format("%02X", file_type1)..string.format("%02X", file_type2)..string.format("%02X", file_type3)..string.format("%02X", file_type4)
print("file type (4 Bytes)=                ", current_data)
if (string.byte(file_type4) & 0x01)~= 0 then print("\t\t\tOne shot") else print("\t\t\tLoop") end
if (string.byte(file_type4) & 0x02)~= 0 then print("\t\t\tRoot note is set") else print("\t\t\tNo root note") end
if (string.byte(file_type4) & 0x04)~= 0 then print("\t\t\tStretch is on") else print("\t\t\tStretch is off") end
if (string.byte(file_type4) & 0x08)~= 0 then print("\t\t\tDisk based") else print("\t\t\tRAM based") end
if (string.byte(file_type4) & 0x10)~= 0 then print("\t\t\tbit 4 set") else print("\t\t\tbit 4 off") end
if (string.byte(file_type4) & 0x20)~= 0 then print("\t\t\tbit 5 set") else print("\t\t\tbit 5 off") end
if (string.byte(file_type4) & 0x40)~= 0 then print("\t\t\tbit 6 set") else print("\t\t\tbit 6 off") end
if (string.byte(file_type4) & 0x80)~= 0 then print("\t\t\tbit 7 set") else print("\t\t\tbit 7 off") end
----------------------

root_note2 = f:read(1)
root_note1 = f:read(1)
current_data = string.format("%02X",string.byte(root_note1))..string.format("%02X",string.byte(root_note2))
print("root note (2 Bytes)=      ".. current_data)

notes = {
"C#-1","D-1","D#-1","E-1","F-1","F#-1","G-1","G#-1","A-1","A#-1","B-1",
"C0","C#0","D0","D#0","E0","F0","F#0","G0","G#0","A0","A#0","B0",
"C1","C#1","D1","D#1","E1","F1","F#1","G1","G#1","A1","A#1","B1",
"C2","C#2","D2","D#2","E2","F2","F#2","G2","G#2","A2","A#2","B2",
"C3","C#3","D3","D#3","E3","F3","F#3","G3","G#3","A3","A#3","B3",
"C4","C#4","D4","D#4","E4","F4","F#4","G4","G#4","A4","A#4","B4",
"C5","C#5","D5","D#5","E5","F5","F#5","G5","G#5","A5","A#5","B5",
"C6","C#6","D6","D#6","E6","F6","F#6","G6","G#6","A6","A#6","B6",
"C7","C#7","D7","D#7","E7","F7","F#7","G7","G#7","A7","A#7","B7",
"C8","C#8","D8","D#8","E8","F8","F#8","G8","G#8","A8","A#8","B8",
"C9","C#9","D9","D#9","E9","F9","F#9","G9","G#9","A9","A#9","B9",
"C10","C#10","D10","D#10","E10","F10","F#10","G10","G#10","A10","A#10","B10"}

print("Root note is:\t\t".. notes[tonumber(current_data, 16)]) 
-------------------------

always_8000_2 = f:read(1)
always_8000_1 = f:read(1)
current_data = string.format("%02X",string.byte(always_8000_1))..string.format("%02X",string.byte(always_8000_2))
print("always 8000 (2 Bytes)=      ".. current_data)
----------------------

always_0_4 = f:read(1)
always_0_3 = f:read(1)
always_0_2 = f:read(1)
always_0_1 = f:read(1)
current_data = string.format("%02X",string.byte(always_0_1))..string.format("%02X",string.byte(always_0_2))..string.format("%02X",string.byte(always_0_3))..string.format("%02X",string.byte(always_0_4))
print("always 00000000  (4 Bytes)= ".. current_data)
----------------------

num_beats4 = f:read(1)
num_beats3 = f:read(1)
num_beats2 = f:read(1)
num_beats1 = f:read(1)
current_data = string.format("%02X",string.byte(num_beats1))..string.format("%02X",string.byte(num_beats2))..string.format("%02X",string.byte(num_beats3))..string.format("%02X",string.byte(num_beats4))
print("number of beats (4 Bytes)= ".. current_data)
----------------------

meter_den2 = f:read(1)
meter_den1 = f:read(1)
current_data = string.format("%02X",string.byte(meter_den1))..string.format("%02X",string.byte(meter_den2))
print("meter_denominator = (2 Bytes)".. current_data)
----------------------

meter_num2 = f:read(1)
meter_num1 = f:read(1)
current_data = string.format("%02X",string.byte(meter_num1))..string.format("%02X",string.byte(meter_num2))
print("meter_numerator = (2 Bytes)".. current_data)
---------------------

--unknown = f:read(2)
--current_data = string.format("%04X",string.byte(unknown))
--print("unknown =       ", current_data)
------------------

tempo4 = f:read(1)
tempo3 = f:read(1)
tempo2 = f:read(1)
tempo1 = f:read(1)
current_data = string.format("%02X",string.byte(tempo1))..string.format("%02X",string.byte(tempo2))..string.format("%02X",string.byte(tempo3))..string.format("%02X",string.byte(tempo4))
print("tempo (4 Bytes)=         ".. current_data)
print("tempo decimal =          ".. hexToFloat(current_data))


end
assert(f:close())

::finish::
__________________
ReaTrakStudio Chord Track for Reaper forum
www.reatrak.com
STASH Downloads https://stash.reaper.fm/u/ReaTrak

Last edited by MusoBob; 11-30-2020 at 11:43 PM.
MusoBob is offline   Reply With Quote
Old 11-30-2020, 10:51 PM   #3
MusoBob
Human being with feelings
 
MusoBob's Avatar
 
Join Date: Sep 2014
Posts: 2,643
Default

This one should work better:
Code:
--------------------------------------------------------------------------
-- script to extract information placed by ACID in a wav file           --
--------------------------------------------------------------------------

--Information structure
-----------------------
-- 4 bytes          'acid' the signature of the start of the information block
-- 4 bytes (int)     length of chunk starting at next byte
--
-- 4 bytes (int)     type of file:
--        this appears to be a bit mask,however some combinations
--        are probably impossible and/or qualified as "errors"
--
--        0x01 On: One Shot         Off: Loop
--        0x02 On: Root note is Set Off: No root
--        0x04 On: Stretch is On,   Off: Strech is OFF
--        0x08 On: Disk Based       Off: Ram based
--        0x10 On: ??????????       Off: ????????? (Acidizer puts that ON)
--
-- 2 bytes (short)   root note
--        if type 0x10 is OFF : [C,C#,(...),B] -> [0x30 to 0x3B]
--        if type 0x10 is ON  : [C,C#,(...),B] -> [0x3C to 0x47]
--         (both types fit on same MIDI pitch albeit different octaves, so who cares)
--
-- 2 bytes (short)   ??? always set to 0x8000
-- 4 bytes (float)   ??? seems to be always 0
-- 4 bytes (int)     number of beats
-- 2 bytes (short)   meter denominator   //always 4 in SF/ACID
-- 2 bytes (short)   meter numerator     //always 4 in SF/ACID
--                   //are we sure about the order?? usually its num/denom
-- 4 bytes (float)   tempo
--

function print(value)
  
    reaper.ShowConsoleMsg(tostring(value) .. "\n")
  
end

function round(val, decimal)
  local exp = decimal and 10^decimal or 1
  return math.ceil(val * exp - 0.5) / exp
end

-- convert hex to floating point routine
----------------------------------------
tab={["0"]="0000",["1"]="0001",["2"]="0010",["3"]="0011",
         ["4"]="0100",["5"]="0101",["6"]="0110",["7"]="0111",
         ["8"]="1000",["9"]="1001",["a"]="1010",["b"]="1011",
         ["c"]="1100",["d"]="1101",["e"]="1110",["f"]="1111",
         ["A"]="1010",["B"]="1011",["C"]="1100",["D"]="1101",["E"]="1110",["F"]="1111"}
function hexToFloat(str)
 
    local str1=""
    local a,z
    for z=1,string.len(str) do
        a=string.sub(str,z,z)
        str1=str1..tab[a]
    end
    local pm=string.sub(str1,1,1)
    local exp=string.sub(str1,2,9)
    local c=tonumber(exp,2)-127
    local p=2^c
    local man="1"..string.sub(str1,10,32)
    local x=0
    for z=1,string.len(man) do
        if string.sub(man,z,z)=="1" then
            x=x+p
        end
        p=p/2
    end
    if pm=="1" then
        x= -x
    end    
    return(x)
end

--main routine
---------------------------------------------------------------------------------------------
sel_item = reaper.GetSelectedMediaItem( 0, 0 )
if not sel_item then goto finish end

item_take = reaper.GetMediaItemTake( sel_item, 0 )
      
source = reaper.GetMediaItemTake_Source( item_take )
      
filename = reaper.GetMediaSourceFileName(source, "")


local f = assert(io.open(filename, "rb"))
local byte_count = f:seek("end")
print("file has ", byte_count, " bytes")
i = 1
while true do
  f:seek("set", byte_count - i)
  read_byte4 = f:read(1)
  current_byte = string.format("%02X",string.byte(read_byte4))
  --print(current_byte)
  if current_byte == "64" then --get next byte
    f:seek("set", byte_count - i -1)
    read_byte3 = f:read(1)
    current_byte = string.format("%02X",string.byte(read_byte3))
    if current_byte == "69" then --get next byte
      f:seek("set", byte_count - i - 2)
      read_byte2 = f:read(1)
      current_byte = string.format("%02X",string.byte(read_byte2))
      if current_byte == "63" then --get next byte
        f:seek("set", byte_count - i - 3)
        read_byte1 = f:read(1)
        current_byte = string.format("%02X",string.byte(read_byte1))
        if current_byte == "61" then break
        end
      end
    end
  end
  i = i +1
  --if i >= byte_count - 5 then print("File has not been ACIDized, no 'acid' signature found") fail = true break end
  if i > 1000 then 
    print("File has not been ACIDized, no 'acid' signature found") 
    fail = true
    break goto finish end
  end
if not fail then 
data_start = byte_count - i + 1
print("data starts at byte ", data_start)

--read data bytes
-------------------------
f:seek("set", data_start)
chunk_length4 = f:read(1)
chunk_length3 = f:read(1)
chunk_length2 = f:read(1)
chunk_length1 = f:read(1)
current_data = string.format("%02X",string.byte(chunk_length1))..string.format("%02X",string.byte(chunk_length2))..string.format("%02X",string.byte(chunk_length3))..string.format("%02X",string.byte(chunk_length4))
print("ACID info chunk length (4 Bytes)= ".. current_data)
print("ACID info chunk length (decimal)= ".. tonumber(current_data, 16))
-----------------------------------

file_type4 = string.byte(f:read(1))
file_type3 = string.byte(f:read(1))
file_type2 = string.byte(f:read(1))
file_type1 = string.byte(f:read(1))
current_data = string.format("%02X", file_type1)..string.format("%02X", file_type2)..string.format("%02X", file_type3)..string.format("%02X", file_type4)
print("file type (4 Bytes)=                ", current_data)
if (string.byte(file_type4) & 0x01)~= 0 then print("\t\t\tOne shot") else print("\t\t\tLoop") end
if (string.byte(file_type4) & 0x02)~= 0 then print("\t\t\tRoot note is set") else print("\t\t\tNo root note") end
if (string.byte(file_type4) & 0x04)~= 0 then print("\t\t\tStretch is on") else print("\t\t\tStretch is off") end
if (string.byte(file_type4) & 0x08)~= 0 then print("\t\t\tDisk based") else print("\t\t\tRAM based") end
if (string.byte(file_type4) & 0x10)~= 0 then print("\t\t\tbit 4 set") else print("\t\t\tbit 4 off") end
if (string.byte(file_type4) & 0x20)~= 0 then print("\t\t\tbit 5 set") else print("\t\t\tbit 5 off") end
if (string.byte(file_type4) & 0x40)~= 0 then print("\t\t\tbit 6 set") else print("\t\t\tbit 6 off") end
if (string.byte(file_type4) & 0x80)~= 0 then print("\t\t\tbit 7 set") else print("\t\t\tbit 7 off") end
----------------------

root_note2 = f:read(1)
root_note1 = f:read(1)
current_data = string.format("%02X",string.byte(root_note1))..string.format("%02X",string.byte(root_note2))
print("root note (2 Bytes)=      ".. current_data)

notes = {
"C#-1","D-1","D#-1","E-1","F-1","F#-1","G-1","G#-1","A-1","A#-1","B-1",
"C0","C#0","D0","D#0","E0","F0","F#0","G0","G#0","A0","A#0","B0",
"C1","C#1","D1","D#1","E1","F1","F#1","G1","G#1","A1","A#1","B1",
"C2","C#2","D2","D#2","E2","F2","F#2","G2","G#2","A2","A#2","B2",
"C3","C#3","D3","D#3","E3","F3","F#3","G3","G#3","A3","A#3","B3",
"C4","C#4","D4","D#4","E4","F4","F#4","G4","G#4","A4","A#4","B4",
"C5","C#5","D5","D#5","E5","F5","F#5","G5","G#5","A5","A#5","B5",
"C6","C#6","D6","D#6","E6","F6","F#6","G6","G#6","A6","A#6","B6",
"C7","C#7","D7","D#7","E7","F7","F#7","G7","G#7","A7","A#7","B7",
"C8","C#8","D8","D#8","E8","F8","F#8","G8","G#8","A8","A#8","B8",
"C9","C#9","D9","D#9","E9","F9","F#9","G9","G#9","A9","A#9","B9",
"C10","C#10","D10","D#10","E10","F10","F#10","G10","G#10","A10","A#10","B10"}

print("Root note is:\t\t".. notes[tonumber(current_data, 16)]) 
-------------------------

always_8000_2 = f:read(1)
always_8000_1 = f:read(1)
current_data = string.format("%02X",string.byte(always_8000_1))..string.format("%02X",string.byte(always_8000_2))
print("always 8000 (2 Bytes)=      ".. current_data)
----------------------

always_0_4 = f:read(1)
always_0_3 = f:read(1)
always_0_2 = f:read(1)
always_0_1 = f:read(1)
current_data = string.format("%02X",string.byte(always_0_1))..string.format("%02X",string.byte(always_0_2))..string.format("%02X",string.byte(always_0_3))..string.format("%02X",string.byte(always_0_4))
print("always 00000000  (4 Bytes)= ".. current_data)
----------------------

num_beats4 = f:read(1)
num_beats3 = f:read(1)
num_beats2 = f:read(1)
num_beats1 = f:read(1)
current_data = string.format("%02X",string.byte(num_beats1))..string.format("%02X",string.byte(num_beats2))..string.format("%02X",string.byte(num_beats3))..string.format("%02X",string.byte(num_beats4))
print("number of beats (4 Bytes)= ".. current_data)
----------------------

meter_den2 = f:read(1)
meter_den1 = f:read(1)
current_data = string.format("%02X",string.byte(meter_den1))..string.format("%02X",string.byte(meter_den2))
print("meter_denominator = (2 Bytes)".. current_data)
----------------------

meter_num2 = f:read(1)
meter_num1 = f:read(1)
current_data = string.format("%02X",string.byte(meter_num1))..string.format("%02X",string.byte(meter_num2))
print("meter_numerator = (2 Bytes)".. current_data)
---------------------

--unknown = f:read(2)
--current_data = string.format("%04X",string.byte(unknown))
--print("unknown =       ", current_data)
------------------

tempo4 = f:read(1)
tempo3 = f:read(1)
tempo2 = f:read(1)
tempo1 = f:read(1)
current_data = string.format("%02X",string.byte(tempo1))..string.format("%02X",string.byte(tempo2))..string.format("%02X",string.byte(tempo3))..string.format("%02X",string.byte(tempo4))
print("tempo (4 Bytes)=         ".. current_data)
print("tempo decimal =          ".. hexToFloat(current_data))


end
assert(f:close())

::finish::
__________________
ReaTrakStudio Chord Track for Reaper forum
www.reatrak.com
STASH Downloads https://stash.reaper.fm/u/ReaTrak

Last edited by MusoBob; 11-30-2020 at 11:43 PM.
MusoBob is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 12:58 AM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.