Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
ADPCM compression
#5
So, I finally managed to fix the encoder for ADPCM WAV 16-bit stereo.
The program loads an MP3 file, converts it and compresses it into ADPCM WAV format (IMA ADPCM, stereo), and then plays it using _SndPlay, since this format is also supported in QB64PE.

The resulting compressed WAV file is usually about 3× larger than the original MP3, but around 4.5× smaller than an uncompressed 16-bit stereo WAV at the same sample rate.

This audio format (as well as the mono version from the previous post) is supported in AVI containers as audio tracks.

Some slight to moderate distortion may be noticeable, since this is a lossy compression format.

Don't forget to change the name of the MP3 file on line 22. The audio must be in MP3 format, the program expects this.

Code: (Select All)

' --------------------------------------------
' QB64PE IMA ADPCM Stereo (4-bit, 2 channels)        done after 9 months. Some bugs!!!
' --------------------------------------------
_Title "IMA ADPCM Stereo"

ReDim Shared IMA_StepTable(88) As Integer
ReDim Shared IMA_IndexTable(15) As Integer

IMA_InitTables

' ----------------
' Parametry
' ----------------
Dim sampleRate As Long: sampleRate = 44100
Dim duration As Single: duration = 30

' PCM buffery (16bit signed)
Dim WavLeft(sampleRate * duration) As Integer
Dim WavRight(sampleRate * duration) As Integer

Print "Loading stereo audio to RAM..." '        --------------------------------------------------------------------------
GKS_Stereo WavLeft(), WavRight(), "a.mp3", -1 ' <- here must be mp3 file because is internaly loaded do MEM as 4byte record
'                                                --------------------------------------------------------------------------
Dim totalFrames As Long
totalFrames = UBound(WavLeft) + 1

' ----------------
'    ADPCM block
' ----------------
Dim blockAlign As Integer
blockAlign = 256 'block size in bytes

Dim framesPerBlock As Long
framesPerBlock = blockAlign - 7 ' MS IMA ADPCM stereo specification

Dim compressedData As _Unsigned _Byte
ReDim compressedData(0) As _Unsigned _Byte

ConvertPCMToADPCMStereo WavLeft(), WavRight(), blockAlign, framesPerBlock, compressedData(), totalFrames

' ------------------------------------------------
' Save stereo ADPCM compressed WAV file (finally!)
' ------------------------------------------------
Dim OutFile As String
OutFile = "ADPCM_Stereo.wav"
If _FileExists(OutFile) Then Kill OutFile
SaveIMAADPCMWavStereo OutFile, sampleRate, totalFrames, blockAlign, framesPerBlock, compressedData()
ff = FreeFile
Dim FileSize As Long
Open OutFile For Binary As ff
FileSize = LOF(ff)
Close ff
snd = _SndOpen(OutFile)
SndLen = _SndLen(snd)



Print "Saved stereo ADPCM WAV: "; OutFile
Print "File sizes:"
Print "Created ADPCM (compressed) stereo wav, 16bit,"; _SndRate; ":"; FileSize
Dim CalcSize As Long
CalcSize = 44 + 2 * _SndRate * SndLen * 2 '44 = header, 2 byte / sample, soundrate typical 44100 and channels: 2
Print "Calculated uncompressed wav (16 bit, stereo) size: "; CalcSize
Print "Track lenght:"; SndLen; " (seconds)"
_SndPlay snd


End


'===========================================================
'    Load PCM audio data from file (float -> 16bit PCM)
'===========================================================
Sub GKS_Stereo (wavL() As Integer, wavR() As Integer, SourceFile As String, duration As Single)
    Dim m As _MEM, s As Long, i As Long, k As Long
    s = _SndOpen(SourceFile)
    _Delay .7
    If s < 1 Then Print "Sound source file not found.": End

    If duration < 1 Then duration = _SndLen(s)

    ReDim wavL(duration * _SndRate) As Integer
    ReDim wavR(duration * _SndRate) As Integer

    m = _MemSound(s, 0)

    Do Until k& >= _SndRate * duration * 8 - 8
        wavL(i) = 32767 * _MemGet(m, m.OFFSET + k&, Single)
        wavR(i) = 32767 * _MemGet(m, m.OFFSET + k& + 4, Single)
        i = i + 1
        k& = k& + 8
    Loop

    'truncation to actual number
    If i > 0 Then
        ReDim _Preserve wavL(i - 1) As Integer
        ReDim _Preserve wavR(i - 1) As Integer
    End If

    _MemFree m
    _SndClose s
End Sub


