Author Topic: SDL 2 Audio NASM GCC  (Read 2979 times)

Offline encryptor256

  • Full Member
  • **
  • Posts: 250
  • Country: lv
  • Win64 .
    • On Youtube: encryptor256
SDL 2 Audio NASM GCC
« on: October 05, 2013, 10:27:25 AM »
Hello!

.LET_HEADER_BEGIN:

; *************************************************
; *************************************************
; *************************************************
; This is: SDL 2 Audio NASM GCC.
; Author: J.K. Encryptor256.
; Date: October 05, year of 2013.
; Purpose of this: Just for fun, why not?
; *************************************************
; Using: The Netwide Assembler and GCC.
; Nasm: Produces .O file.
; GCC: Produces EXE file, from .O file.
; *************************************************
; Web links:
; The Netwide Assembler: (http://www.nasm.us/)
; The GNU Compiler Collection: (http://gcc.gnu.org/)
; Simple DirectMedia Layer: (http://www.libsdl.org/)
; Wav file downloaded from: (http://www.wavsource.com)
; *************************************************
; Compile:
; Nasm: "nasm program.asm -f win32 -o program.o".
; GCC: "gcc program.o -m32 -o program.exe -lSDL2 -LK:/win32/sdl".
; *************************************************
; +Program Requires:
; 1. Wav file: "this_is_the_end_x.wav"
; 2. Library: "sdl2.dll"
; *************************************************
; +Description:
; Program loads wav file and plays it.
; Program terminates after playback or on error.
; *************************************************
; Using following SDL 2 functions:
; 1. SDL_Init.
; 2. SDL_GetError.
; 3. SDL_Quit.
; 4. SDL_RWFromFile.
; 5. SDL_LoadWAV_RW.
; 6. SDL_FreeWAV.
; 7. SDL_OpenAudioDevice.
; 8. SDL_PauseAudioDevice.
; 9. SDL_Delay.
; 10. SDL_CloseAudioDevice.
; 11. SDL_memset - I think this is the same as C memset, so, nothing new.
; 12. SDL_memcpy - I think this is the same as C memcpy, so, nothing new.
; *************************************************
; Using following C functions:
; 1. main - Entry Point.
; 2. printf.
; 3. atexit.
; *************************************************

.COMPILE:

NASM: "nasm.exe SDL_Audio.asm -f win32 -o SDL_Audio.o"

GCC: "gcc SDL_Audio.o -m32 -o SDL_Audio.exe -lSDL2 -LK:/win32/sdl"

Parameter: "-L K:/win32/sdl" works in conjuction with "-l SDL2",
Parameter: -L, tells the compiler where to find library SDL2, so,
SDL2.DLL file is located in "K:/win32/sdl" (Needed at link time).
SDL2.DLL file is located also in the current directory(Needed at Runtime time).

.VIDEO:

Tired of reading?

Watch video on youtube named: "SDL 2 Audio NASM GCC"

Link: http://youtu.be/qzoiZHZLnKU

.CODE:

Added attachment of code file, wav file, dll file.

.END:

Bye!

Have a nice day and let bytes protect you! :)

Encryptor256



Encryptor256's Investigation \ Research Department.

Offline encryptor256

  • Full Member
  • **
  • Posts: 250
  • Country: lv
  • Win64 .
    • On Youtube: encryptor256
Re: SDL 2 Audio NASM GCC
« Reply #1 on: October 05, 2013, 10:30:22 AM »
Hello!

.LET_CODE_BEGIN:

Code: [Select]
; ###############################################
; .LET_MACROS_BEGIN:
;        [!!!] Define some NASM macros [!!!]
; ###############################################

; Macro: cextern
; + example provided
; -------------------------------------------------------------------
%macro cextern 1                       ; cextern printf
        extern _%1                         ;         extern _printf
        %ifndef %1                         ;        %ifndef printf

                %define %1 _%1        ;                %define printf _printf
        %endif                                ;        %endif
%endmacro                                 ; %endmacro

; Macro: cglobal
; + example provided
; -------------------------------------------------------------------
%macro cglobal 1                        ; cglobal main
        global _%1                          ;         global _main
        %ifndef %1                          ;        %ifndef main

                %define %1 _%1        ;                %define main _main
        %endif                                 ;        %endif
%endmacro                                 ; %endmacro

; Macro: cexternList
; -------------------------------------------------------------------
%macro cexternList 1-*
        %rep %0
                cextern %1
                %rotate 1
        %endrep
%endmacro

; Macro: invoke
; This macro will call a cdecl procedure and clean up stack
; Param NR.1 is the function name
; Rest of params could be function, at param NR.1, params
; -------------------------------------------------------------------
%macro invoke 1-*
        %assign max %0-1
        %if %0 > 1
                %rotate max
                %rep max
                        push dword %1
                        %rotate -1
                %endrep
        %endif
        call %1
        add esp,dword max * 4
%endmacro

; Macro: invoke_error
; This macro used in conjunction with macro invoke
; It compares eax at param NR.2
; It takes jump type at param NR.1
; It jumps at location at param NR.3
; -------------------------------------------------------------------
%macro invoke_error 3

        cmp eax,dword %2
        %1 %3
%endmacro

; Macro: invoke_returns_at
; This macro used in conjunction with macro invoke
; It moves value of eax into param NR.1
; -------------------------------------------------------------------
%macro invoke_returns_at 1

        mov dword %1, eax
%endmacro

; ###############################################
; .LET_CODE_BEGIN:
;        Simple DirectMedia Layer 2:  AUDIO
; ###############################################

; # Tell compiler what cpu type to use
cpu 386

; # Tell compiler to generate 32 bit code
bits 32

; # Use our predefined macro, define functions

        ; SDL functions
        cexternList SDL_Init, \
                        SDL_GetError, \
                        SDL_Quit, \
                        SDL_RWFromFile, \
                        SDL_LoadWAV_RW, \
                        SDL_FreeWAV, \
                        SDL_OpenAudioDevice, \
                        SDL_PauseAudioDevice, \
                        SDL_Delay, \
                        SDL_CloseAudioDevice, \
                        SDL_memset, \
                        SDL_memcpy

        ; C functions
        cexternList printf, \
                        atexit, \

        ; C global def's
        cglobal main

; # Define some constants

        ; SDL Constants ----------------------------------------------------------------------
 
        SDL_AUDIO_ALLOW_FREQUENCY_CHANGE equ 0x00000001
        SDL_AUDIO_ALLOW_FORMAT_CHANGE equ 0x00000002
        SDL_AUDIO_ALLOW_CHANNELS_CHANGE equ 0x00000004
        SDL_AUDIO_ALLOW_ANY_CHANGE equ SDL_AUDIO_ALLOW_CHANNELS_CHANGE \
                                                                + SDL_AUDIO_ALLOW_FORMAT_CHANGE \
                                                                + SDL_AUDIO_ALLOW_FREQUENCY_CHANGE

        SDL_INIT_TIMER equ 0x00000001
        SDL_INIT_AUDIO equ 0x00000010
        SDL_INIT_VIDEO equ 0x00000020
        SDL_INIT_JOYSTICK equ 0x00000200
        SDL_INIT_HAPTIC equ 0x00001000
        SDL_INIT_GAMECONTROLLER equ 0x00002000
        SDL_INIT_EVENTS equ 0x00004000
        SDL_INIT_NOPARACHUTE equ 0x00100000
        SDL_INIT_EVERYTHING equ SDL_INIT_TIMER \
                                                + SDL_INIT_AUDIO \
                                                + SDL_INIT_VIDEO \
                                                + SDL_INIT_EVENTS \
                                                + SDL_INIT_JOYSTICK \
                                                + SDL_INIT_HAPTIC \
                                                + SDL_INIT_GAMECONTROLLER

        ; SDL Structures ----------------------------------------------------------------------
       
        ; SDL_AudioSpec:
        ; A structure that contains the audio output format.
        ; It also contains a callback that is called when the audio device needs more data.
        struc SDL_AudioSpec
                .freq: resb 4
                .format: resb 2
                .channels: resb 1
                .silence: resb 1
                .samples: resb 2
                .padding: resb 2
                .size: resb 4
                .callback: resb 4
                .userdata: resb 4
        endstruc

        ; SDL_AUDIO_USER_DATA:
        ; User defined structure, i design it, on my own.
        struc SDL_AUDIO_USER_DATA
                .wav_audio_buf: resb 4
                .wav_audio_len: resb 4
                .played_len: resb 4
                .done: resb 4
        endstruc

;# Data segment
; ---------------------------------------------------------------
segment .data use32
       
        ; Textual data:

                txt_error_format db 13,"SDL Error:  %s",13,0

                ; Wav file name: (http://www.wavsource.com)
                txt_wav_name db "this_is_the_end_x.wav",0

                ; Info messages:
                txt_play_start db 10,"Info: Playback started...",0
                txt_playing db 10,"Info: Playing...",0
                txt_play_end db 10,"Info: Playback en dead...",0

        ; Data used by SDL_LoadWav:

                wav_audio_spec:
                                istruc SDL_AudioSpec
                                iend

                wav_audio_buf: dd 0
                wav_audio_len: dd 0


        ; Data used by SDL_OpenAudioDevice:

                got_audio_spec:
                                istruc SDL_AudioSpec
                                iend

                audio_device_id: dd 0

                audio_user_data:
                                istruc SDL_AUDIO_USER_DATA
                                iend

;# Code segment
; ---------------------------------------------------------------
segment .text use32
       
; ###############################################
;
; main: Entry point, the magical place where code begins.
;
; ###############################################

main:
        push ebp
        mov ebp,esp       

        ; SDL_Init:
        ; Use this function to initialize the SDL library.
        ; This must be called before using any other SDL function.
        ; ---------------------------------------------------------
        invoke SDL_Init, SDL_INIT_AUDIO
        invoke_error jne,0,main.quitError

        ; atexit:
        ; Set function to be executed on exit
        ; ---------------------------------------------------------
        ; SDL_Quit:
        ; Use this function to clean up all initialized subsystems.
        ; You should call it upon all exit conditions.
        ; ---------------------------------------------------------
        invoke atexit, SDL_Quit

        ; SDL_LoadWAV:
        ; Use this function to load a WAVE from a file.
        ; * Warning: be aware, that value returned into wav_audio_buf is a pointer to pointer.
        ; ---------------------------------------------------------
        invoke SDL_LoadWAV,txt_wav_name,wav_audio_spec,wav_audio_buf,wav_audio_len
        invoke_error je,0,main.quitError

        ; Wav file SDL_AudioSpec obtained, update callback and user data:
        ; ---------------------------------------------------------
        mov dword [wav_audio_spec+SDL_AudioSpec.callback],SDL_AudioCallback
        mov dword [wav_audio_spec+SDL_AudioSpec.userdata],audio_user_data

        ; Fill audio user data: SDL_AUDIO_USER_DATA
        ; ---------------------------------------------------------
        mov eax,dword [wav_audio_buf]
        mov dword [audio_user_data+SDL_AUDIO_USER_DATA.wav_audio_buf],eax
        mov eax,dword [wav_audio_len]
        mov dword [audio_user_data+SDL_AUDIO_USER_DATA.wav_audio_len],eax
        mov dword [audio_user_data+SDL_AUDIO_USER_DATA.played_len],0
        mov dword [audio_user_data+SDL_AUDIO_USER_DATA.done],0

        ; SDL_OpenAudioDevice:
        ; Use this function to open a specific audio device.
        ; ---------------------------------------------------------
        invoke SDL_OpenAudioDevice, \
                        0, \
                        0, \
                        wav_audio_spec, \
                        got_audio_spec, \
                        SDL_AUDIO_ALLOW_FORMAT_CHANGE

        invoke_error je,0,main.quitError
        invoke_returns_at [audio_device_id]

        ; SDL_PauseAudioDevice:
        ; (Start playback, pause = 0)
        ; Use this function to pause and unpause audio playback on a specified device.
        ; ---------------------------------------------------------
        invoke SDL_PauseAudioDevice,[audio_device_id],0


        ; Display info: Playback started
        ; ---------------------------------------------------
        pushad
        invoke printf,txt_play_start
        popad

        .mainLoop:

                ; SDL_Delay:
                ; Use this function to wait a specified number of milliseconds before returning.
                ; ---------------------------------------------------------
                invoke SDL_Delay,100

                ; Is playback done: ?
                ; ---------------------------------------------------
                mov eax,dword audio_user_data
                cmp dword [eax + SDL_AUDIO_USER_DATA.done],1

                jne .mainLoop

        ; Display info: Playback ended
        ; ---------------------------------------------------
        pushad
        invoke printf,txt_play_end
        popad

        ; SDL_PauseAudioDevice:
        ; (Stop playback, pause = 1)
        ; Use this function to pause and unpause audio playback on a specified device.
        ; ---------------------------------------------------------
        invoke SDL_PauseAudioDevice,[audio_device_id],1

        ; SDL_CloseAudioDevice:
        ; Use this function to shut down audio processing and close the audio device.
        ; ---------------------------------------------------------
        invoke SDL_CloseAudioDevice,[audio_device_id]
       
        ; SDL_FreeWAV:
        ; Use this function to free data
        ; previously allocated with SDL_LoadWAV() or SDL_LoadWAV_RW().
        ; ---------------------------------------------------------
        invoke SDL_FreeWAV,wav_audio_buf


        ; Quit normal:
        ; ---------------------------------------------------------
        mov eax,dword 0
        pop ebp
        ret

        ; Quit Error:
        ; ---------------------------------------------------------
        main.quitError:

                ; SDL_GetError:
                ; Use this function to retrieve a message about the last error that occurred.
                ; ---------------------------------------------------
                invoke SDL_GetError

                ; Print error information:
                ; ---------------------------------------------------
                invoke printf,txt_error_format,eax

                mov eax,dword 0
                pop ebp
                ret
       
; ###############################################
;
; SDL_LoadWAV: Directly nonexistant function.
;                        Sub calls: SDL_RWFromFile, SDL_LoadWAV_RW.
;
; ###############################################

SDL_LoadWAV:
        push ebp
        mov ebp,esp
        ; ---------------------------------------------------------
        %define ebp_file ebp+8
        %define ebp_spec ebp+12
        %define ebp_audio_buf ebp+16
        %define ebp_audio_len ebp+20
        ; ---------------------------------------------------------
        ; Jump over small data area, there:
        jmp SDL_LoadWAV.theCode
        ; ---------------------------------------------------------
        ; Small data area,here:
        SDL_LoadWAV.fileaccess: db "rb",0
        ; ---------------------------------------------------------
        SDL_LoadWAV.theCode:
        ; ---------------------------------------------------------

        ; # SDL_LoadWAV: ---------------------------------------


                ; SDL_RWFromFile:
                ; Use this function to create a new SDL_RWops structure
                ; for reading from and/or writing to a named file.
                ; ---------------------------------------------------
                invoke SDL_RWFromFile,[ebp_file],SDL_LoadWAV.fileaccess
                invoke_error jne,0,SDL_LoadWAV.SDL_RWFromFile_Success

                ; Quit error:
                ; ---------------------------------------------------
                mov eax,dword 0
                pop ebp
                ret

        ; ---------------------------------------------------------
        SDL_LoadWAV.SDL_RWFromFile_Success:

                ; SDL_LoadWAV_RW:
                ; Use this function to load a WAVE from the data source,
                ; automatically freeing that source if freesrc is non-zero.
                ; ---------------------------------------------------
                invoke SDL_LoadWAV_RW,eax,1, [ebp_spec], [ebp_audio_buf], [ebp_audio_len]
                invoke_error jne,0,SDL_LoadWAV.SDL_LoadWAV_RW_Success

                ; Quit error:
                ; ---------------------------------------------------
                mov eax,dword 0
                pop ebp
                ret

        ; ---------------------------------------------------------
        SDL_LoadWAV.SDL_LoadWAV_RW_Success:

        ; ---------------------------------------------------------
        pop ebp
        ret


; ###############################################
;
; SDL_AudioCallback: Audio callback.
;
; ###############################################

SDL_AudioCallback:
        push ebp
        mov ebp,esp
        ; ---------------------------------------------------------
        %define ebp_userdata ebp+8
        %define ebp_peestream ebp+12
        %define ebp_len ebp+16
        ; ---------------------------------------------------------
        ; user data:
        ; ---------------------------------------------------------
        ; [ebp_userdata] + SDL_AUDIO_USER_DATA.wav_audio_buf
        ; [ebp_userdata] + SDL_AUDIO_USER_DATA.wav_audio_len
        ; [ebp_userdata] + SDL_AUDIO_USER_DATA.played_len
        ; [ebp_userdata] + SDL_AUDIO_USER_DATA.done
        ; ---------------------------------------------------------

        ; On entry, do init all stream with silence:
        ; ---------------------------------------------------------
       
                invoke SDL_memset,[ebp_peestream],0,[ebp_len]

        ; Address of user data, goes into: EAX
        ; ---------------------------------------------------------
       
                mov eax,dword [ebp_userdata]

        ; Already played: ?
        ; ---------------------------------------------------------
       
                cmp dword [eax+ SDL_AUDIO_USER_DATA.done],1
                jne SDL_AudioCallback.continuePlay

        ; Yes: Already played, quit:
        ; ---------------------------------------------------------

                pop ebp
                ret

        ; No: Still playing, continue:
        ; ---------------------------------------------------------
        SDL_AudioCallback.continuePlay:

                ; Display info:
                pushad
                invoke printf,txt_playing
                popad

        ; Setup initial values of EBX, ECX:
        ; ---------------------------------------------------------
                mov ebx,dword [eax+SDL_AUDIO_USER_DATA.wav_audio_buf]
                mov ecx,dword [eax+SDL_AUDIO_USER_DATA.played_len]

        ; Audio buffer data pointer, goes into: EBX
        ; ---------------------------------------------------------

                add ebx,ecx

        ; played_len + len, goes into: ECX
        ; ---------------------------------------------------------

                add ecx,dword [ebp_len]

        ; Determine if (played_len + len) exceeds wav_audio_len
        ; ---------------------------------------------------------

        cmp ecx,dword [eax +SDL_AUDIO_USER_DATA.wav_audio_len]
        jg SDL_AudioCallback.onExceeds
                       
                ; ---------------------------------------------------
                ; played_len + len < wav_audio_len
                ; ---------------------------------------------------
       
                ; Update value of SDL_AUDIO_USER_DATA.played_len
                ; ---------------------------------------------------
                mov edx,eax
                add edx,SDL_AUDIO_USER_DATA.played_len
                mov [edx],ecx

                ; Fill stream with data,
                ; params: EBX(address), [ebp_len] is actual len
                ; ---------------------------------------------------
                invoke SDL_memcpy,[ebp_peestream],ebx,[ebp_len]

                pop ebp
                ret

        SDL_AudioCallback.onExceeds:
                ; ---------------------------------------------------
                ; played_len + len > wav_audio_len
                ; ---------------------------------------------------

                ; Reverse last aritmetic operation: ECX
                sub ecx,dword [ebp_len]

                ; Obtain wav_audio_len into: EDX
                mov edx,dword [eax +SDL_AUDIO_USER_DATA.wav_audio_len]

                ; wav_audio_len minus played_len: EDX
                sub edx,ecx

                ; Fill stream: EBX(address), EDX(new len: wav_audio_len-played_len)
                invoke SDL_memcpy,[ebp_peestream],ebx,edx

                ; Set: SDL_AUDIO_USER_DATA.done
                ; ---------------------------------------------------
                mov eax,dword [ebp_userdata]
                mov dword [eax + SDL_AUDIO_USER_DATA.done],1

                ; Set: SDL_AUDIO_USER_DATA.played_len
                ; ---------------------------------------------------
                mov edx,dword [eax +SDL_AUDIO_USER_DATA.wav_audio_len]
                mov dword [eax+SDL_AUDIO_USER_DATA.played_len],edx

                ; ---------------------------------------------------
                pop ebp
                ret


; ----
; END ---
; ----

There was a post limitation of 20k characters, so, split two was the result.

Bye!
Encryptor256's Investigation \ Research Department.