[development thread] Wwise (4-bit) IMA ADPCM encoder

Extraction and unpacking of game archives and compression, encryption, obfuscation, decoding of unknown files
AnonBaiter
Posts: 1125
Joined: Tue Feb 02, 2016 2:35 am

[development thread] Wwise (4-bit) IMA ADPCM encoder

Post by AnonBaiter »

hey. i need serious help with this script.

Code: Select all

# Wwise (4-bit) IMA ADPCM encoder

# ima_adpcm_index_table
putarray 0 0 -1
putarray 0 1 -1
putarray 0 2 -1
putarray 0 3 -1
putarray 0 4 2
putarray 0 5 4
putarray 0 6 6
putarray 0 7 8
putarray 0 8 -1
putarray 0 9 -1
putarray 0 10 -1
putarray 0 11 -1
putarray 0 12 2
putarray 0 13 4
putarray 0 14 6
putarray 0 15 8
# ima_adpcm_step_size_table
putarray 1 0 0x7
putarray 1 1 0x8
putarray 1 2 0x9
putarray 1 3 0xa
putarray 1 4 0xb
putarray 1 5 0xc
putarray 1 6 0xd
putarray 1 7 0xe
putarray 1 8 0x10
putarray 1 9 0x11
putarray 1 10 0x13
putarray 1 11 0x15
putarray 1 12 0x17
putarray 1 13 0x19
putarray 1 14 0x1c
putarray 1 15 0x1f
putarray 1 16 0x22
putarray 1 17 0x25
putarray 1 18 0x29
putarray 1 19 0x2d
putarray 1 20 0x32
putarray 1 21 0x37
putarray 1 22 0x3c
putarray 1 23 0x42
putarray 1 24 0x49
putarray 1 25 0x50
putarray 1 26 0x58
putarray 1 27 0x61
putarray 1 28 0x6b
putarray 1 29 0x76
putarray 1 30 0x82
putarray 1 31 0x8f
putarray 1 32 0x9d
putarray 1 33 0xad
putarray 1 34 0xbe
putarray 1 35 0xd1
putarray 1 36 0xe6
putarray 1 37 0xfd
putarray 1 38 0x117
putarray 1 39 0x133
putarray 1 40 0x151
putarray 1 41 0x173
putarray 1 42 0x198
putarray 1 43 0x1c1
putarray 1 44 0x1ee
putarray 1 45 0x220
putarray 1 46 0x256
putarray 1 47 0x292
putarray 1 48 0x2d4
putarray 1 49 0x31c
putarray 1 50 0x36c
putarray 1 51 0x3c3
putarray 1 52 0x424
putarray 1 53 0x48e
putarray 1 54 0x502
putarray 1 55 0x583
putarray 1 56 0x610
putarray 1 57 0x6ab
putarray 1 58 0x756
putarray 1 59 0x812
putarray 1 60 0x8e0
putarray 1 61 0x9c3
putarray 1 62 0xabd
putarray 1 63 0xbd0
putarray 1 64 0xcff
putarray 1 65 0xe4c
putarray 1 66 0xfba
putarray 1 67 0x114c
putarray 1 68 0x1307
putarray 1 69 0x14ee
putarray 1 70 0x1706
putarray 1 71 0x1954
putarray 1 72 0x1bdc
putarray 1 73 0x1ea5
putarray 1 74 0x21b6
putarray 1 75 0x2515
putarray 1 76 0x28ca
putarray 1 77 0x2cdf
putarray 1 78 0x315b
putarray 1 79 0x364b
putarray 1 80 0x3bb9
putarray 1 81 0x41b2
putarray 1 82 0x4844
putarray 1 83 0x4f7e
putarray 1 84 0x5771
putarray 1 85 0x602f
putarray 1 86 0x69ce
putarray 1 87 0x7462
putarray 1 88 0x7fff

endian little

get pcm_size asize
math sample_rate = 48000
math channels = 2
math bit_depth = 24
xmath reduced_bit_depth "bit_depth / 8"
math is_signed = 1
math processing_samples = 0
math processed_samples = 0x42
math block_size_per_channel = 0x24
xmath full_block_size "block_size_per_channel * channels"

