Assembly code snippets

Back to the snippets overview

Details

TitleMP3 information [bitrate/time/...]
AuthorThomas
Submitted by:Thomas
Date added:2002-04-29 18:23:52
Date modified:2002-08-31 19:18:53

Comments

Parameters:
lpFilename: pointer to the filename of the MP3 to get info from
lpInfoStruct: pointer to an MP3INFO structure that will receive the info on return

This function scans the whole MP3 and calculates the bitrate, time and some other information.
MP3INFO STRUCT
fileSize: size of the MP3 file
fileLength: length of the file, in msecs
bitrate: bitrate in bits per second. This value can be negative, in that case the mp3 has a variable bitrate. The value is the average bitrate, negated.
frequency: frequency of the MP3, in Herz (44100, 22050 etc)
nFrames: number of MPEG frames. Note: do not rely on this value, it isn't always correct (yet).
MP3INFO ENDS

The procedure is optimized for size.

Returns:
false on failure, true otherwise

Snippet

.data
    ;V1 layer 3 bitrates
    bitrateTableV1  dw -1,32,40,48,56,64,80,96,112,128,160,192,224,256,320,-1
    ;V2/2.5 layer 3 bitrates
    bitrateTableV2  dw -1,8,16,24,32,40,48,56,64,80,96,112,128,144,160,-1

    samplerateTableV1   dw 44100,48000,32000
    samplerateTableV2   dw 22050,24000,16000
    samplerateTableV2_5 dw 11025,12000,8000

.code

MP3INFO STRUCT
    fileSize        dword   ?   ; in bytes
    fileLength      dword   ?   ; in miliseconds
    bitrate         sdword  ?   ; in bps. if negative:
                                ; variable bitrate,bitrate=negated average
    frequency       dword   ?   ; in Hertz
    nFrames         dword   ?   ; number of MPEG frames.
                                ; Note: do not rely on this value,
                                ; it isn't always correct (yet).
MP3INFO ENDS