'===========================================================
' convert stereo PCM -> IMA ADPCM (Microsoft stereo layout)
'===========================================================
Sub ConvertPCMToADPCMStereo(wavL() As Integer, wavR() As Integer, _
    blockAlign As Integer, framesPerBlock As Long, _
    outData() As _Unsigned _Byte, totalFrames As Long)

    Dim samplesPerBlock As Long
    samplesPerBlock = framesPerBlock ' = blockAlign - 7

    Dim blockCount As Long
    blockCount = (totalFrames + samplesPerBlock - 1) \ samplesPerBlock

    Dim outSize As Long
    outSize = blockCount * blockAlign
    ReDim outData(outSize - 1) As _Unsigned _Byte

    Dim writePos As Long
    writePos = 0

    Dim b As Long
    For b = 0 To blockCount - 1
        Dim startS As Long
        startS = b * samplesPerBlock

        Dim count As Long
        count = samplesPerBlock
        If startS + count > totalFrames Then
            count = totalFrames - startS
            If count <= 0 Then Exit For
        End If

        Dim block As _Unsigned _Byte
        ReDim block(blockAlign - 1) As _Unsigned _Byte ' defaultně nulová vyplň

        '----- channels headers --------
        Dim predL As Long, predR As Long
        Dim idxL As Long, idxR As Long

        predL = wavL(startS)
        predR = wavR(startS)
        idxL = 0
        idxR = 0

        ' Left header
        block(0) = predL And &HFF
        block(1) = (predL \ 256) And &HFF
        block(2) = idxL And &HFF
        block(3) = 0

        ' Right header
        block(4) = predR And &HFF
        block(5) = (predR \ 256) And &HFF
        block(6) = idxR And &HFF
        block(7) = 0

        ' ---- DATA: interleaving after 4 bytes (8 samples) L/R ----
        Dim poss As Long
        poss = 8

        Dim posL As Long, posR As Long
        Dim nRemL As Long, nRemR As Long
        Dim iL As Long, iR As Long

        iL = 1: iR = 1
        nRemL = count - 1
        nRemR = count - 1

        Dim j As Long
        Dim s1L As Integer, s2L As Integer
        Dim s1R As Integer, s2R As Integer
        Dim nib1 As _Unsigned _Byte, nib2 As _Unsigned _Byte

        Do While nRemL > 0 Or nRemR > 0
            ' 4 byte LEFT
            For j = 0 To 3
                If nRemL <= 0 Then Exit For

                s1L = wavL(startS + iL)
                iL = iL + 1
                nRemL = nRemL - 1

                If nRemL > 0 Then
                    s2L = wavL(startS + iL)
                    iL = iL + 1
                    nRemL = nRemL - 1
                Else
                    s2L = s1L
                End If

                EncodeNibble s1L, predL, idxL, nib1
                EncodeNibble s2L, predL, idxL, nib2

                block(poss) = (nib2 * 16) Or (nib1 And &HF)
                poss = poss + 1
            Next j

            ' 4 byte RIGHT
            For j = 0 To 3
                If nRemR <= 0 Then Exit For

                s1R = wavR(startS + iR)
                iR = iR + 1
                nRemR = nRemR - 1

                If nRemR > 0 Then
                    s2R = wavR(startS + iR)
                    iR = iR + 1
                    nRemR = nRemR - 1
                Else
                    s2R = s1R
                End If

                EncodeNibble s1R, predR, idxR, nib1
                EncodeNibble s2R, predR, idxR, nib2

                block(poss) = (nib2 * 16) Or (nib1 And &HF)
                poss = poss + 1
            Next j
        Loop

        ' block to output
        Dim k As Long
        For k = 0 To blockAlign - 1
            outData(writePos + k) = block(k)
        Next k

        writePos = writePos + blockAlign
    Next b

    ReDim _Preserve outData(writePos - 1) As _Unsigned _Byte
End Sub


'===========================================================
'    IMA ADPCM nibble encoder (standard Intel/Microsoft)
'===========================================================
Sub EncodeNibble (inS As Integer, pred As Long, idx As Long, nib As _Unsigned _Byte)
    Dim stepVal As Long
    Dim diff As Long
    Dim vpdiff As Long

    stepVal = IMA_StepTable(idx)

    diff = inS - pred
    nib = 0

    If diff < 0 Then
        nib = nib Or 8
        diff = -diff
    End If

    If diff >= stepVal Then
        nib = nib Or 4
        diff = diff - stepVal
    End If
    If diff >= stepVal \ 2 Then
        nib = nib Or 2
        diff = diff - (stepVal \ 2)
    End If
    If diff >= stepVal \ 4 Then
        nib = nib Or 1
    End If

    vpdiff = stepVal \ 8
    If (nib And 4) <> 0 Then vpdiff = vpdiff + stepVal
    If (nib And 2) <> 0 Then vpdiff = vpdiff + (stepVal \ 2)
    If (nib And 1) <> 0 Then vpdiff = vpdiff + (stepVal \ 4)

    If (nib And 8) <> 0 Then
        pred = pred - vpdiff
    Else
        pred = pred + vpdiff
    End If

    If pred > 32767 Then pred = 32767
    If pred < -32768 Then pred = -32768

    idx = idx + IMA_IndexTable(nib And 7)
    If idx < 0 Then idx = 0
    If idx > 88 Then idx = 88