xmath pcm_samples_per_decoded_block "processed_samples * reduced_bit_depth"
xmath size_aid_02 "pcm_size / pcm_samples_per_decoded_block"
xmath pcm_hist_size "size_aid_02 * 4"
xmath size_aid_04 "pcm_size / reduced_bit_depth / 2"
xmath size_aid_05 "size_aid_04 - size_aid_02"
xmath full_ima_size "size_aid_05 + pcm_hist_size"

log MEMORY_FILE 0 0
putdstring "RIFF" 4 MEMORY_FILE # "RIFF"
put 0 long MEMORY_FILE # reserved
putdstring "WAVE" 4 MEMORY_FILE # "WAVE"
putdstring "fmt " 4 MEMORY_FILE # "fmt "
put 0x18 long MEMORY_FILE # fmt size
put 2 short MEMORY_FILE # Wwise IMA ADPCM
put channels short MEMORY_FILE
put sample_rate long MEMORY_FILE
xmath average_bytes_per_second_01 "(sample_rate * channels) * 2"
xmath average_bytes_per_second_02 "average_bytes_per_second_01 / pcm_samples_per_decoded_block"
xmath average_bytes_per_second_03 "average_bytes_per_second_01 / 2 / 2" # 0xbb80
xmath average_bytes_per_second_04 "average_bytes_per_second_03 -average_bytes_per_second_02"
xmath average_bytes_per_second "average_bytes_per_second_04 + (average_bytes_per_second_02 * 4)"
put average_bytes_per_second long MEMORY_FILE
put full_block_size short MEMORY_FILE
put 4 short MEMORY_FILE # bits per sample
put 6 short MEMORY_FILE # extra size
put 0x18 short MEMORY_FILE # i don't know what this means
if channels = 1
   put 1 long MEMORY_FILE
elif channels = 2
   put 3 long MEMORY_FILE
endif
putdstring "JUNK" 4 MEMORY_FILE # "JUNK"
put 4 long MEMORY_FILE
put 0 long MEMORY_FILE
putdstring "data" 4 MEMORY_FILE # "data"
put full_ima_size long MEMORY_FILE
get riff_header_size asize MEMORY_FILE
xmath riff_full_size "full_ima_size + riff_header_size - 8"
goto 4 MEMORY_FILE
put riff_full_size long MEMORY_FILE
append
log "test.wem" 0 riff_header_size MEMORY_FILE
append

xmath num_samples "pcm_size / reduced_bit_depth / channels"
for i = 0 < num_samples
   if processing_samples == 0
      log MEMORY_FILE3 0 0
      log MEMORY_FILE4 0 0
   endif
   for j = 0 < channels
      if i == 0
         math predicted_sample = 0
         math index = 0
         math step_size = 7
      endif
      if j = 0
         math file_number = -3
      elif j = 1
         math file_number = -4
      endif
      if is_signed = 1
         xmath signed_bit_depth "bit_depth - 1"
         getbits sample signed_bit_depth
         getbits is_negative 1
         if is_negative == 1
            if bit_depth == 16
               math sample ^ 0x7fff
            elif bit_depth == 24
               math sample ^ 0x7fffff
            endif
            math sample ~ sample
         endif
         if reduced_bit_depth == 3
            math sample >> 8
         endif
         if processing_samples == 0
            goto 0 file_number
            put sample signed_short file_number
            math write_ima_nibble = 0
            callfunction bake_ima_sample 1
            put index byte file_number
            put 0 byte file_number
            putarray 2 j predicted_sample
            putarray 3 j index
            putarray 4 j step_size
         else
            getarray predicted_sample 2 j
            getarray index 3 j
            getarray step_size 4 j
            math write_ima_nibble = 1
            callfunction bake_ima_sample 1
            putarray 2 j predicted_sample
            putarray 3 j index
            putarray 4 j step_size
         endif
      endif
   next j
   math processing_samples + 1
   if processing_samples == processed_samples
      log MEMORY_FILE2 0 0
      append
      log MEMORY_FILE2 0 block_size_per_channel MEMORY_FILE3
      if channels == 2
         log MEMORY_FILE2 0 block_size_per_channel MEMORY_FILE4
      endif
      append
      append
      log "test.wem" 0 full_block_size MEMORY_FILE2
      append
      math processing_samples = 0
   endif
