Added x86 asm and C wav writer and player examples.

Specifically:
* Added win32, elf32 and elf64 asm player and wav writers using winmm.
* Added dsound player in C.
* Separated the ALL target and the examples; introduced a new examples target.
This commit is contained in:
Alexander Kraus
2023-08-28 23:54:04 +02:00
committed by Veikko Sariola
parent a439a4fa48
commit 607e5b5da0
17 changed files with 886 additions and 34 deletions

View File

@ -0,0 +1,10 @@
if(WIN32)
set(CMAKE_ASM_NASM_OBJECT_FORMAT win32)
elseif(UNIX)
set(CMAKE_ASM_NASM_OBJECT_FORMAT elf32)
endif()
set(CMAKE_ASM_NASM_COMPILE_OBJECT "<CMAKE_ASM_NASM_COMPILER> <INCLUDES> <DEFINES> <FLAGS> -f ${CMAKE_ASM_NASM_OBJECT_FORMAT} -o <OBJECT> <SOURCE>")
add_asm_example(asmplay "${PROJECT_SOURCE_DIR}/examples/patches/physics_girl_st.yml" 386 32 "winmm" "asound;pthread")
add_asm_example(asmwav "${PROJECT_SOURCE_DIR}/examples/patches/physics_girl_st.yml" 386 32 "" "")
target_compile_definitions(asmwav-386 PRIVATE FILENAME="physics_girl_st.wav")

View File

@ -0,0 +1,81 @@
%include TRACK_INCLUDE
%define SND_PCM_FORMAT_S16_LE 0x2
%define SND_PCM_FORMAT_FLOAT 0xE
%define SND_PCM_ACCESS_RW_INTERLEAVED 0x3
%define SND_PCM_STREAM_PLAYBACK 0x0
section .bss
sound_buffer:
resb SU_LENGTH_IN_SAMPLES * SU_SAMPLE_SIZE * SU_CHANNEL_COUNT
render_thread:
resd 1
pcm_handle:
resd 1
section .data
default_device:
db "default", 0
section .text
symbols:
extern pthread_create
extern sleep
extern snd_pcm_open
extern snd_pcm_set_params
extern snd_pcm_writei
global main
main:
; elf32 uses the cdecl calling convention. This is more readable imo ;)
; Prologue
push ebp
mov ebp, esp
sub esp, 0x10
; Unix does not have gm.dls, no need to ifdef and setup here.
; We render in the background while playing already.
push sound_buffer
lea eax, su_render_song
push eax
push 0
push render_thread
call pthread_create
; We can't start playing too early or the missing samples will be audible.
push 0x2
call sleep
; Play the track.
push 0x0
push SND_PCM_STREAM_PLAYBACK
push default_device
push pcm_handle
call snd_pcm_open
push SU_LENGTH_IN_SAMPLES
push 0
push SU_SAMPLE_RATE
push SU_CHANNEL_COUNT
push SND_PCM_ACCESS_RW_INTERLEAVED
%ifdef SU_SAMPLE_FLOAT
push SND_PCM_FORMAT_FLOAT
%else ; SU_SAMPLE_FLOAT
push SND_PCM_FORMAT_S16_LE
%endif ; SU_SAMPLE_FLOAT
push dword [pcm_handle]
call snd_pcm_set_params
push SU_LENGTH_IN_SAMPLES
push sound_buffer
push dword [pcm_handle]
call snd_pcm_writei
exit:
; At least we can skip the epilogue :)
leave
ret

View File