End Sub


'===========================================================
'        Save as Microsoft IMA ADPCM WAV (stereo)
'===========================================================
Sub SaveIMAADPCMWavStereo (fname As String, sRate As Long, totalFrames As Long, blockAlign As Integer, framesPerBlock As Long, cData() As _Unsigned _Byte)
    Dim ff As Integer
    ff = FreeFile
    Open fname For Binary As #ff

    Dim riffID As String * 4: riffID = "RIFF": Put #ff, , riffID

    Dim fmtSize As Long: fmtSize = 20
    Dim factSize As Long: factSize = 4
    Dim dataSize As Long: dataSize = UBound(cData) + 1

    Dim fileSize As Long
    fileSize = 4 + (8 + fmtSize) + (8 + factSize) + (8 + dataSize)
    Put #ff, , fileSize

    Dim waveID As String * 4: waveID = "WAVE": Put #ff, , waveID

    ' fmt chunk
    Dim fmtID As String * 4: fmtID = "fmt ": Put #ff, , fmtID
    Put #ff, , fmtSize

    Dim wFormatTag As Integer: wFormatTag = &H11: Put #ff, , wFormatTag
    Dim nChannels As Integer: nChannels = 2: Put #ff, , nChannels
    Dim nSamplesPerSec As Long: nSamplesPerSec = sRate: Put #ff, , nSamplesPerSec

    Dim nAvgBytesPerSec As Long
    nAvgBytesPerSec = (sRate * blockAlign) \ framesPerBlock
    Put #ff, , nAvgBytesPerSec

    Dim wBlockAlign As Integer: wBlockAlign = blockAlign: Put #ff, , wBlockAlign
    Dim wBitsPerSample As Integer: wBitsPerSample = 4: Put #ff, , wBitsPerSample

    Dim cbSize As Integer: cbSize = 2: Put #ff, , cbSize
    Dim wSamplesPerBlock As Integer: wSamplesPerBlock = framesPerBlock
    Put #ff, , wSamplesPerBlock

    ' fact chunk
    Dim factID As String * 4: factID = "fact": Put #ff, , factID
    Put #ff, , factSize
    Put #ff, , totalFrames

    ' data chunk
    Dim dataID As String * 4: dataID = "data": Put #ff, , dataID
    Put #ff, , dataSize
    Put #ff, , cData()

    Close #ff
End Sub


'===========================================================
'            IMA tables inits (standard table)
'===========================================================
Sub IMA_InitTables
    Dim i As Integer, R As Long

    ' standard IMA ADPCM step table (0..88)
    Data 7,8,9,10,11,12,13,14,16,17,19,21,23,25,28,31
    Data 34,37,41,45,50,55,60,66,73,80,88,97,107,118,130,143
    Data 157,173,190,209,230,253,279,307,337,371,408,449,494,544
    Data 598,658,724,796,876,963,1060,1166,1282,1411,1552,1707,1878,2066
    Data 2272,2499,2749,3024,3327,3660,4026,4428,4871,5358,5894,6484,7132
    Data 7845,8630,9493,10442,11487,12635,13899,15289,16818,18500,20350
    Data 22385,24623,27086,29794,32767

    For i = 0 To 88
        Read R
        IMA_StepTable(i) = R
    Next i

    IMA_IndexTable(0) = -1
    IMA_IndexTable(1) = -1
    IMA_IndexTable(2) = -1
    IMA_IndexTable(3) = -1
    IMA_IndexTable(4) = 2
    IMA_IndexTable(5) = 4
    IMA_IndexTable(6) = 6
    IMA_IndexTable(7) = 8
    IMA_IndexTable(8) = -1
    IMA_IndexTable(9) = -1
    IMA_IndexTable(10) = -1
    IMA_IndexTable(11) = -1
    IMA_IndexTable(12) = 2
    IMA_IndexTable(13) = 4
    IMA_IndexTable(14) = 6
    IMA_IndexTable(15) = 8
End Sub


Reply


Messages In This Thread
ADPCM compression - by Petr - 01-19-2025, 03:13 PM
RE: ADPCM compression - by Petr - 01-20-2025, 09:37 PM
RE: ADPCM compression - by Petr - 04-18-2025, 10:10 AM
RE: ADPCM compression - by madscijr - 04-18-2025, 12:49 PM
RE: ADPCM compression - by Petr - 11-19-2025, 06:25 PM
RE: ADPCM compression - by madscijr - 11-19-2025, 08:25 PM
RE: ADPCM compression - by Petr - 11-19-2025, 08:55 PM
RE: ADPCM compression - by ahenry3068 - 11-19-2025, 08:59 PM
RE: ADPCM compression - by Petr - 11-19-2025, 09:59 PM

Forum Jump:


Users browsing this thread: 1 Guest(s)