next i

startfunction bake_ima_sample
   xmath difference "sample - predicted_sample"
   if difference >= 0
      math new_sample = 0
   else
      math new_sample = 8
      math difference n difference
   endif
   math mask = 4
   math temp_step_size = step_size
   for m = 0 < 3
      if difference >= temp_step_size
         math new_sample |= mask
         math difference -= temp_step_size
      endif
      math temp_step_size >>= 1
      math mask >>= 1
   next m
   if write_ima_nibble != 0
      putbits new_sample 4 file_number
   endif
   xmath difference "step_size * ((new_sample & 7) * 2 + 1)"
   xmath difference "(difference + (difference >> 0x1f & 7)) >> 3"
   xmath ns1 "new_sample & 8"
   if ns1 == 8
      math difference n difference
   endif
   math predicted_sample += difference
   if predicted_sample > 32767
      math predicted_sample = 32767
   elif predicted_sample < -32768
      math predicted_sample = -32768
   endif
   getarray index_table 0 new_sample
   math index += index_table
   if index < 0
      math index = 0
   elif index > 88
      math index = 88
   endif
   getarray step_size_table 1 index
   math step_size = step_size_table
endfunction
this script requires a headerless PCM file (NOT FLAC!!!) whose bit depth is either 16 or 24 bits. if in doubt test these files with this script and report back once you're finished.

anyway, there are three values you can configure right at the start of the script for your input file(sample_rate, channels, bit_depth), that is if you have the number of sample rate, channels and bit depth in mind and if you know what you're doing (generally speaking). i am not in any mood whatsoever to explain exactly what you are supposed to do as i spent way too much time trying to write something like this by this point. (please note that i'm not talking about the files i have linked above, these are just meant to help you to help me out)

now as for the reason why i need help with this script in the first place is because the resulting test.wem file is a crapper to play on vgmstream (latest version is here). in fact, it sounds more like a cacophony of shit sounds coming from a shit encoder than something you'd expect out of an Wwise project. source file is [signed_16bit_little_endian_pcm][48000][stereo]sega_saturn_boot_up.aaa.wav in case you're wondering. test.wem is here, manually fixed "RIFF" and "data" sizes and all.

do be warned though that this script is some DBZ scene without the fighting slow to run on quickbms, do what you can to optimize the script to run faster, maybe things are gonna change with a new version of the script, i don't know.

EDIT#1 - fixed a stupid mistake in the script.
EDIT#2 - re-done the whole "difference" part after the "putbits new_sample" part. this time, it's much accurate to the algorithm as used on (recent) Wwise projects.
EDIT#3 - script has now been attached for those who don't want to Ctrl-C+Ctrl-V the entire script into an entirely new text file.
Last edited by AnonBaiter on Mon Mar 23, 2020 9:49 pm, edited 5 times in total.
AnonBaiter
Posts: 1125
Joined: Tue Feb 02, 2016 2:35 am

Re: [development thread] Wwise (4-bit) IMA ADPCM encoder

Post by AnonBaiter »

AAAAAAAAAAAAAAAAAA my oversight this thread was supposed to be posted on "Audio and Video file formats"
aluigi plz fix this
AnonBaiter
Posts: 1125
Joined: Tue Feb 02, 2016 2:35 am

Re: [development thread] Wwise (4-bit) IMA ADPCM encoder

Post by AnonBaiter »

i fixed up the encoding algorithm now (see first post). and i got more accurate results on vgmstream.
with that being said, the full_ima_size and average_bytes_per_second need to be fixed badly but i can't figure out why they're that way...

as for the algorithm itself that's a different story. despite my efforts to ensure the algorithm is faithful to Wwise IMA ADPCM code, the resulting file as played on vgmstream can sound like it has gotten a different pitch compared to the source file. i tried to see what was the cause of it but unfortunately i didn't come up with an efficient solution to compare the encoded file (during playback on vgmstream) against source file.

outside help is welcome.
AnonBaiter
Posts: 1125
Joined: Tue Feb 02, 2016 2:35 am

Re: [development thread] Wwise (4-bit) IMA ADPCM encoder

Post by AnonBaiter »

a new sample file has been added to the google drive link for anyone to try out with wwise_ima_adpcm.bms