@ -0,0 +1,120 @@
%define MANGLED
%include TRACK_INCLUDE
%define WAVE_FORMAT_PCM 0x1
%define WAVE_FORMAT_IEEE_FLOAT 0x3
%define WHDR_PREPARED 0x2
%define WAVE_MAPPER 0xFFFFFFFF
%define TIME_SAMPLES 0x2
%define PM_REMOVE 0x1
section .bss
sound_buffer:
resb SU_LENGTH_IN_SAMPLES * SU_SAMPLE_SIZE * SU_CHANNEL_COUNT
wave_out_handle:
resd 1
msg:
resd 1
message:
resd 7
section .data
wave_format:
%ifdef SU_SAMPLE_FLOAT
dw WAVE_FORMAT_IEEE_FLOAT
%else ; SU_SAMPLE_FLOAT
dw WAVE_FORMAT_PCM
%endif ; SU_SAMPLE_FLOAT
dw SU_CHANNEL_COUNT
dd SU_SAMPLE_RATE
dd SU_SAMPLE_SIZE * SU_SAMPLE_RATE * SU_CHANNEL_COUNT
dw SU_SAMPLE_SIZE * SU_CHANNEL_COUNT
dw SU_SAMPLE_SIZE * 8
dw 0
wave_header:
dd sound_buffer
dd SU_LENGTH_IN_SAMPLES * SU_SAMPLE_SIZE * SU_CHANNEL_COUNT
times 2 dd 0
dd WHDR_PREPARED
times 4 dd 0
wave_header_end:
mmtime:
dd TIME_SAMPLES
sample:
times 2 dd 0
mmtime_end:
section .text
symbols:
extern _CreateThread@24
extern _waveOutOpen@24
extern _waveOutWrite@12
extern _waveOutGetPosition@12
extern _PeekMessageA@20
extern _TranslateMessage@4
extern _DispatchMessageA@4
global _mainCRTStartup
_mainCRTStartup:
; win32 uses the cdecl calling convention. This is more readable imo ;)
; We can also skip the prologue; Windows doesn't mind.
%ifdef SU_LOAD_GMDLS
call _su_load_gmdls
%endif ; SU_LOAD_GMDLS
times 2 push 0
push sound_buffer
lea eax, _su_render_song@4
push eax
times 2 push 0
call _CreateThread@24
; We render in the background while playing already. Fortunately,
; Windows is slow with the calls below, so we're not worried that
; we don't have enough samples ready before the track starts.
times 3 push 0
push wave_format
push WAVE_MAPPER
push wave_out_handle
call _waveOutOpen@24
push wave_header_end - wave_header
push wave_header
push dword [wave_out_handle]
call _waveOutWrite@12
; We need to handle windows messages properly while playing, as waveOutWrite is async.
mainloop:
dispatchloop:
push PM_REMOVE
times 3 push 0
push msg
call _PeekMessageA@20
jz dispatchloop_end
push msg
call _TranslateMessage@4
push msg
call _DispatchMessageA@4
jmp dispatchloop
dispatchloop_end:
push mmtime_end - mmtime
push mmtime
push dword [wave_out_handle]
call _waveOutGetPosition@12
cmp dword [sample], SU_LENGTH_IN_SAMPLES
jne mainloop
exit:
; At least we can skip the epilogue :)
leave
ret

View File

@ -0,0 +1,91 @@
%include TRACK_INCLUDE
%define WAVE_FORMAT_PCM 0x1
%define WAVE_FORMAT_IEEE_FLOAT 0x3
section .bss
sound_buffer:
resb SU_LENGTH_IN_SAMPLES * SU_SAMPLE_SIZE * SU_CHANNEL_COUNT
file:
resd 1
section .data
; Change the filename over -DFILENAME="yourfilename.wav"
filename:
db FILENAME, 0
format:
db "wb", 0
; This is the wave file header.
wave_file:
db "RIFF"
dd wave_file_end + SU_LENGTH_IN_SAMPLES * SU_SAMPLE_SIZE * SU_CHANNEL_COUNT - wave_file
db "WAVE"
db "fmt "
wave_format_end:
dd wave_format_end - wave_file
%ifdef SU_SAMPLE_FLOAT
dw WAVE_FORMAT_IEEE_FLOAT
%else ; SU_SAMPLE_FLOAT
dw WAVE_FORMAT_PCM
%endif ; SU_SAMPLE_FLOAT
dw SU_CHANNEL_COUNT
dd SU_SAMPLE_RATE
dd SU_SAMPLE_SIZE * SU_SAMPLE_RATE * SU_CHANNEL_COUNT
dw SU_SAMPLE_SIZE * SU_CHANNEL_COUNT
dw SU_SAMPLE_SIZE * 8
wave_header_end:
db "data"
dd wave_file_end + SU_LENGTH_IN_SAMPLES * SU_SAMPLE_SIZE * SU_CHANNEL_COUNT - wave_header_end
wave_file_end:
section .text
symbols:
extern fopen
extern fwrite
extern fclose
global main
main:
; elf32 uses the cdecl calling convention. This is more readable imo ;)
; Prologue
push ebp
mov ebp, esp
sub esp, 0x10
; Unix does not have gm.dls, no need to ifdef and setup here.
; We render the complete track here.
push sound_buffer
call su_render_song
; Now we open the file and save the track.
push format
push filename
call fopen
mov dword [file], eax
; Write header
push dword [file]
push 0x1
push wave_file_end - wave_file
push wave_file
call fwrite
; write data
push dword [file]
push 0x1
push SU_LENGTH_IN_SAMPLES * SU_SAMPLE_SIZE * SU_CHANNEL_COUNT
push sound_buffer
call fwrite
push dword [file]
call fclose
exit:
; At least we can skip the epilogue :)
leave
ret