GetMP3Info  proc uses esi edi ebx lpFilename:DWORD, lpInfoStruct:DWORD
LOCAL   hFile:DWORD
LOCAL   hMap:DWORD
LOCAL   pMap:DWORD
LOCAL   totalbitrate:DWORD
LOCAL   srTablePtr:DWORD
    xor     ebx, ebx
    mov     hFile, ebx
    mov     hMap, ebx
    mov     pMap, ebx
    mov     edi, lpInfoStruct
    push    edi
    or      eax, -1
    mov     ecx, (sizeof MP3INFO) / 4
    rep     stosd
    mov     totalbitrate, ebx

    assume  edi:PTR MP3INFO
    pop     edi
    mov     [edi].nFrames, ebx

    invoke  CreateFile, lpFilename, GENERIC_READ, FILE_SHARE_READ,
                ebx, OPEN_EXISTING,
                FILE_FLAG_SEQUENTIAL_SCAN OR FILE_FLAG_NO_BUFFERING, ebx
    cmp     eax, INVALID_HANDLE_VALUE
    je      _invalid

    mov      hFile, eax
    invoke   CreateFileMapping, eax, ebx, PAGE_READONLY, ebx,ebx,ebx
    or      eax, eax
    jz      _invalid

    mov      hMap, eax

    invoke  MapViewOfFile, eax, FILE_MAP_READ,ebx,ebx,ebx
    or      eax, eax
    jz      _invalid

    mov     pMap, eax
    mov     esi, eax

    invoke  GetFileSize, hFile, NULL
    mov     [edi].fileSize, eax
    lea     ebx, [eax+esi]

    _gotonextframe:
    ; find first 8 bits of sync bits
    cmp     byte ptr [esi], 0FFh
    je      @F
    inc     esi
    cmp     esi, ebx
    jb      _gotonextframe
    jmp     _done

    ;possible first 8 sync bits found
    @@:
    inc     esi
    cmp     esi, ebx
    jae     _done
    mov     al, [esi]
    mov     cl, al
    and     cl, 11100000b
    cmp     cl, 11100000b
    jne     _gotonextframe
    inc     esi

    _frame_found:
    mov     cl, al
    and     cl, 00011110b
    mov     edx, offset bitrateTableV1
    mov     srTablePtr, offset samplerateTableV1
    cmp     cl, 00011010b   ;MPEG v1, layer 3?
    je      _frame_continue

    mov     edx, offset bitrateTableV2
    mov     srTablePtr,  offset samplerateTableV2
    cmp     cl, 00010010b ;MPEG v2, layer 3?
    je      _frame_continue

    mov     srTablePtr, offset samplerateTableV2_5

    cmp     cl, 00000010b ;MPEG v2.5, layer 3
    jne     _gotonextframe  ;find next frame
    PrintText "falling"
    ;fall through...
    _frame_continue:
    PrintText "---frame continue---"
    mov     eax, ebx
    sub     eax, esi

    cmp     eax, 2 ;at least 2 bytes readable?
    jb      _done
    movzx   eax, byte ptr [esi]
    mov     ecx, eax
    shr     al, 4

    movsx   eax, word ptr [edx][2*eax]

    cmp     eax, -1
    je      _gotonextframe
    sub     esi, 2 ;point back to start of frame, as framesize is added later
    ; eax is bitrate
    add     totalbitrate, eax
    mov     edx, [edi].bitrate
    cmp     edx, -1
    jne     _bitrate_already_set
    mov     [edi].bitrate, eax
    jmp     _fixed_bitrate_so_far
    _bitrate_already_set:
    cmp     eax, edx        ;current bitrate same as set bitrate?
    je      _fixed_bitrate_so_far
    ;variable bitrate
    and     [edi].bitrate, 0    ;set to zero indicating variable bitrate
    ;fall through...
    _fixed_bitrate_so_far:

    xor     edx, edx ; init framesize to zero
    shr     ecx, 2
    setc    dl       ;last bit shifted out is padding bit,
                     ; if set increase framesize by one

    and     ecx, 3

    cmp     ecx, 3
    jne     @F
    inc     esi
    cmp     esi, ebx
    jae     _done
    jmp _gotonextframe
    @@:

    shl     ecx, 1
    add     ecx, srTablePtr
    movzx   ecx, word ptr [ecx]
    mov     [edi].frequency, ecx
    ;FrameSize = 144 * BitRate / SampleRate + Padding
    ; eax = bitrate(in kbps, needs to be converted to bps yet)
    ; ecx = sampleRate
    ; edx = padding
    ; framesize = 144 * eax / ecx + edx
    push    edx
    mov     edx, 144*1000
    cmp     srTablePtr, offset samplerateTableV1
    je      @F
    mov     edx, 72*1000
    @@:
    mul     edx
    div     ecx
    pop     edx
    add     edx, eax

    ; edx is framesize now

    add     esi, edx
    cmp     esi, ebx
    jae     _done   ; frame bigger than file, file is cut

    inc     [edi].nFrames
    jmp     _gotonextframe


    _invalid:
    xor     eax, eax
    jmp     _exit

    _done:
    mov     ebx, 1000

    mov     ecx, [edi].nFrames  ;get number of frames
    mov     eax, [edi].bitrate  ;get bitrate in kbps
    mul     ebx                 ;*1000, in bpsd
    mov     [edi].bitrate, eax  ;set new bitrate, in bps
    or      eax, eax            ;bitrate zero?
    jnz     _fixed_bitrate      ;if not, fixed
    mov     eax, totalbitrate   ;else, variable. get total (sum of all bitrates)
    mul     ebx                 ;multiple by 1000
    or      ecx, ecx            ;no frames?
    jz      _invalid            ;bye
    div     ecx                 ;divide by number of frames
    neg     eax                 ;negate, indicating a variable bitrate
    mov     [edi].bitrate, eax
    _fixed_bitrate:

    ; 1152 = # of samples in one frame for layer 3 mpegs

    mov     ebx, 1152*1000
    mov     eax, [edi].nFrames
    xor     edx, edx
    mul     ebx
    mov     ebx, [edi].frequency
    or      ebx, ebx
    jz      _invalid
    div     ebx
    mov     [edi].fileLength, eax   ;set filelengths, in msecs

    ;calculate time
    ;bitrate
    assume  edi:nothing
    xor     eax, eax
    inc     eax

    _exit:
    push    eax

    mov     eax, pMap
    or      eax, eax
    jz      @F
    invoke  UnmapViewOfFile, eax
    @@:

    mov     eax, hMap
    or      eax, eax
    jz      @F
    invoke  CloseHandle, eax
    @@:

    mov     eax, hFile
    or      eax, eax
    jz      @F
    invoke  CloseHandle, eax
    @@:

    pop     eax
ret
GetMP3Info endp