mirror of
https://github.com/vsariola/sointu.git
synced 2025-06-04 01:28:45 -04:00
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:
parent
a439a4fa48
commit
607e5b5da0
@ -167,9 +167,15 @@ wat2wasm --enable-bulk-memory test_chords.wat
|
|||||||
|
|
||||||
#### Examples
|
#### Examples
|
||||||
|
|
||||||
The folder `examples/code` contains usage examples in C. If you want to target smaller executable sizes, using a compressing linker
|
The folder `examples/code` contains usage examples for Sointu with winmm und dsound playback under Windows and asound playback under Unix. Source code is available in C and x86 assembly (win32, elf32 and elf64 versions).
|
||||||
|
|
||||||
|
To build the examples, use `ninja examples`.
|
||||||
|
|
||||||
|
If you want to target smaller executable sizes, using a compressing linker
|
||||||
like [Crinkler](https://github.com/runestubbe/Crinkler) on Windows is recommended.
|
like [Crinkler](https://github.com/runestubbe/Crinkler) on Windows is recommended.
|
||||||
|
|
||||||
|
The linux examples use ALSA and need libasound2-dev (or libasound2-dev:386) installed. The 386 version also needs pipewire-alsa:386 installed, which is not there by default.
|
||||||
|
|
||||||
### Native virtual machine
|
### Native virtual machine
|
||||||
|
|
||||||
The native bridge allows Go to call the sointu compiled x86 native
|
The native bridge allows Go to call the sointu compiled x86 native
|
||||||
|
@ -1,34 +1,68 @@
|
|||||||
|
# this fixes a bug in creating a static library from asm, similar to
|
||||||
|
# https://discourse.cmake.org/t/building-lib-file-from-asm-cmake-bug/1959
|
||||||
|
# but for NASM
|
||||||
|
if(MSVC)
|
||||||
|
set(CMAKE_ASM_NASM_CREATE_STATIC_LIBRARY "<CMAKE_AR> /OUT:<TARGET> <LINK_FLAGS> <OBJECTS>")
|
||||||
|
endif()
|
||||||
|
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
COMMAND
|
COMMAND
|
||||||
${compilecmd} -arch=${arch} -o physics_girl_st.asm "${PROJECT_SOURCE_DIR}/examples/patches/physics_girl_st.yml"
|
${compilecmd} -arch=${arch} -o physics_girl_st.asm "${PROJECT_SOURCE_DIR}/examples/patches/physics_girl_st.yml"
|
||||||
WORKING_DIRECTORY
|
WORKING_DIRECTORY
|
||||||
${CMAKE_CURRENT_BINARY_DIR}
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
DEPENDS
|
DEPENDS
|
||||||
"${PROJECT_SOURCE_DIR}/examples/patches/physics_girl_st.yml"
|
"${PROJECT_SOURCE_DIR}/examples/patches/physics_girl_st.yml"
|
||||||
OUTPUT
|
OUTPUT
|
||||||
physics_girl_st.asm
|
physics_girl_st.asm
|
||||||
physics_girl_st.h
|
physics_girl_st.h
|
||||||
physics_girl_st.inc
|
physics_girl_st.inc
|
||||||
COMMENT
|
COMMENT
|
||||||
"Compiling ${PROJECT_SOURCE_DIR}/examples/patches/physics-girl-st.yml..."
|
"Compiling ${PROJECT_SOURCE_DIR}/examples/patches/physics-girl-st.yml..."
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(physics_girl_st physics_girl_st.asm)
|
add_library(physics_girl_st physics_girl_st.asm)
|
||||||
add_dependencies(physics_girl_st sointu-compiler)
|
add_dependencies(physics_girl_st sointu-compiler)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
add_executable(cplay
|
add_executable(cplay-winmm
|
||||||
cplay.windows.c
|
cplay.windows.winmm.c
|
||||||
physics_girl_st.h
|
physics_girl_st.h
|
||||||
)
|
)
|
||||||
target_link_libraries(cplay PRIVATE winmm)
|
target_link_libraries(cplay-winmm PRIVATE winmm)
|
||||||
|
target_link_libraries(cplay-winmm PRIVATE physics_girl_st)
|
||||||
|
target_include_directories(cplay-winmm PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
add_dependencies(examples cplay-winmm)
|
||||||
|
|
||||||
|
add_executable(cplay-directsound
|
||||||
|
cplay.windows.directsound.c
|
||||||
|
physics_girl_st.h
|
||||||
|
)
|
||||||
|
target_link_libraries(cplay-directsound PRIVATE dsound ws2_32 ucrt)
|
||||||
|
target_link_libraries(cplay-directsound PRIVATE physics_girl_st)
|
||||||
|
target_include_directories(cplay-directsound PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
add_dependencies(examples cplay-directsound)
|
||||||
elseif(UNIX)
|
elseif(UNIX)
|
||||||
add_executable(cplay
|
add_executable(cplay
|
||||||
cplay.unix.c
|
cplay.unix.c
|
||||||
physics_girl_st.h
|
physics_girl_st.h
|
||||||
)
|
)
|
||||||
target_link_libraries(cplay PRIVATE asound pthread)
|
target_link_libraries(cplay PRIVATE asound pthread)
|
||||||
target_link_options(cplay PRIVATE -z noexecstack -no-pie)
|
target_link_options(cplay PRIVATE -z noexecstack -no-pie)
|
||||||
|
target_link_libraries(cplay PRIVATE physics_girl_st)
|
||||||
|
target_include_directories(cplay PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
add_dependencies(examples cplay)
|
||||||
endif()
|
endif()
|
||||||
target_link_libraries(cplay PRIVATE physics_girl_st)
|
|
||||||
target_include_directories(cplay PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
add_executable(cwav
|
||||||
|
cwav.c
|
||||||
|
physics_girl_st.h
|
||||||
|
)
|
||||||
|
if(WIN32)
|
||||||
|
target_compile_definitions(cwav PRIVATE _CRT_SECURE_NO_WARNINGS)
|
||||||
|
elseif(UNIX)
|
||||||
|
target_link_options(cwav PRIVATE -z noexecstack -no-pie)
|
||||||
|
endif()
|
||||||
|
target_link_libraries(cwav PRIVATE physics_girl_st)
|
||||||
|
target_include_directories(cwav PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
|
add_dependencies(examples cwav)
|
||||||
|
@ -20,7 +20,19 @@ int main(int argc, char **args) {
|
|||||||
|
|
||||||
// Play the track.
|
// Play the track.
|
||||||
snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
|
snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
|
||||||
snd_pcm_set_params(pcm_handle, SND_PCM_FORMAT_FLOAT, SND_PCM_ACCESS_RW_INTERLEAVED, SU_CHANNEL_COUNT, SU_SAMPLE_RATE, 0, SU_LENGTH_IN_SAMPLES);
|
snd_pcm_set_params(
|
||||||
|
pcm_handle,
|
||||||
|
#ifdef SU_SAMPLE_FLOAT
|
||||||
|
SND_PCM_FORMAT_FLOAT,
|
||||||
|
#else // SU_SAMPLE_FLOAT
|
||||||
|
SND_PCM_FORMAT_S16_LE,
|
||||||
|
#endif // SU_SAMPLE_FLOAT
|
||||||
|
SND_PCM_ACCESS_RW_INTERLEAVED,
|
||||||
|
SU_CHANNEL_COUNT,
|
||||||
|
SU_SAMPLE_RATE,
|
||||||
|
0,
|
||||||
|
SU_LENGTH_IN_SAMPLES
|
||||||
|
);
|
||||||
snd_pcm_writei(pcm_handle, sound_buffer, SU_LENGTH_IN_SAMPLES);
|
snd_pcm_writei(pcm_handle, sound_buffer, SU_LENGTH_IN_SAMPLES);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
75
examples/code/C/cplay.windows.directsound.c
Normal file
75
examples/code/C/cplay.windows.directsound.c
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "physics_girl_st.h"
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_EXTRA_LEAN
|
||||||
|
#include <Windows.h>
|
||||||
|
#include "mmsystem.h"
|
||||||
|
#include "mmreg.h"
|
||||||
|
#define CINTERFACE
|
||||||
|
#include <dsound.h>
|
||||||
|
|
||||||
|
#ifndef DSBCAPS_TRUEPLAYPOSITION // Not defined in MinGW dsound headers, so let's add it
|
||||||
|
#define DSBCAPS_TRUEPLAYPOSITION 0x00080000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SUsample sound_buffer[SU_LENGTH_IN_SAMPLES * SU_CHANNEL_COUNT];
|
||||||
|
WAVEFORMATEX wave_format = {
|
||||||
|
#ifdef SU_SAMPLE_FLOAT
|
||||||
|
WAVE_FORMAT_IEEE_FLOAT,
|
||||||
|
#else
|
||||||
|
WAVE_FORMAT_PCM,
|
||||||
|
#endif
|
||||||
|
SU_CHANNEL_COUNT,
|
||||||
|
SU_SAMPLE_RATE,
|
||||||
|
SU_SAMPLE_RATE * SU_SAMPLE_SIZE * SU_CHANNEL_COUNT,
|
||||||
|
SU_SAMPLE_SIZE * SU_CHANNEL_COUNT,
|
||||||
|
SU_SAMPLE_SIZE*8,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
DSBUFFERDESC buffer_description = {
|
||||||
|
sizeof(DSBUFFERDESC),
|
||||||
|
DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS | DSBCAPS_TRUEPLAYPOSITION,
|
||||||
|
SU_LENGTH_IN_SAMPLES * SU_SAMPLE_SIZE * SU_CHANNEL_COUNT,
|
||||||
|
0,
|
||||||
|
&wave_format,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char **args) {
|
||||||
|
// Load gm.dls if necessary.
|
||||||
|
#ifdef SU_LOAD_GMDLS
|
||||||
|
su_load_gmdls();
|
||||||
|
#endif // SU_LOAD_GMDLS
|
||||||
|
|
||||||
|
HWND hWnd = GetForegroundWindow();
|
||||||
|
if(hWnd == NULL) {
|
||||||
|
hWnd = GetDesktopWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
LPDIRECTSOUND direct_sound;
|
||||||
|
LPDIRECTSOUNDBUFFER direct_sound_buffer;
|
||||||
|
DirectSoundCreate(0, &direct_sound, 0);
|
||||||
|
IDirectSound_SetCooperativeLevel(direct_sound, hWnd, DSSCL_PRIORITY);
|
||||||
|
IDirectSound_CreateSoundBuffer(direct_sound, &buffer_description, &direct_sound_buffer, NULL);
|
||||||
|
|
||||||
|
LPVOID p1;
|
||||||
|
DWORD l1;
|
||||||
|
IDirectSoundBuffer_Lock(direct_sound_buffer, 0, SU_LENGTH_IN_SAMPLES * SU_CHANNEL_COUNT * SU_SAMPLE_SIZE, &p1, &l1, NULL, NULL, 0);
|
||||||
|
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)su_render_song, p1, 0, 0);
|
||||||
|
IDirectSoundBuffer_Play(direct_sound_buffer, 0, 0, 0);
|
||||||
|
|
||||||
|
// We need to handle windows messages properly while playing, as waveOutWrite is async.
|
||||||
|
MSG msg = {0};
|
||||||
|
DWORD last_play_cursor = 0;
|
||||||
|
for(DWORD play_cursor = 0; play_cursor >= last_play_cursor; IDirectSoundBuffer_GetCurrentPosition(direct_sound_buffer, (DWORD*)&play_cursor, NULL)) {
|
||||||
|
while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
|
||||||
|
TranslateMessage(&msg);
|
||||||
|
DispatchMessageA(&msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
last_play_cursor = play_cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
SUsample sound_buffer[SU_LENGTH_IN_SAMPLES * SU_CHANNEL_COUNT];
|
SUsample sound_buffer[SU_LENGTH_IN_SAMPLES * SU_CHANNEL_COUNT];
|
||||||
HWAVEOUT wave_out_handle;
|
HWAVEOUT wave_out_handle;
|
||||||
WAVEFORMATEX WaveFMT = {
|
WAVEFORMATEX wave_format = {
|
||||||
#ifdef SU_SAMPLE_FLOAT
|
#ifdef SU_SAMPLE_FLOAT
|
||||||
WAVE_FORMAT_IEEE_FLOAT,
|
WAVE_FORMAT_IEEE_FLOAT,
|
||||||
#else
|
#else
|
||||||
@ -22,7 +22,7 @@ WAVEFORMATEX WaveFMT = {
|
|||||||
SU_SAMPLE_SIZE*8,
|
SU_SAMPLE_SIZE*8,
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
WAVEHDR WaveHDR = {
|
WAVEHDR wave_header = {
|
||||||
(LPSTR)sound_buffer,
|
(LPSTR)sound_buffer,
|
||||||
SU_LENGTH_IN_SAMPLES * SU_SAMPLE_SIZE * SU_CHANNEL_COUNT,
|
SU_LENGTH_IN_SAMPLES * SU_SAMPLE_SIZE * SU_CHANNEL_COUNT,
|
||||||
0,
|
0,
|
||||||
@ -32,7 +32,7 @@ WAVEHDR WaveHDR = {
|
|||||||
0,
|
0,
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
MMTIME MMTime = {
|
MMTIME mmtime = {
|
||||||
TIME_SAMPLES,
|
TIME_SAMPLES,
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
@ -48,11 +48,11 @@ int main(int argc, char **args) {
|
|||||||
// We render in the background while playing already. Fortunately,
|
// We render in the background while playing already. Fortunately,
|
||||||
// Windows is slow with the calls below, so we're not worried that
|
// Windows is slow with the calls below, so we're not worried that
|
||||||
// we don't have enough samples ready before the track starts.
|
// we don't have enough samples ready before the track starts.
|
||||||
waveOutOpen(&wave_out_handle, WAVE_MAPPER, &WaveFMT, 0, 0, CALLBACK_NULL);
|
waveOutOpen(&wave_out_handle, WAVE_MAPPER, &wave_format, 0, 0, CALLBACK_NULL);
|
||||||
waveOutWrite(wave_out_handle, &WaveHDR, sizeof(WaveHDR));
|
waveOutWrite(wave_out_handle, &wave_header, sizeof(wave_header));
|
||||||
|
|
||||||
// We need to handle windows messages properly while playing, as waveOutWrite is async.
|
// We need to handle windows messages properly while playing, as waveOutWrite is async.
|
||||||
for(MSG msg = {0}; MMTime.u.sample != SU_LENGTH_IN_SAMPLES; waveOutGetPosition(wave_out_handle, &MMTime, sizeof(MMTIME))) {
|
for(MSG msg = {0}; mmtime.u.sample != SU_LENGTH_IN_SAMPLES; waveOutGetPosition(wave_out_handle, &mmtime, sizeof(MMTIME))) {
|
||||||
while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
|
while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
|
||||||
TranslateMessage(&msg);
|
TranslateMessage(&msg);
|
||||||
DispatchMessageA(&msg);
|
DispatchMessageA(&msg);
|
72
examples/code/C/cwav.c
Normal file
72
examples/code/C/cwav.c
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "physics_girl_st.h"
|
||||||
|
|
||||||
|
#define WAVE_FORMAT_PCM 0x1
|
||||||
|
#define WAVE_FORMAT_IEEE_FLOAT 0x3
|
||||||
|
|
||||||
|
static SUsample sound_buffer[SU_LENGTH_IN_SAMPLES * SU_CHANNEL_COUNT];
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
typedef struct {
|
||||||
|
char riff[4];
|
||||||
|
uint32_t file_size;
|
||||||
|
char wavefmt[8];
|
||||||
|
} riff_header_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char data[4];
|
||||||
|
uint32_t data_size;
|
||||||
|
} data_header_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
riff_header_t riff_header;
|
||||||
|
uint32_t riff_header_size;
|
||||||
|
uint16_t sample_type;
|
||||||
|
uint16_t channel_count;
|
||||||
|
uint32_t sample_rate;
|
||||||
|
uint32_t bytes_per_second;
|
||||||
|
uint16_t bytes_per_channel;
|
||||||
|
uint16_t bits_per_sample;
|
||||||
|
data_header_t data_header;
|
||||||
|
} wave_header_t;
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
int main(int argc, char **args) {
|
||||||
|
wave_header_t wave_header = {
|
||||||
|
.riff_header = (riff_header_t) {
|
||||||
|
.riff = "RIFF",
|
||||||
|
.file_size = sizeof(wave_header_t) + SU_LENGTH_IN_SAMPLES * SU_SAMPLE_SIZE * SU_CHANNEL_COUNT,
|
||||||
|
.wavefmt = "WAVEfmt ",
|
||||||
|
},
|
||||||
|
.riff_header_size = sizeof(riff_header_t),
|
||||||
|
#ifdef SU_SAMPLE_FLOAT
|
||||||
|
.sample_type = WAVE_FORMAT_IEEE_FLOAT,
|
||||||
|
#else // SU_SAMPLE_FLOAT
|
||||||
|
.sample_type = WAVE_FORMAT_PCM,
|
||||||
|
#endif // SU_SAMPLE_FLOAT
|
||||||
|
.channel_count = SU_CHANNEL_COUNT,
|
||||||
|
.sample_rate = SU_SAMPLE_RATE,
|
||||||
|
.bytes_per_second = SU_SAMPLE_SIZE * SU_SAMPLE_RATE * SU_CHANNEL_COUNT,
|
||||||
|
.bytes_per_channel = SU_SAMPLE_SIZE * SU_CHANNEL_COUNT,
|
||||||
|
.bits_per_sample = SU_SAMPLE_SIZE * 8,
|
||||||
|
.data_header = (data_header_t) {
|
||||||
|
.data = "data",
|
||||||
|
.data_size = sizeof(data_header_t) + SU_LENGTH_IN_SAMPLES * SU_SAMPLE_SIZE * SU_CHANNEL_COUNT
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Load gm.dls if necessary.
|
||||||
|
#ifdef SU_LOAD_GMDLS
|
||||||
|
su_load_gmdls();
|
||||||
|
#endif // SU_LOAD_GMDLS
|
||||||
|
|
||||||
|
su_render_song(sound_buffer);
|
||||||
|
|
||||||
|
FILE *file = fopen("physics_girl_st.wav", "wb");
|
||||||
|
fwrite(&wave_header, sizeof(wave_header_t), 1, file);
|
||||||
|
fwrite((uint8_t *)sound_buffer, 1, SU_LENGTH_IN_SAMPLES * SU_SAMPLE_SIZE * SU_CHANNEL_COUNT, file);
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -1 +1,5 @@
|
|||||||
|
set_directory_properties(PROPERTIES EXCLUDE_FROM_ALL ON)
|
||||||
|
add_custom_target(examples)
|
||||||
|
|
||||||
|
add_subdirectory(asm)
|
||||||
add_subdirectory(C)
|
add_subdirectory(C)
|
||||||
|
10
examples/code/asm/386/CMakeLists.txt
Normal file
10
examples/code/asm/386/CMakeLists.txt
Normal 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")
|
81
examples/code/asm/386/asmplay.elf32.asm
Normal file
81
examples/code/asm/386/asmplay.elf32.asm
Normal 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
|
120
examples/code/asm/386/asmplay.win32.asm
Normal file
120
examples/code/asm/386/asmplay.win32.asm
Normal 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
|
91
examples/code/asm/386/asmwav.elf32.asm
Normal file
91
examples/code/asm/386/asmwav.elf32.asm
Normal 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
|
102
examples/code/asm/386/asmwav.win32.asm
Normal file
102
examples/code/asm/386/asmwav.win32.asm
Normal 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
|
58
examples/code/asm/CMakeLists.txt
Normal file
58
examples/code/asm/CMakeLists.txt
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# identifier: Name of the example
|
||||||
|
# songfile: File path of the song YAML file.
|
||||||
|
# architecture: 386 or amd64
|
||||||
|
# abi: 32 or 64
|
||||||
|
# windows_libraries: All libraries that you need to link on Windows
|
||||||
|
# unix_libraries: All libraries that you need to link on unix
|
||||||
|
function(add_asm_example identifier songfile architecture sizeof_void_ptr windows_libraries unix_libraries)
|
||||||
|
get_filename_component(songprefix ${songfile} NAME_WE)
|
||||||
|
|
||||||
|
# Generate the song assembly file
|
||||||
|
add_custom_command(
|
||||||
|
COMMAND
|
||||||
|
${compilecmd} -arch=${architecture} -o ${songprefix}_${architecture}.asm ${songfile}
|
||||||
|
WORKING_DIRECTORY
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
DEPENDS
|
||||||
|
${songfile}
|
||||||
|
OUTPUT
|
||||||
|
${songprefix}_${architecture}.asm
|
||||||
|
${songprefix}_${architecture}.h
|
||||||
|
${songprefix}_${architecture}.inc
|
||||||
|
COMMENT
|
||||||
|
"Compiling ${PROJECT_SOURCE_DIR}/examples/patches/physics-girl-st.yml..."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Platform dependent options
|
||||||
|
if(WIN32)
|
||||||
|
set(abi win)
|
||||||
|
set(libraries ${windows_libraries})
|
||||||
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||||
|
set(link_options -nostartfiles)
|
||||||
|
endif()
|
||||||
|
elseif(UNIX)
|
||||||
|
set(abi elf)
|
||||||
|
set(link_options -z noexecstack -no-pie)
|
||||||
|
set(libraries ${unix_libraries})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Add target
|
||||||
|
add_executable(${identifier}-${architecture}
|
||||||
|
${identifier}.${abi}${sizeof_void_ptr}.asm
|
||||||
|
${songprefix}_${architecture}.asm
|
||||||
|
${songprefix}_${architecture}.inc
|
||||||
|
)
|
||||||
|
set_target_properties(${identifier}-${architecture} PROPERTIES ASM_NASM_COMPILE_OPTIONS -f${abi}${sizeof_void_ptr})
|
||||||
|
target_include_directories(${identifier}-${architecture} PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
set_target_properties(${identifier}-${architecture} PROPERTIES LINKER_LANGUAGE C)
|
||||||
|
target_link_options(${identifier}-${architecture} PRIVATE -m${sizeof_void_ptr} ${link_options})
|
||||||
|
target_link_libraries(${identifier}-${architecture} PRIVATE ${libraries})
|
||||||
|
target_compile_definitions(${identifier}-${architecture} PRIVATE TRACK_INCLUDE="${songprefix}_${architecture}.inc")
|
||||||
|
|
||||||
|
# Set up dependencies
|
||||||
|
add_dependencies(${identifier}-${architecture} sointu-compiler)
|
||||||
|
add_dependencies(examples ${identifier}-${architecture})
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
add_subdirectory(386)
|
||||||
|
add_subdirectory(amd64)
|
12
examples/code/asm/amd64/CMakeLists.txt
Normal file
12
examples/code/asm/amd64/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
if(WIN32)
|
||||||
|
set(CMAKE_ASM_NASM_OBJECT_FORMAT win64)
|
||||||
|
elseif(UNIX)
|
||||||
|
set(CMAKE_ASM_NASM_OBJECT_FORMAT elf64)
|
||||||
|
endif()
|
||||||
|
set(CMAKE_ASM_NASM_COMPILE_OBJECT "<CMAKE_ASM_NASM_COMPILER> <INCLUDES> <DEFINES> <FLAGS> -f ${CMAKE_ASM_NASM_OBJECT_FORMAT} -o <OBJECT> <SOURCE>")
|
||||||
|
|
||||||
|
if(UNIX)
|
||||||
|
add_asm_example(asmplay "${PROJECT_SOURCE_DIR}/examples/patches/physics_girl_st.yml" amd64 64 "winmm" "asound;pthread")
|
||||||
|
add_asm_example(asmwav "${PROJECT_SOURCE_DIR}/examples/patches/physics_girl_st.yml" amd64 64 "" "")
|
||||||
|
target_compile_definitions(asmwav-amd64 PRIVATE FILENAME="physics_girl_st.wav")
|
||||||
|
endif()
|
81
examples/code/asm/amd64/asmplay.elf64.asm
Normal file
81
examples/code/asm/amd64/asmplay.elf64.asm
Normal 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:
|
||||||
|
resq 1
|
||||||
|
|
||||||
|
pcm_handle:
|
||||||
|
resq 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:
|
||||||
|
; Prologue
|
||||||
|
push rbp
|
||||||
|
mov rbp, rsp
|
||||||
|
sub rsp, 0x10
|
||||||
|
|
||||||
|
; Unix does not have gm.dls, no need to ifdef and setup here.
|
||||||
|
|
||||||
|
; We render in the background while playing already.
|
||||||
|
mov rcx, sound_buffer
|
||||||
|
lea rdx, su_render_song
|
||||||
|
mov rsi, 0x0
|
||||||
|
mov rdi, render_thread
|
||||||
|
call pthread_create
|
||||||
|
|
||||||
|
; We can't start playing too early or the missing samples will be audible.
|
||||||
|
mov edi, 0x2
|
||||||
|
call sleep
|
||||||
|
|
||||||
|
; Play the track.
|
||||||
|
mov rdi, pcm_handle
|
||||||
|
mov rsi, default_device
|
||||||
|
mov rdx, SND_PCM_STREAM_PLAYBACK
|
||||||
|
mov rcx, 0x0
|
||||||
|
call snd_pcm_open
|
||||||
|
|
||||||
|
; This is unfortunate. amd64 ABI calling convention kicks in.
|
||||||
|
; now we have to maintain the stack pointer :/
|
||||||
|
mov rdi, qword [pcm_handle]
|
||||||
|
sub rsp, 0x8
|
||||||
|
push SU_LENGTH_IN_SAMPLES
|
||||||
|
%ifdef SU_SAMPLE_FLOAT
|
||||||
|
mov rsi, SND_PCM_FORMAT_FLOAT
|
||||||
|
%else ; SU_SAMPLE_FLOAT
|
||||||
|
mov rsi, SND_PCM_FORMAT_S16_LE
|
||||||
|
%endif ; SU_SAMPLE_FLOAT
|
||||||
|
mov rdx, SND_PCM_ACCESS_RW_INTERLEAVED
|
||||||
|
mov rcx, SU_CHANNEL_COUNT
|
||||||
|
mov r8d, SU_SAMPLE_RATE
|
||||||
|
mov r9d, 0x0
|
||||||
|
call snd_pcm_set_params
|
||||||
|
|
||||||
|
mov rdi, qword [pcm_handle]
|
||||||
|
mov rsi, sound_buffer
|
||||||
|
mov rdx, SU_LENGTH_IN_SAMPLES
|
||||||
|
call snd_pcm_writei
|
||||||
|
|
||||||
|
exit:
|
||||||
|
; At least we can skip the epilogue :)
|
||||||
|
leave
|
||||||
|
ret
|
91
examples/code/asm/amd64/asmwav.elf64.asm
Normal file
91
examples/code/asm/amd64/asmwav.elf64.asm
Normal 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:
|
||||||
|
resq 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 rbp
|
||||||
|
mov rbp, rsp
|
||||||
|
sub rsp, 0x10
|
||||||
|
|
||||||
|
; Unix does not have gm.dls, no need to ifdef and setup here.
|
||||||
|
|
||||||
|
; We render the complete track here.
|
||||||
|
mov rdi, sound_buffer
|
||||||
|
call su_render_song
|
||||||
|
|
||||||
|
; Now we open the file and save the track.
|
||||||
|
mov rsi, format
|
||||||
|
mov rdi, filename
|
||||||
|
call fopen
|
||||||
|
mov qword [file], rax
|
||||||
|
|
||||||
|
; Write header
|
||||||
|
mov rcx, qword [file]
|
||||||
|
mov rdx, 0x1
|
||||||
|
mov rsi, wave_file_end - wave_file
|
||||||
|
mov rdi, wave_file
|
||||||
|
call fwrite
|
||||||
|
|
||||||
|
; write data
|
||||||
|
mov rcx, qword [file]
|
||||||
|
mov rdx, 0x1
|
||||||
|
mov rsi, SU_LENGTH_IN_SAMPLES * SU_SAMPLE_SIZE * SU_CHANNEL_COUNT
|
||||||
|
mov rdi, sound_buffer
|
||||||
|
call fwrite
|
||||||
|
|
||||||
|
mov rdi, qword [file]
|
||||||
|
call fclose
|
||||||
|
|
||||||
|
exit:
|
||||||
|
; At least we can skip the epilogue :)
|
||||||
|
leave
|
||||||
|
ret
|
@ -37,9 +37,12 @@
|
|||||||
%define SU_SYNC
|
%define SU_SYNC
|
||||||
{{- end}}
|
{{- end}}
|
||||||
|
|
||||||
section _su_symbols text
|
|
||||||
_su_symbols:
|
_su_symbols:
|
||||||
|
%ifdef MANGLED
|
||||||
extern _su_render_song@4
|
extern _su_render_song@4
|
||||||
|
%else ; MANGLED
|
||||||
|
extern su_render_song
|
||||||
|
%endif ; MANGLED
|
||||||
|
|
||||||
{{- if gt (.SampleOffsets | len) 0}}
|
{{- if gt (.SampleOffsets | len) 0}}
|
||||||
extern _su_load_gmdls
|
extern _su_load_gmdls
|
||||||
|
Loading…
x
Reference in New Issue
Block a user