View File

@ -0,0 +1,102 @@
%define MANGLED
%include TRACK_INCLUDE
%define WAVE_FORMAT_PCM 0x1
%define WAVE_FORMAT_IEEE_FLOAT 0x3
%define FILE_ATTRIBUTE_NORMAL 0x00000080
%define CREATE_ALWAYS 2
%define GENERIC_WRITE 0x40000000
section .bss
sound_buffer:
resb SU_LENGTH_IN_SAMPLES * SU_SAMPLE_SIZE * SU_CHANNEL_COUNT
file:
resd 1
bytes_written:
resd 1
section .data
; Change the filename over -DFILENAME="yourfilename.wav"
filename:
db FILENAME, 0
; This is the wave file header.
wave_file:
db "RIFF"
dd wave_file_end + SU_LENGTH_IN_SAMPLES * SU_SAMPLE_SIZE * SU_CHANNEL_COUNT - wave_file
db "WAVE"
db "fmt "
wave_format_end:
dd wave_format_end - wave_file
%ifdef SU_SAMPLE_FLOAT
dw WAVE_FORMAT_IEEE_FLOAT
%else ; SU_SAMPLE_FLOAT
dw WAVE_FORMAT_PCM
%endif ; SU_SAMPLE_FLOAT
dw SU_CHANNEL_COUNT
dd SU_SAMPLE_RATE
dd SU_SAMPLE_SIZE * SU_SAMPLE_RATE * SU_CHANNEL_COUNT
dw SU_SAMPLE_SIZE * SU_CHANNEL_COUNT
dw SU_SAMPLE_SIZE * 8
wave_header_end:
db "data"
dd wave_file_end + SU_LENGTH_IN_SAMPLES * SU_SAMPLE_SIZE * SU_CHANNEL_COUNT - wave_header_end
wave_file_end:
section .text
symbols:
extern _CreateFileA@28
extern _WriteFile@20
extern _CloseHandle@4
global _mainCRTStartup
_mainCRTStartup:
; Prologue
push ebp
mov ebp, esp
sub esp, 0x10
%ifdef SU_LOAD_GMDLS
call _su_load_gmdls
%endif ; SU_LOAD_GMDLS
; We render the complete track here.
push sound_buffer
call _su_render_song@4
; Now we open the file and save the track.
push 0x0
push FILE_ATTRIBUTE_NORMAL
push CREATE_ALWAYS
push 0x0
push 0x0
push GENERIC_WRITE
push filename
call _CreateFileA@28
mov dword [file], eax
; This is the WAV header
push 0x0
push bytes_written
push wave_file_end - wave_file
push wave_file
push dword [file]
call _WriteFile@20
; There we write the actual samples
push 0x0
push bytes_written
push SU_LENGTH_IN_SAMPLES * SU_CHANNEL_COUNT * SU_SAMPLE_SIZE
push sound_buffer
push dword [file]
call _WriteFile@20
push dword [file]
call _CloseHandle@4
exit:
; At least we can skip the epilogue :)
leave
ret