├── kiyanka.exe ├── libs ├── Gdi32.Lib ├── User32.Lib ├── WinMM.Lib ├── OpenGL32.Lib └── kernel32.Lib ├── .gitmodules ├── kiyanka ├── build.bat ├── CMakeLists.txt ├── aud_io.h └── kiyanka.cc ├── .gitignore ├── common.bat ├── dump-music.bat ├── deps └── get-deps.ps1 ├── dump-music.asm ├── README.md ├── shader.frag ├── intro.asm ├── 4klang.inc └── 4klang.asm /kiyanka.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w23/bepct4k/HEAD/kiyanka.exe -------------------------------------------------------------------------------- /libs/Gdi32.Lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w23/bepct4k/HEAD/libs/Gdi32.Lib -------------------------------------------------------------------------------- /libs/User32.Lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w23/bepct4k/HEAD/libs/User32.Lib -------------------------------------------------------------------------------- /libs/WinMM.Lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w23/bepct4k/HEAD/libs/WinMM.Lib -------------------------------------------------------------------------------- /libs/OpenGL32.Lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w23/bepct4k/HEAD/libs/OpenGL32.Lib -------------------------------------------------------------------------------- /libs/kernel32.Lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w23/bepct4k/HEAD/libs/kernel32.Lib -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "kiyanka/atto"] 2 | path = kiyanka/atto 3 | url = http://github.com/w23/atto 4 | -------------------------------------------------------------------------------- /kiyanka/build.bat: -------------------------------------------------------------------------------- 1 | mkdir build_msvc2017 2 | cd build_msvc2017 3 | cmake -G "Visual Studio 15" .. 4 | 5 | pause 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ~$* 2 | *~ 3 | *.swp 4 | *.html 5 | tags 6 | *.zip 7 | *.obj 8 | *.exe 9 | music.raw 10 | shader.inc 11 | *.vcxproj 12 | *.vcxproj.* 13 | .vs 14 | *.cmake 15 | CMakeCache.txt 16 | CMakeFiles 17 | *.dir 18 | *.sln 19 | Debug 20 | Win32 21 | deps/crinkler22 22 | deps/nasm-2.14.02 23 | -------------------------------------------------------------------------------- /kiyanka/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | project(kiyanka) 4 | 5 | add_executable(kiyanka WIN32 kiyanka.cc atto/src/app_windows.c) 6 | target_include_directories(kiyanka PRIVATE atto/include atto/include/khronos) 7 | 8 | # FIXME find_package(OpenGL) ... 9 | target_link_libraries(kiyanka PRIVATE opengl32 winmm) 10 | -------------------------------------------------------------------------------- /common.bat: -------------------------------------------------------------------------------- 1 | set LIBS= /LIBPATH:libs opengl32.lib winmm.lib kernel32.lib user32.lib gdi32.lib 2 | 3 | set SHADER_MINIFIER=deps\shader_minifier.exe 4 | set NASM=deps\nasm-2.14.02\nasm.exe 5 | set CRINKLER=deps\crinkler22\win64\crinkler.exe 6 | 7 | if not exist %SHADER_MINIFIER% goto get_deps 8 | if not exist %NASM% goto get_deps 9 | if not exist %CRINKLER% goto get_deps 10 | goto end 11 | 12 | :get_deps 13 | Powershell.exe -ExecutionPolicy RemoteSigned -File deps\get-deps.ps1 14 | 15 | :end 16 | -------------------------------------------------------------------------------- /dump-music.bat: -------------------------------------------------------------------------------- 1 | if not defined in_subprocess (cmd /k set in_subprocess=y ^& %0 %*) & exit ) 2 | 3 | call common.bat 4 | 5 | set OPTS= ^ 6 | /ENTRY:start ^ 7 | /SUBSYSTEM:WINDOWS ^ 8 | /PRINT:IMPORTS ^ 9 | /PRINT:LABELS 10 | 11 | %NASM% -fwin32 -o dump-music.obj -DDEBUG dump-music.asm || exit /b 2 12 | %NASM% -fwin32 -o 4klang.obj 4klang.asm || exit /b 3 13 | 14 | %CRINKLER% ^ 15 | %OPTS% ^ 16 | %LIBS% ^ 17 | dump-music.obj 4klang.obj /OUT:dump-music.exe ^ 18 | || exit /b 2 19 | 20 | dump-music.exe 21 | -------------------------------------------------------------------------------- /deps/get-deps.ps1: -------------------------------------------------------------------------------- 1 | $client = New-Object System.Net.WebClient 2 | $script_dir = Split-Path $script:MyInvocation.MyCommand.Path 3 | $script_dir += "\" 4 | 5 | function download([string]$url, [string]$filename) { 6 | Write-Host Downloading $url to $filename 7 | $client.DownloadFile($url, $script_dir + $filename) 8 | } 9 | 10 | function downloadAndUnpack([string]$url, [string]$filename) { 11 | download $url $filename 12 | Write-Host Extracting $filename 13 | $filename = $script_dir + $filename 14 | Expand-Archive $filename -DestinationPath $script_dir -Force 15 | } 16 | 17 | download "https://github.com/laurentlb/Shader_Minifier/releases/download/1.1.6/shader_minifier.exe" "shader_minifier.exe" 18 | downloadAndUnpack "http://crinkler.net/crinkler22.zip" "crinkler22.zip" 19 | downloadANdUnpack "https://www.nasm.us/pub/nasm/releasebuilds/2.14.02/win64/nasm-2.14.02-win64.zip" "nasm-2.14.02-win64.zip" -------------------------------------------------------------------------------- /dump-music.asm: -------------------------------------------------------------------------------- 1 | BITS 32 2 | 3 | %include "4klang.inc" 4 | extern __4klang_render@4 5 | 6 | %macro WINAPI_FUNC 2 7 | %if 1 8 | extern __imp__ %+ %1 %+ @ %+ %2 9 | %define %1 [__imp__ %+ %1 %+ @ %+ %2] 10 | %else 11 | extern _ %+ %1 %+ @ %+ %2 12 | %define %1 _ %+ %1 %+ @ %+ %2 13 | %endif 14 | %endmacro 15 | 16 | WINAPI_FUNC ExitProcess, 4 17 | WINAPI_FUNC CreateFileA, 28 18 | WINAPI_FUNC WriteFile, 20 19 | WINAPI_FUNC CloseHandle, 4 20 | 21 | GENERIC_READ EQU 0x80000000 22 | GENERIC_WRITE EQU 0x40000000 23 | FILE_SHARE_READ EQU 0x00000001 24 | CREATE_ALWAYS EQU 2 25 | FILE_ATTRIBUTE_NORMAL EQU 0x80 26 | 27 | section _sndbuf bss align=1 28 | sound_buffer: resd MAX_SAMPLES * 2 29 | sound_buffer_end: 30 | 31 | %macro FNCALL 1-* 32 | %rep %0-1 33 | %rotate -1 34 | push %1 35 | %endrep 36 | %rotate -1 37 | call %1 38 | %endmacro 39 | 40 | section _filename data align=1 41 | filename: db 'music.raw', 0 42 | 43 | section _text text align=1 44 | _start: 45 | FNCALL __4klang_render@4, sound_buffer 46 | FNCALL CreateFileA, filename, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 47 | push eax 48 | FNCALL WriteFile, eax, sound_buffer, sound_buffer_end - sound_buffer, 0, 0 49 | pop eax 50 | FNCALL CloseHandle, eax 51 | call ExitProcess 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BEPCT4K: A pure assembly framework for making 4k intros 2 | Very simple framework for making generic 4klang + fullscreen fragment shader 4k intros. Written in pure assembly and does not require Visual Studio installed. 3 | Just add `4klang.inc` and `shader.frag` files and run `build-slow.bat` to build a release binary of your intro. 4 | 5 | ## How to use 6 | ### Make music 7 | 1. Make music in [4klang](https://github.com/hzdgopher/4klang) 8 | 2. Export it into `4klang.inc`, replacing existing file here 9 | 10 | ### Make visuals 11 | 1. Make a full screen GLSL fragment shader somewhere, e.g. in [Shadertoy](https://shadertoy.com) or [Bonzomatic](https://github.com/Gargaj/Bonzomatic) 12 | * We plan to provide a more convenient tool for editing shader in sync with music, see [issue #4](https://github.com/w23/bepct4k/issues/4) 13 | 2. Copy shader source to `shader.frag` file 14 | 3. "Port" shader from your tool, e.g. (it may require more editing): 15 | * This tool will create OpenGL 2.1 context, so GL is limited to `#version 130` 16 | * add `uniform float t;`, this is the uniform that will get current time in BPM-dependent music ticks, *not seconds*. 17 | * make `void main()` and `gl_FragColor` and `gl_FragCoord` instead of whatever your tools have 18 | * replace Shadertoy's: 19 | * `iTime` with `t` 20 | * `fResolution` with `vec2(1920., 1080.)` 21 | * Bonzomatic's: 22 | * `fGlobalTime` with `t` 23 | 24 | ### Build 25 | 1. Run `build-fast.bat` to build `intro-fast.exe` 26 | 27 | ### Usage recommendations 28 | Use `build-debug.bat` when first porting your shader. It will build a debug configuration that won't have music, will be windowed, will check all gl calls and `MessageBox` in your face about shader compilation errors. 29 | 30 | Use `build-fast.bat` when iterating and estimating intro size. It will build your intro in seconds. 31 | 32 | Use `build-slow.bat` when crunching for final. It will be 50-100 bytes less than fast variant, but will take several minutes to build. 33 | 34 | ## How it works 35 | 1. Shader Minifier is ran against `shader.frag` file to generate `shader.inc` with minified shader file. 36 | 2. NASM compiles `intro.asm` into `intro.obj`; it includes `shader.inc` and `4klang.inc` for metadata. 37 | 3. NASM compiles `4klang.asm` into `4klang.obj`; in includes `4klang.inc` for music data. 38 | 4. Crinkler links and compresses these object files into a final binary. 39 | 40 | If you understand Russian, you can watch a somewhat more in-depth explanation here: 41 | - [Как сделать 4к интро своими руками из песка и слюны, часть 1](https://www.youtube.com/watch?v=M0whtvWUKsM) 42 | - [Как сделать 4к интро своими руками из песка и слюны, часть 2](https://www.youtube.com/watch?v=Iw2wJo9xXyA) 43 | 44 | ## Dependencies 45 | This framework uses: 46 | - [4klang](https://github.com/hzdgopher/4klang) for music synthesis 47 | - [Shader Minifier.exe](https://github.com/laurentlb/Shader_Minifier) for shader minification 48 | - [NASM](https://nasm.us) for compiling assembly 49 | - [Crinkler](http://crinkler.net) for compressing and linking the final binary 50 | 51 | Kudos to all these projects authors! Without them modern size-coding scene would not be the same! 52 | 53 | All these dependencies are downloaded automatically whenever you run any of the build scripts for the first time. 54 | 55 | ## LICENSE 56 | WTFPL for this repo itself (or Public Domain if you're boring). 57 | 58 | Dependencies have their own respective licenses, consult them. 59 | -------------------------------------------------------------------------------- /shader.frag: -------------------------------------------------------------------------------- 1 | uniform float t; 2 | 3 | float hash1(float f) { return fract(sin(f)*68354.452); } 4 | float hash2(vec2 v) { return hash1(dot(v, vec2(37.467,63.534))); } 5 | 6 | const vec3 E = vec3(.0, .001, 1.); 7 | 8 | float noise2(vec2 v) { 9 | vec2 V=floor(v); v-=V; 10 | v *= v * (3. - 2. * v); 11 | return mix( 12 | mix(hash2(V), hash2(V+E.zx), v.x), 13 | mix(hash2(V+E.xz), hash2(V+E.zz), v.x), v.y); 14 | } 15 | 16 | const float PI2 = 3.1415926 * 2.; 17 | 18 | vec3 iqcolor(vec3 a, vec3 b, vec3 c, vec3 d, float t) { 19 | return a + b * cos(PI2 * (c + t * d)); 20 | } 21 | 22 | vec3 c1(float t) { 23 | return iqcolor(vec3(0.5, 0.5, 0.5), 24 | vec3(0.5, 0.5, 0.5), 25 | vec3(1.0, 1.0, 1.0), 26 | vec3(0.00, 0.33, 0.67), t); 27 | } 28 | 29 | vec3 rep3(vec3 p, vec3 s) { 30 | return mod(p, s) - s * .5; 31 | } 32 | 33 | float vmax(vec3 p) { return max(max(p.x, p.y), p.z); } 34 | float box3(vec3 p, vec3 s) { 35 | return vmax(abs(p) - s); 36 | } 37 | 38 | mat2 Rm(float a) { float c=cos(a), s=sin(a); return mat2(c,s,-s,c); } 39 | 40 | mat2 bm; 41 | 42 | float PI = 3.1415926; 43 | 44 | float wball; 45 | float wgnd; 46 | float w(vec3 p) { 47 | vec3 bp = p; 48 | bp.xz *= bm; 49 | bp.yz *= bm; 50 | float box = box3(bp, vec3(.5)); 51 | float phase = t/8., ph = fract(phase) ; 52 | phase = floor(phase) + mix(ph, ph * ph, step(32., t)); 53 | wball = length(rep3(p, vec3(.4 + .1*sin(phase * PI * 2.)))) - .15; 54 | float h = noise2(p.xz) + .25 * noise2(p.xz*2.3+vec2(t)); 55 | wgnd = p.y + .5 + h*h; 56 | //max(ball, p.y - .8 * sin(t)) 57 | return min(max(wball, box), wgnd); 58 | } 59 | 60 | vec3 wn(vec3 p) { 61 | return normalize(vec3( 62 | w(p+E.yxx), w(p+E.xyx), w(p+E.xxy)) - w(p)); //xyzw rgba 63 | } 64 | 65 | //void mainImage( out vec4 fragColor, in vec2 fragCoord ) { 66 | void main() { 67 | // Normalized pixel coordinates (from 0 to 1) 68 | vec2 iResolution = vec2(1920., 1080.); 69 | vec2 uv = gl_FragCoord.xy/iResolution.xy - .5; 70 | uv.x *= iResolution.x / iResolution.y; 71 | 72 | //fragColor = vec4(noise2(uv*5.)); return; 73 | 74 | //t = iTime; 75 | bm = Rm(t/4.); 76 | 77 | vec3 skycolor = vec3(.4, .7, .9); 78 | vec3 C = skycolor; 79 | 80 | mat2 mc = Rm(.2), mc2 = Rm(t/8.); 81 | vec3 O = vec3(0., 0., 3.); 82 | vec3 D = normalize(vec3(uv, -2.)); 83 | O.yz *= mc; D.yz *= mc; 84 | O.xz *= mc2; D.xz *= mc2; 85 | 86 | float L = 10.; 87 | float i, d, l = 0.; 88 | vec3 P; 89 | float Ni = 200.; 90 | for (i = 0.; i < Ni; ++i) { 91 | P = O + D * l; 92 | d = w(P); 93 | l += d * .6; 94 | if (d < .001 * l || l > L) break; 95 | } 96 | 97 | if (l < L) { 98 | float spec = 25.; 99 | vec3 alb = vec3(1.); 100 | 101 | if (d == wgnd) { 102 | alb = c1(P.y-3.);//vec3(.5, .8, .6); 103 | } 104 | 105 | vec3 N = wn(P); 106 | 107 | //vec3 sundir = normalize(vec3(sin(t), 1., cos(t))); 108 | vec3 sundir = normalize(vec3(1.));//sin(t), 1., cos(t))); 109 | vec3 h = normalize(sundir-D); 110 | 111 | vec3 suncolor = vec3(.9, .8, .5); 112 | vec3 mc = vec3(1.) * suncolor; 113 | 114 | vec3 c = suncolor * alb * ( 115 | max(0., dot(N, sundir)) + pow(max(0., dot(N, h)), spec)); 116 | 117 | c += alb * skycolor * max(N.y, 0.); 118 | c += 2. * float(i/Ni); 119 | 120 | C = mix(C, c, smoothstep(L, L*.5, l)); 121 | } 122 | 123 | C *= pow(smoothstep(0., 32., t), 2.); 124 | 125 | // Output to screen 126 | gl_FragColor = vec4(sqrt(C), .0); 127 | } 128 | -------------------------------------------------------------------------------- /kiyanka/aud_io.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | typedef void(*audio_callback_f)(void *userdata, float *samples, int nsamples); 8 | typedef void(*midi_callback_f)(void *userdata, const unsigned char *data, int bytes); 9 | int audioOpen(int samplerate, int channels, void *userdata, audio_callback_f acb, const char *dev, midi_callback_f mcb); 10 | void audioClose(); 11 | 12 | #if defined(AUDIO_IMPLEMENT) 13 | #if defined(__linux__) 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | static struct { 22 | snd_pcm_t *pcm; 23 | snd_rawmidi_t *midi; 24 | audio_callback_f acb; 25 | midi_callback_f mcb; 26 | pthread_t thread; 27 | int should_exit; 28 | int channels; 29 | } audio_; 30 | 31 | #define AUDIO_BUFFER_SIZE 256 32 | #define MIDI_BUFFER_SIZE 128 33 | 34 | static void *audio_Thread(void *data) { 35 | // TODO priority 36 | while (!__atomic_load_n(&audio_.should_exit, __ATOMIC_SEQ_CST)) { 37 | float buffer[AUDIO_BUFFER_SIZE]; 38 | unsigned char midibuf[MIDI_BUFFER_SIZE]; 39 | 40 | if (audio_.midi && audio_.mcb) { 41 | int err = snd_rawmidi_read(audio_.midi, midibuf, sizeof(midibuf)); 42 | if (err < 0 && err != -EAGAIN) { 43 | printf("midi_read: %s\n", snd_strerror(err)); 44 | return NULL; 45 | } 46 | 47 | if (err > 0) 48 | audio_.mcb(data, midibuf, err); 49 | } 50 | 51 | audio_.acb(data, buffer, AUDIO_BUFFER_SIZE / audio_.channels); 52 | 53 | int err = snd_pcm_writei(audio_.pcm, buffer, AUDIO_BUFFER_SIZE / audio_.channels); 54 | if (err < 0) err = snd_pcm_recover(audio_.pcm, err, 0); 55 | if (err < 0) { 56 | printf("recover error: %s\n", snd_strerror(err)); 57 | break; 58 | } 59 | } 60 | 61 | return NULL; 62 | } 63 | 64 | int audioOpen(int samplerate, int channels, void *userdata, audio_callback_f acb, const char *midi, midi_callback_f mcb) { 65 | int err = snd_pcm_open(&audio_.pcm, "default", SND_PCM_STREAM_PLAYBACK, 0); 66 | if (err < 0) { 67 | printf("Playback open error: %s\n", snd_strerror(err)); 68 | return -1; 69 | } 70 | 71 | err = snd_pcm_set_params(audio_.pcm, 72 | SND_PCM_FORMAT_FLOAT_LE, 73 | SND_PCM_ACCESS_RW_INTERLEAVED, 74 | channels, samplerate, 1, 10000); 75 | if (err < 0) { 76 | printf("set_params error: %s\n", snd_strerror(err)); 77 | return -2; 78 | } 79 | 80 | if (mcb && midi && midi[0] != '\0') { 81 | err = snd_rawmidi_open(&audio_.midi, NULL, midi, SND_RAWMIDI_NONBLOCK); 82 | if (err < 0) { 83 | printf("rawmidi_open: %s\n", snd_strerror(err)); 84 | return -3; 85 | } 86 | } else { 87 | audio_.midi = NULL; 88 | } 89 | 90 | audio_.channels = channels; 91 | audio_.should_exit = 0; 92 | audio_.acb = acb; 93 | audio_.mcb = mcb; 94 | err = pthread_create(&audio_.thread, NULL, audio_Thread, userdata); 95 | if (err) { 96 | printf("pthread_create: %d\n", err); 97 | return -4; 98 | } 99 | 100 | return 1; 101 | } 102 | 103 | void audioClose() { 104 | __atomic_store_n(&audio_.should_exit, 1, __ATOMIC_SEQ_CST); 105 | pthread_join(audio_.thread, NULL); 106 | snd_pcm_close(audio_.pcm); 107 | // TODO Close midi 108 | } 109 | #elif defined(_WIN32) 110 | 111 | #include 112 | #include 113 | #include 114 | #include 115 | 116 | #define NUM_BUFFERS 4 117 | #define BUFFER_SIZE (4 * 2 * 1024) 118 | 119 | static struct { 120 | audio_callback_f acb; 121 | void *userdata; 122 | int channels; 123 | HWAVEOUT wout; 124 | int nhdr; 125 | WAVEHDR hdr[NUM_BUFFERS]; 126 | HANDLE sem; 127 | } audio_; 128 | 129 | static void headerInit(WAVEHDR *hdr, int size) { 130 | memset(hdr, 0, sizeof(*hdr)); 131 | hdr->lpData = (LPSTR)malloc(size); 132 | hdr->dwBufferLength = size; 133 | waveOutPrepareHeader(audio_.wout, hdr, sizeof(*hdr)); 134 | } 135 | 136 | static void headerDtor(WAVEHDR *hdr) { 137 | free(hdr->lpData); 138 | waveOutUnprepareHeader(audio_.wout, hdr, sizeof(*hdr)); 139 | } 140 | 141 | void CALLBACK waveCallback(HWAVEOUT hWave, UINT uMsg, DWORD dwUser, 142 | DWORD dw1, DWORD dw2) 143 | { 144 | if (uMsg == WOM_DONE) 145 | { 146 | ReleaseSemaphore((HANDLE)dwUser, 1, NULL); 147 | } 148 | } 149 | 150 | static DWORD WINAPI audioThread(LPVOID unused) { 151 | (void)unused; 152 | 153 | SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); 154 | 155 | for (;;) { 156 | WaitForSingleObject(audio_.sem, INFINITE); 157 | 158 | WAVEHDR *hdr = audio_.hdr + audio_.nhdr; 159 | audio_.acb(audio_.userdata, (float*)hdr->lpData, hdr->dwBufferLength / audio_.channels / 4); 160 | waveOutWrite(audio_.wout, hdr, sizeof(*hdr)); 161 | audio_.nhdr = (audio_.nhdr + 1) % NUM_BUFFERS; 162 | } 163 | } 164 | 165 | int audioOpen(int samplerate, int channels, void *userdata, audio_callback_f acb, const char *midi, midi_callback_f mcb) { 166 | audio_.userdata = userdata; 167 | audio_.acb = acb; 168 | audio_.channels = channels; 169 | 170 | audio_.sem = CreateSemaphore(NULL, NUM_BUFFERS, NUM_BUFFERS, NULL); 171 | 172 | const WAVEFORMATEX wave_fmt = 173 | { 174 | WAVE_FORMAT_IEEE_FLOAT, 175 | channels, 176 | samplerate, 177 | samplerate * channels * sizeof(float), // bytes per sec 178 | sizeof(float) * channels, // block alignment; 179 | sizeof(float) * 8, // bits per sample 180 | 0 // extension not needed 181 | }; 182 | waveOutOpen(&audio_.wout, WAVE_MAPPER, &wave_fmt, (DWORD_PTR)waveCallback, (DWORD_PTR)audio_.sem, CALLBACK_FUNCTION); 183 | 184 | for (int i = 0; i < NUM_BUFFERS; ++i) 185 | headerInit(audio_.hdr + i, BUFFER_SIZE); 186 | 187 | CreateThread(NULL, 0, audioThread, NULL, 0, NULL); 188 | 189 | return 1; 190 | } 191 | 192 | void audioClose() { 193 | // FIXME make thread stop 194 | waveOutReset(audio_.wout); 195 | waveOutClose(audio_.wout); 196 | CloseHandle(audio_.sem); 197 | } 198 | 199 | #else 200 | #error NOT IMPLEMENTED 201 | #endif 202 | 203 | #endif /* if defined(AUDIO_IMPLEMENT) */ 204 | 205 | #ifdef __cplusplus 206 | } /* extern "C" */ 207 | #endif 208 | -------------------------------------------------------------------------------- /intro.asm: -------------------------------------------------------------------------------- 1 | BITS 32 2 | 3 | WIDTH equ 1920 4 | HEIGHT equ 1080 5 | 6 | %ifndef DEBUG 7 | %define FULLSCREEN 8 | %define AUDIO_THREAD 9 | %define GLCHECK 10 | %else 11 | %define NO_AUDIO 12 | %macro GLCHECK 0 13 | call glGetError 14 | test eax, eax 15 | jz %%ok 16 | int 3 17 | %%ok: 18 | %endmacro 19 | %endif 20 | 21 | %ifndef NO_AUDIO 22 | %include "4klang.inc" 23 | extern __4klang_render@4 24 | %else 25 | %define SAMPLE_RATE 44100 26 | %define MAX_SAMPLES 44100*120 27 | %define SAMPLES_PER_TICK 44100/16 28 | %endif 29 | 30 | ;GL_TEXTURE_2D EQU 0x0de1 31 | GL_FRAGMENT_SHADER EQU 0x8b30 32 | ;GL_UNSIGNED_BYTE EQU 0x1401 33 | ;GL_FLOAT EQU 0x1406 34 | ;GL_RGBA EQU 0x1908 35 | ;GL_LINEAR EQU 0x2601 36 | ;GL_TEXTURE_MIN_FILTER EQU 0x2801 37 | ;GL_RGBA16F EQU 0x881a 38 | ;GL_FRAMEBUFFER EQU 0x8d40 39 | ;GL_COLOR_ATTACHMENT0 EQU 0x8ce0 40 | 41 | %macro WINAPI_FUNC 2 42 | %if 1 43 | extern __imp__ %+ %1 %+ @ %+ %2 44 | %define %1 [__imp__ %+ %1 %+ @ %+ %2] 45 | %else 46 | extern _ %+ %1 %+ @ %+ %2 47 | %define %1 _ %+ %1 %+ @ %+ %2 48 | %endif 49 | %endmacro 50 | 51 | %ifdef FULLSCREEN 52 | WINAPI_FUNC ChangeDisplaySettingsA, 8 53 | %endif 54 | %ifdef AUDIO_THREAD 55 | WINAPI_FUNC CreateThread, 24 56 | %endif 57 | %ifdef DEBUG 58 | WINAPI_FUNC MessageBoxA, 16 59 | %endif 60 | WINAPI_FUNC ChoosePixelFormat, 8 61 | WINAPI_FUNC CreateWindowExA, 48 62 | WINAPI_FUNC ExitProcess, 4 63 | WINAPI_FUNC GetAsyncKeyState, 4 64 | WINAPI_FUNC GetDC, 4 65 | WINAPI_FUNC PeekMessageA, 20 66 | WINAPI_FUNC SetPixelFormat, 12 67 | WINAPI_FUNC ShowCursor, 4 68 | WINAPI_FUNC SwapBuffers, 4 69 | WINAPI_FUNC waveOutGetPosition, 12 70 | WINAPI_FUNC waveOutOpen, 24 71 | WINAPI_FUNC waveOutWrite, 12 72 | WINAPI_FUNC wglCreateContext, 4 73 | WINAPI_FUNC wglGetProcAddress, 4 74 | WINAPI_FUNC wglMakeCurrent, 8 75 | ;WINAPI_FUNC glGenTextures, 8 76 | ;WINAPI_FUNC glBindTexture, 8 77 | ;WINAPI_FUNC glTexImage2D, 36 78 | ;WINAPI_FUNC glTexParameteri, 12 79 | WINAPI_FUNC glRects, 16 80 | %ifdef DEBUG 81 | WINAPI_FUNC glGetError, 0 82 | %endif 83 | 84 | %macro FNCALL 1-* 85 | %rep %0-1 86 | %rotate -1 87 | push %1 88 | %endrep 89 | %rotate -1 90 | call %1 91 | %endmacro 92 | 93 | %macro GL_FUNC 1 94 | section _ %+ %1 data align=1 95 | %1: 96 | %defstr %[%1 %+ __str] %1 97 | db %1 %+ __str, 0 98 | %endmacro 99 | 100 | GL_FUNC glCreateShaderProgramv 101 | GL_FUNC glUseProgram 102 | GL_FUNC glGetUniformLocation 103 | ;GL_FUNC glUniform1i 104 | GL_FUNC glUniform1f 105 | ;GL_FUNC glGenFramebuffers 106 | ;GL_FUNC glBindFramebuffer 107 | ;GL_FUNC glFramebufferTexture2D 108 | ;GL_FUNC glUniform1fv 109 | 110 | %ifdef DEBUG 111 | GL_FUNC glGetProgramInfoLog 112 | %endif 113 | 114 | %ifdef DEBUG 115 | WNDCLASS EQU static_ 116 | %else 117 | %define WNDCLASS 0xc018 118 | %endif 119 | 120 | %ifdef FULLSCREEN 121 | section _devmode data align=1 122 | devmode: 123 | times 9 dd 0 124 | db 0x9c, 0, 0, 0 125 | db 0, 0, 0x1c, 0 126 | times 15 dd 0 127 | DD 020H, WIDTH, HEIGHT 128 | times 10 dd 0 129 | %endif 130 | 131 | section _pfd data align=1 132 | pfd: 133 | %if 0 134 | DW 028H, 01H 135 | DD 025H 136 | DB 00H, 020H, 00H, 00H, 00H, 00H, 00H, 00H, 08H, 00H, 00H, 00H, 00H, 00H 137 | DB 00H, 020H, 00H, 00H, 00H, 00H 138 | DD 00H, 00H, 00H 139 | %else 140 | DW 00H, 00H 141 | DD 21H ;025H 142 | DB 00H, 00H, 00H, 00H, 00H, 00H, 00H, 00H, 00H, 00H, 00H, 00H, 00H, 00H 143 | DB 00H, 00H, 00H, 00H, 00H, 00H 144 | DD 00H, 00H, 00H 145 | %endif 146 | 147 | section _wavefmt data align=1 148 | wavefmt: 149 | dw 3 ; wFormatTag = WAVE_FORMAT_IEEE_FLOAT 150 | dw 2 ; nChannels 151 | dd SAMPLE_RATE ; nSamplesPerSec 152 | dd SAMPLE_RATE * 4 * 2; nAvgBytesPerSec 153 | dw 4 * 2 ; nBlockAlign 154 | dw 8 * 4 ; wBitsPerSample 155 | dw 0 ; cbSize 156 | 157 | section _wavehdr data align=1 158 | wavehdr: 159 | dd sound_buffer ; lpData 160 | dd MAX_SAMPLES * 2 * 4 ; dwBufferLength 161 | times 2 dd 0 ; unused stuff 162 | dd 2 ; dwFlags WHDR_PREPARED = 0x00000002 163 | times 4 dd 0 ; unused stuff 164 | wavehdr_size EQU ($ - wavehdr) 165 | 166 | section _mmtime bss align=1 167 | mmtime: resb 12 168 | 169 | section _waveout bss align=1 170 | waveout: resd 8 171 | 172 | section _sndbuf bss align=1 173 | tmp: 174 | sound_buffer: resd MAX_SAMPLES * 2 175 | 176 | %ifdef DEBUG 177 | section _infolog bss align=1 178 | infolog: resb 1024 179 | %endif 180 | 181 | section _shader data align=1 182 | %if 1 183 | %include "shader.inc" 184 | %else 185 | _shader_frag: 186 | db 'uniform int t;' 187 | db 'float t = t/44100.;' 188 | db 'void main(){gl_FragColor = vec4(sin(t));}' 189 | %endif 190 | 191 | section _shdrptr data align=1 192 | src_main: 193 | dd _shader_frag 194 | 195 | section _strings data align=1 196 | %ifdef DEBUG 197 | static_: db "static", 0 198 | %endif 199 | 200 | section _text text align=1 201 | _start: 202 | %if 1 203 | %define ZERO 0 204 | %else 205 | %define ZERO ecx 206 | xor ZERO, ZERO 207 | %endif 208 | 209 | %ifdef FULLSCREEN 210 | FNCALL ChangeDisplaySettingsA, devmode, 4 211 | %endif 212 | 213 | FNCALL ShowCursor, ZERO 214 | FNCALL CreateWindowExA, ZERO, WNDCLASS, ZERO, 0x90000000, ZERO, ZERO, WIDTH, HEIGHT, ZERO, ZERO, ZERO, ZERO 215 | FNCALL GetDC, eax 216 | mov ebp, eax ; ebp = HDC 217 | FNCALL ChoosePixelFormat, ebp, pfd 218 | FNCALL SetPixelFormat, ebp, eax, pfd 219 | FNCALL wglCreateContext, ebp 220 | FNCALL wglMakeCurrent, ebp, eax 221 | GLCHECK 222 | 223 | %ifndef NO_AUDIO 224 | %ifdef AUDIO_THREAD 225 | FNCALL CreateThread, ZERO, ZERO, __4klang_render@4, sound_buffer, ZERO, ZERO 226 | %else 227 | FNCALL __4klang_render@4, sound_buffer 228 | %endif 229 | %endif 230 | 231 | FNCALL wglGetProcAddress, glCreateShaderProgramv 232 | FNCALL eax, GL_FRAGMENT_SHADER, 1, src_main 233 | %ifdef DEBUG 234 | push eax 235 | push infolog 236 | push 0 237 | push 1023 238 | push eax 239 | FNCALL wglGetProcAddress, glGetProgramInfoLog 240 | call eax 241 | push 0 242 | push infolog 243 | push infolog 244 | push 0 245 | call MessageBoxA 246 | pop eax 247 | %endif 248 | mov esi, eax 249 | FNCALL wglGetProcAddress, glUseProgram 250 | FNCALL eax, esi 251 | GLCHECK 252 | 253 | ; PLAY MUSIC 254 | FNCALL waveOutOpen, waveout, byte -1, wavefmt, ZERO, ZERO, ZERO 255 | FNCALL waveOutWrite, dword [waveout], wavehdr, wavehdr_size 256 | 257 | mainloop: 258 | ;mov ebx, esp 259 | ;mov dword [ebx], 4 260 | FNCALL waveOutGetPosition, dword [waveout], mmtime, 12 261 | mov ebx, dword [mmtime + 4] 262 | cmp ebx, MAX_SAMPLES * 8 263 | jge exit 264 | 265 | push 01bH ;GetAsyncKeyState 266 | 267 | ; PeekMessageA 268 | push 1 269 | push 0 270 | push 0 271 | push 0 272 | push 0 273 | 274 | ; SwapBuffers 275 | push ebp 276 | 277 | ; glRects 278 | push 1 279 | push 1 280 | push byte -1 281 | push byte -1 282 | 283 | push ebx 284 | fild dword [esp] 285 | push SAMPLES_PER_TICK * 8 * 4 286 | fild dword [esp] 287 | ;mov dword [esp], SAMPLES_PER_TICK * 8 * 4 288 | ;fidiv dword [esp] 289 | fdivp 290 | pop ebx 291 | fstp dword [esp] 292 | push _var_T 293 | push esi 294 | push glGetUniformLocation 295 | 296 | call wglGetProcAddress 297 | call eax 298 | push eax 299 | push glUniform1f 300 | call wglGetProcAddress 301 | call eax 302 | 303 | call glRects 304 | 305 | call SwapBuffers 306 | call PeekMessageA 307 | call GetAsyncKeyState 308 | jz mainloop 309 | exit: 310 | call ExitProcess 311 | -------------------------------------------------------------------------------- /kiyanka/kiyanka.cc: -------------------------------------------------------------------------------- 1 | #define AUDIO_IMPLEMENT 2 | #include "aud_io.h" 3 | 4 | #include "atto/app.h" 5 | 6 | #define ATTO_GL_H_IMPLEMENT 7 | #include "atto/gl.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #define MSG(...) aAppDebugPrintf(__VA_ARGS__) 14 | 15 | #ifndef WIN32_LEAN_AND_MEAN 16 | #define WIN32_LEAN_AND_MEAN 17 | #endif 18 | #ifndef NOMSG 19 | #define NOMSG 20 | #endif 21 | #include 22 | 23 | typedef FILETIME FrFileTime; 24 | 25 | static FrFileTime readFileTime(const char *filename) { 26 | FrFileTime ft = { 0, 0 }; 27 | const HANDLE fh = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 28 | if (fh == INVALID_HANDLE_VALUE) { 29 | MSG("Cannot check file '%s' modification time: %x", filename, GetLastError()); 30 | return ft; 31 | } 32 | 33 | if (!GetFileTime(fh, NULL, NULL, &ft)) { 34 | MSG("Cannot get file '%s' modification time", filename); 35 | ft.dwHighDateTime = ft.dwLowDateTime = 0; 36 | return ft; 37 | } 38 | 39 | CloseHandle(fh); 40 | return ft; 41 | } 42 | 43 | static int areFileTimesEqual(const FrFileTime *a, const FrFileTime *b) { 44 | return a->dwHighDateTime == b->dwHighDateTime && a->dwLowDateTime == b->dwLowDateTime; 45 | } 46 | 47 | static int readFileContents(const char *filename, char *out, int max_size) { 48 | const HANDLE fh = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 49 | if (fh == INVALID_HANDLE_VALUE) { 50 | MSG("Cannot open file '%s'", filename); 51 | return 0; 52 | } 53 | 54 | LARGE_INTEGER splurge_integer; 55 | if (!GetFileSizeEx(fh, &splurge_integer)) 56 | { 57 | MSG("Cannot get file '%s'", filename); 58 | return 0; 59 | } 60 | 61 | if (splurge_integer.QuadPart > max_size) { 62 | MSG("File '%s' size %d is too large, max %d", 63 | filename, (int)splurge_integer.QuadPart, max_size); 64 | return 0; 65 | } 66 | 67 | const int size = (int)splurge_integer.QuadPart; 68 | DWORD bytes_read = 0; 69 | if (!ReadFile(fh, out, size, &bytes_read, NULL) || bytes_read != size) { 70 | MSG("Cannot read %d bytes from file '%s'", size, filename); 71 | return 0; 72 | } 73 | CloseHandle(fh); 74 | 75 | return size; 76 | } 77 | 78 | #define MAX_SHADER_SOURCE_SIZE 65536 79 | 80 | static const char shader_filename[] = "shader.frag"; 81 | static FrFileTime shader_file_timestamp; 82 | char shader_source[MAX_SHADER_SOURCE_SIZE]; 83 | 84 | static const char shader_vertex_passthrough[] = 85 | "attribute vec2 av2_pos;\n" 86 | "void main() {\n" 87 | "gl_Position = vec4(av2_pos, 0., 1.);\n" 88 | "}" 89 | ; 90 | 91 | static const char shader_fragment_blit[] = 92 | "#version 130\n" 93 | "uniform sampler2D frame;\n" 94 | "uniform vec2 R;\n" 95 | "void main() {\n" 96 | "vec2 ts = vec2(textureSize(frame, 0));\n" 97 | "vec2 k = ts / R;\n" 98 | "float scale = max(k.x, k.y);\n" 99 | "vec2 off = (R-ts/scale)/2. * vec2(step(k.x, k.y), step(k.y, k.x));\n" 100 | "vec2 tc = scale * (gl_FragCoord.xy - off);\n" 101 | "gl_FragColor = vec4(0.);\n" 102 | "if (tc.x >= 0. && tc.x < ts.x && tc.y >= 0. && tc.y < ts.y)\n" 103 | "gl_FragColor = texture2D(frame, tc / (ts + vec2(1.)));\n" 104 | "}\n" 105 | ; 106 | 107 | static const float vertexes_full_quad[] = { 108 | 1.f, -1.f, 109 | 1.f, 1.f, 110 | -1.f, -1.f, 111 | -1.f, 1.f, 112 | }; 113 | 114 | static struct { 115 | AGLDrawMerge merge; 116 | 117 | AGLAttribute frame_attr[1]; 118 | AGLProgramUniform frame_uniform[1]; 119 | AGLDrawSource draw_frame; 120 | AGLDrawTarget target_frame; 121 | 122 | AGLTexture frame_texture; 123 | AGLFramebufferParams frame_buffer_params; 124 | 125 | AGLAttribute screen_attr[1]; 126 | AGLProgramUniform screen_uniform[2]; 127 | AGLDrawSource draw_screen; 128 | AGLDrawTarget target_screen; 129 | } g; 130 | 131 | static void init(void) { 132 | g.draw_screen.program = aGLProgramCreateSimple(shader_vertex_passthrough, shader_fragment_blit); 133 | if (g.draw_screen.program <= 0) { 134 | aAppDebugPrintf("shader error: %s", a_gl_error); 135 | /* \fixme add fatal */ 136 | } 137 | 138 | g.frame_attr[0].name = "av2_pos"; 139 | g.frame_attr[0].buffer = 0; 140 | g.frame_attr[0].size = 2; 141 | g.frame_attr[0].type = GL_FLOAT; 142 | g.frame_attr[0].normalized = GL_FALSE; 143 | g.frame_attr[0].stride = 0; 144 | g.frame_attr[0].ptr = vertexes_full_quad; 145 | 146 | g.frame_uniform[0].name = "t"; 147 | g.frame_uniform[0].type = AGLAT_Float; 148 | g.frame_uniform[0].count = 1; 149 | 150 | g.draw_frame.primitive.mode = GL_TRIANGLE_STRIP; 151 | g.draw_frame.primitive.count = 4; 152 | g.draw_frame.primitive.first = 0; 153 | g.draw_frame.primitive.index.buffer = 0; 154 | g.draw_frame.primitive.index.data.ptr = 0; 155 | g.draw_frame.primitive.index.type = 0; 156 | 157 | g.draw_frame.attribs.p = g.frame_attr; 158 | g.draw_frame.attribs.n = sizeof g.frame_attr / sizeof *g.frame_attr; 159 | 160 | g.draw_frame.uniforms.p = g.frame_uniform; 161 | g.draw_frame.uniforms.n = sizeof g.frame_uniform / sizeof *g.frame_uniform; 162 | 163 | { 164 | AGLTextureUploadData data; 165 | data.format = AGLTF_U8_RGBA; 166 | data.x = data.y = 0; 167 | data.width = 1920; 168 | data.height = 1080; 169 | data.pixels = 0; 170 | 171 | g.frame_texture = aGLTextureCreate(); 172 | aGLTextureUpload(&g.frame_texture, &data); 173 | g.frame_texture.min_filter = AGLTmF_Linear; 174 | } 175 | 176 | g.frame_buffer_params.depth.texture = 0; 177 | g.frame_buffer_params.depth.mode = AGLDBM_Texture; 178 | g.frame_buffer_params.color = &g.frame_texture; 179 | 180 | g.target_frame.viewport.x = g.target_frame.viewport.y = 0; 181 | g.target_frame.viewport.w = 1920; 182 | g.target_frame.viewport.h = 1080; 183 | g.target_frame.framebuffer = &g.frame_buffer_params; 184 | 185 | g.screen_attr[0].name = "av2_pos"; 186 | g.screen_attr[0].buffer = 0; 187 | g.screen_attr[0].size = 2; 188 | g.screen_attr[0].type = GL_FLOAT; 189 | g.screen_attr[0].normalized = GL_FALSE; 190 | g.screen_attr[0].stride = 0; 191 | g.screen_attr[0].ptr = vertexes_full_quad; 192 | 193 | g.screen_uniform[0].name = "frame"; 194 | g.screen_uniform[0].type = AGLAT_Texture; 195 | g.screen_uniform[0].value.texture = &g.frame_texture; 196 | g.screen_uniform[0].count = 1; 197 | 198 | g.screen_uniform[1].name = "R"; 199 | g.screen_uniform[1].type = AGLAT_Vec2; 200 | g.screen_uniform[1].count = 1; 201 | 202 | g.draw_screen.primitive.mode = GL_TRIANGLE_STRIP; 203 | g.draw_screen.primitive.count = 4; 204 | g.draw_screen.primitive.first = 0; 205 | g.draw_screen.primitive.index.buffer = 0; 206 | g.draw_screen.primitive.index.data.ptr = 0; 207 | g.draw_screen.primitive.index.type = 0; 208 | 209 | g.draw_screen.attribs.p = g.screen_attr; 210 | g.draw_screen.attribs.n = sizeof g.screen_attr / sizeof *g.screen_attr; 211 | 212 | g.draw_screen.uniforms.p = g.screen_uniform; 213 | g.draw_screen.uniforms.n = sizeof g.screen_uniform / sizeof *g.screen_uniform; 214 | 215 | aGLAttributeLocate(g.draw_screen.program, g.screen_attr, g.draw_screen.attribs.n); 216 | aGLUniformLocate(g.draw_screen.program, g.screen_uniform, g.draw_screen.uniforms.n); 217 | 218 | g.merge.blend.enable = 0; 219 | g.merge.depth.mode = AGLDM_Disabled; 220 | } 221 | 222 | static void resize(ATimeUs timestamp, unsigned int old_w, unsigned int old_h) { 223 | (void)(timestamp); (void)(old_w); (void)(old_h); 224 | g.target_screen.viewport.x = g.target_screen.viewport.y = 0; 225 | g.target_screen.viewport.w = a_app_state->width; 226 | g.target_screen.viewport.h = a_app_state->height; 227 | 228 | g.target_screen.framebuffer = 0; 229 | } 230 | 231 | static struct { 232 | int pos; 233 | int paused; 234 | int start, end; 235 | int set; 236 | } loop; 237 | 238 | static struct { 239 | float *data; 240 | size_t samples; 241 | int samples_per_row; 242 | } audio; 243 | 244 | const int pattern_length = 16; 245 | 246 | static void timeShift(int rows) { 247 | int next_pos = loop.pos + rows * audio.samples_per_row; 248 | const int loop_length = loop.end - loop.start; 249 | while (next_pos < loop.start) 250 | next_pos += loop_length; 251 | while (next_pos > loop.end) 252 | next_pos -= loop_length; 253 | loop.pos = next_pos; 254 | MSG("pos = %d", next_pos / audio.samples_per_row); 255 | } 256 | 257 | static void keyPress(ATimeUs timestamp, AKey key, int pressed) { 258 | (void)(timestamp); 259 | 260 | if (!pressed) 261 | return; 262 | 263 | switch (key) { 264 | case AK_Esc: 265 | //audioClose(); 266 | aAppTerminate(0); 267 | break; 268 | 269 | case AK_Left: 270 | timeShift(-pattern_length); 271 | break; 272 | case AK_Right: 273 | timeShift(pattern_length); 274 | break; 275 | case AK_Up: 276 | timeShift(4*pattern_length); 277 | break; 278 | case AK_Down: 279 | timeShift(-4*pattern_length); 280 | break; 281 | 282 | case AK_Space: 283 | loop.paused ^= 1; 284 | break; 285 | 286 | case AK_Z: 287 | switch (loop.set) { 288 | case 0: 289 | loop.start = ((loop.pos / audio.samples_per_row) / pattern_length) * audio.samples_per_row * pattern_length; 290 | loop.set = 1; 291 | break; 292 | case 1: 293 | loop.end = (((loop.pos / audio.samples_per_row) + (pattern_length-1)) / pattern_length) * audio.samples_per_row * pattern_length; 294 | loop.set = 2; 295 | break; 296 | case 2: 297 | loop.start = 0; 298 | loop.end = audio.samples; 299 | loop.set = 0; 300 | } 301 | break; 302 | 303 | default: 304 | MSG("Unknown key %d", key); 305 | break; 306 | } 307 | } 308 | 309 | static void paint(ATimeUs timestamp, float dt) { 310 | (void)timestamp; 311 | (void)(dt); 312 | 313 | { 314 | const FrFileTime current_file_time = readFileTime(shader_filename); 315 | if (!areFileTimesEqual(&shader_file_timestamp, ¤t_file_time)) { 316 | shader_file_timestamp = current_file_time; 317 | const int size = readFileContents(shader_filename, shader_source, sizeof(shader_source) - 1); 318 | if (size > 0) { 319 | shader_source[size] = '\0'; 320 | const AGLProgram new_program = aGLProgramCreateSimple(shader_vertex_passthrough, shader_source); 321 | if (new_program <= 0) { 322 | MSG("shader error: %s", a_gl_error); 323 | } else { 324 | MSG("read new shader size %d", size); 325 | 326 | if (g.draw_frame.program > 0) 327 | aGLProgramDestroy(g.draw_frame.program); 328 | g.draw_frame.program = new_program; 329 | 330 | aGLAttributeLocate(g.draw_frame.program, g.frame_attr, g.draw_frame.attribs.n); 331 | aGLUniformLocate(g.draw_frame.program, g.frame_uniform, g.draw_frame.uniforms.n); 332 | } 333 | } 334 | } 335 | } 336 | 337 | if (g.draw_frame.program > 0) { 338 | const float time_row = (float)loop.pos / audio.samples_per_row; 339 | g.frame_uniform[0].value.pf = &time_row; 340 | aGLDraw(&g.draw_frame, &g.merge, &g.target_frame); 341 | } else { 342 | AGLClearParams clear; 343 | 344 | clear.a = 1.f; 345 | clear.r = 1.f;//sinf(t*.1f); 346 | clear.g = 0.f;//sinf(t*.2f); 347 | clear.b = 0.f;//sinf(t*.3f); 348 | clear.depth = 0; 349 | clear.bits = AGLCB_Everything; 350 | 351 | aGLClear(&clear, &g.target_frame); 352 | } 353 | 354 | float R[2] = { a_app_state->width, a_app_state->height }; 355 | g.screen_uniform[1].value.pf = R; 356 | aGLDraw(&g.draw_screen, &g.merge, &g.target_screen); 357 | 358 | /* 359 | { 360 | char title[32]; 361 | snprintf(title, 31, "Kiyanka %.3f", loop.pos / (float)audio.samples_per_row); 362 | SetWindowTextA(GetActiveWindow(), title); 363 | } 364 | */ 365 | } 366 | 367 | static void audioCallback(void *unused, float *samples, int nsamples) { 368 | (void)unused; 369 | if (loop.paused || !audio.data) { 370 | memset(samples, 0, sizeof(*samples) * nsamples * 2); 371 | if (!loop.paused) 372 | loop.pos = (loop.pos + nsamples) % audio.samples; 373 | return; 374 | } 375 | 376 | for (int i = 0; i < nsamples; ++i) { 377 | samples[i * 2] = audio.data[loop.pos * 2]; 378 | samples[i * 2 + 1] = audio.data[loop.pos * 2 + 1]; 379 | loop.pos = (loop.pos + 1) % audio.samples; 380 | 381 | if (loop.set == 2) 382 | if (loop.pos >= loop.end) 383 | loop.pos = loop.start; 384 | } 385 | } 386 | 387 | void attoAppInit(struct AAppProctable *proctable) { 388 | aGLInit(); 389 | init(); 390 | 391 | proctable->resize = resize; 392 | proctable->paint = paint; 393 | proctable->key = keyPress; 394 | 395 | FILE *f = fopen("music.raw", "rb"); 396 | if (!f) { 397 | MSG("Cannot open music.raw"); 398 | } else { 399 | fseek(f, 0, SEEK_END); 400 | const size_t size = ftell(f); 401 | fseek(f, 0, SEEK_SET); 402 | audio.data = (float*)malloc(size); 403 | audio.samples = size / (sizeof(float) * 2); 404 | fread(audio.data, size, 1, f); 405 | fclose(f); 406 | } 407 | 408 | audio.samples_per_row = /*FIXME*/5512; 409 | 410 | loop.start = 0; 411 | loop.end = audio.samples ? audio.samples : 44100 * 60; 412 | 413 | audioOpen(44100, 2, nullptr, audioCallback, nullptr, nullptr); 414 | } 415 | -------------------------------------------------------------------------------- /4klang.inc: -------------------------------------------------------------------------------- 1 | %macro export_func 1 2 | global _%1 3 | _%1: 4 | %endmacro 5 | %define USE_SECTIONS 6 | %define SAMPLE_RATE 44100 7 | %define MAX_INSTRUMENTS 4 8 | %define MAX_VOICES 1 9 | %define HLD 1 10 | %define BPM 120.000000 11 | %define MAX_PATTERNS 12 12 | %define PATTERN_SIZE_SHIFT 4 13 | %define PATTERN_SIZE (1 << PATTERN_SIZE_SHIFT) 14 | %define MAX_TICKS (MAX_PATTERNS*PATTERN_SIZE) 15 | %define SAMPLES_PER_TICK 5512 16 | %define DEF_LFO_NORMALIZE 0.0000453515 17 | %define MAX_SAMPLES (SAMPLES_PER_TICK*MAX_TICKS) 18 | ;%define GO4K_USE_16BIT_OUTPUT 19 | ;%define GO4K_USE_GROOVE_PATTERN 20 | ;%define GO4K_USE_ENVELOPE_RECORDINGS 21 | ;%define GO4K_USE_NOTE_RECORDINGS 22 | %define GO4K_CLIP_OUTPUT 23 | %define GO4K_USE_DST 24 | %define GO4K_USE_DLL 25 | %define GO4K_USE_GLOBAL_DLL 26 | %define GO4K_USE_ENV_CHECK 27 | %define GO4K_USE_ENV_MOD_GM 28 | %define GO4K_USE_VCO_CHECK 29 | %define GO4K_USE_VCO_PHASE_OFFSET 30 | %define GO4K_USE_VCO_SHAPE 31 | %define GO4K_USE_VCO_MOD_TM 32 | %define GO4K_USE_VCO_MOD_SM 33 | %define GO4K_USE_VCF_CHECK 34 | %define GO4K_USE_VCF_MOD_FM 35 | %define GO4K_USE_VCF_HIGH 36 | %define GO4K_USE_VCF_BAND 37 | %define GO4K_USE_VCF_PEAK 38 | %define GO4K_USE_DST_CHECK 39 | %define GO4K_USE_DLL_CHORUS_CLAMP 40 | %define GO4K_USE_DLL_DAMP 41 | %define GO4K_USE_DLL_DC_FILTER 42 | %define GO4K_USE_FSTG_CHECK 43 | %define GO4K_USE_WAVESHAPER_CLIP 44 | %define MAX_DELAY 65536 45 | %define MAX_UNITS 48 46 | %define MAX_UNIT_SLOTS 9 47 | %define GO4K_BEGIN_CMDDEF(def_name) 48 | %define GO4K_END_CMDDEF db 0 49 | %define GO4K_BEGIN_PARAMDEF(def_name) 50 | %define GO4K_END_PARAMDEF 51 | GO4K_ENV_ID equ 1 52 | %macro GO4K_ENV 5 53 | db %1 54 | db %2 55 | db %3 56 | db %4 57 | db %5 58 | %endmacro 59 | %define ATTAC(val) val 60 | %define DECAY(val) val 61 | %define SUSTAIN(val) val 62 | %define RELEASE(val) val 63 | %define GAIN(val) val 64 | struc go4kENV_val 65 | .attac resd 1 66 | .decay resd 1 67 | .sustain resd 1 68 | .release resd 1 69 | .gain resd 1 70 | .size 71 | endstruc 72 | struc go4kENV_wrk 73 | .state resd 1 74 | .level resd 1 75 | .gm resd 1 76 | .am resd 1 77 | .dm resd 1 78 | .sm resd 1 79 | .rm resd 1 80 | .size 81 | endstruc 82 | %define ENV_STATE_ATTAC 0 83 | %define ENV_STATE_DECAY 1 84 | %define ENV_STATE_SUSTAIN 2 85 | %define ENV_STATE_RELEASE 3 86 | %define ENV_STATE_OFF 4 87 | GO4K_VCO_ID equ 2 88 | %macro GO4K_VCO 8 89 | db %1 90 | db %2 91 | %ifdef GO4K_USE_VCO_PHASE_OFFSET 92 | db %3 93 | %endif 94 | %ifdef GO4K_USE_VCO_GATE 95 | db %4 96 | %endif 97 | db %5 98 | %ifdef GO4K_USE_VCO_SHAPE 99 | db %6 100 | %endif 101 | db %7 102 | db %8 103 | %endmacro 104 | %define TRANSPOSE(val) val 105 | %define DETUNE(val) val 106 | %define PHASE(val) val 107 | %define GATES(val) val 108 | %define COLOR(val) val 109 | %define SHAPE(val) val 110 | %define FLAGS(val) val 111 | %define SINE 0x01 112 | %define TRISAW 0x02 113 | %define PULSE 0x04 114 | %define NOISE 0x08 115 | %define LFO 0x10 116 | %define GATE 0x20 117 | %define VCO_STEREO 0x40 118 | struc go4kVCO_val 119 | .transpose resd 1 120 | .detune resd 1 121 | %ifdef GO4K_USE_VCO_PHASE_OFFSET 122 | .phaseofs resd 1 123 | %endif 124 | %ifdef GO4K_USE_VCO_GATE 125 | .gate resd 1 126 | %endif 127 | .color resd 1 128 | %ifdef GO4K_USE_VCO_SHAPE 129 | .shape resd 1 130 | %endif 131 | .gain resd 1 132 | .flags resd 1 133 | .size 134 | endstruc 135 | struc go4kVCO_wrk 136 | .phase resd 1 137 | .tm resd 1 138 | .dm resd 1 139 | .fm resd 1 140 | .pm resd 1 141 | .cm resd 1 142 | .sm resd 1 143 | .gm resd 1 144 | .phase2 resd 1 145 | .size 146 | endstruc 147 | GO4K_VCF_ID equ 3 148 | %macro GO4K_VCF 3 149 | db %1 150 | db %2 151 | db %3 152 | %endmacro 153 | %define LOWPASS 0x1 154 | %define HIGHPASS 0x2 155 | %define BANDPASS 0x4 156 | %define BANDSTOP 0x3 157 | %define ALLPASS 0x7 158 | %define PEAK 0x8 159 | %define STEREO 0x10 160 | %define FREQUENCY(val) val 161 | %define RESONANCE(val) val 162 | %define VCFTYPE(val) val 163 | struc go4kVCF_val 164 | .freq resd 1 165 | .res resd 1 166 | .type resd 1 167 | .size 168 | endstruc 169 | struc go4kVCF_wrk 170 | .low resd 1 171 | .high resd 1 172 | .band resd 1 173 | .freq resd 1 174 | .fm resd 1 175 | .rm resd 1 176 | .low2 resd 1 177 | .high2 resd 1 178 | .band2 resd 1 179 | .size 180 | endstruc 181 | GO4K_DST_ID equ 4 182 | %macro GO4K_DST 3 183 | db %1 184 | %ifdef GO4K_USE_DST_SH 185 | db %2 186 | %ifdef GO4K_USE_DST_STEREO 187 | db %3 188 | %endif 189 | %else 190 | %ifdef GO4K_USE_DST_STEREO 191 | db %3 192 | %endif 193 | %endif 194 | %endmacro 195 | %define DRIVE(val) val 196 | %define SNHFREQ(val) val 197 | %define FLAGS(val) val 198 | struc go4kDST_val 199 | .drive resd 1 200 | %ifdef GO4K_USE_DST_SH 201 | .snhfreq resd 1 202 | %endif 203 | .flags resd 1 204 | .size 205 | endstruc 206 | struc go4kDST_wrk 207 | .out resd 1 208 | .snhphase resd 1 209 | .dm resd 1 210 | .sm resd 1 211 | .out2 resd 1 212 | .size 213 | endstruc 214 | GO4K_DLL_ID equ 5 215 | %macro GO4K_DLL 8 216 | db %1 217 | db %2 218 | db %3 219 | %ifdef GO4K_USE_DLL_DAMP 220 | db %4 221 | %endif 222 | %ifdef GO4K_USE_DLL_CHORUS 223 | db %5 224 | db %6 225 | %endif 226 | db %7 227 | db %8 228 | %endmacro 229 | %define PREGAIN(val) val 230 | %define DRY(val) val 231 | %define FEEDBACK(val) val 232 | %define DEPTH(val) val 233 | %define DAMP(val) val 234 | %define DELAY(val) val 235 | %define COUNT(val) val 236 | struc go4kDLL_val 237 | .pregain resd 1 238 | .dry resd 1 239 | .feedback resd 1 240 | %ifdef GO4K_USE_DLL_DAMP 241 | .damp resd 1 242 | %endif 243 | %ifdef GO4K_USE_DLL_CHORUS 244 | .freq resd 1 245 | .depth 246 | %endif 247 | .delay resd 1 248 | .count resd 1 249 | .size 250 | endstruc 251 | struc go4kDLL_wrk 252 | .index resd 1 253 | .store resd 1 254 | .dcin resd 1 255 | .dcout resd 1 256 | %ifdef GO4K_USE_DLL_CHORUS 257 | .phase resd 1 258 | %endif 259 | .buffer resd MAX_DELAY 260 | .size 261 | endstruc 262 | struc go4kDLL_wrk2 263 | .pm resd 1 264 | .fm resd 1 265 | .im resd 1 266 | .dm resd 1 267 | .sm resd 1 268 | .am resd 1 269 | .size 270 | endstruc 271 | GO4K_FOP_ID equ 6 272 | %macro GO4K_FOP 1 273 | db %1 274 | %endmacro 275 | %define OP(val) val 276 | %define FOP_POP 0x1 277 | %define FOP_ADDP 0x2 278 | %define FOP_MULP 0x3 279 | %define FOP_PUSH 0x4 280 | %define FOP_XCH 0x5 281 | %define FOP_ADD 0x6 282 | %define FOP_MUL 0x7 283 | %define FOP_ADDP2 0x8 284 | %define FOP_LOADNOTE 0x9 285 | %define FOP_MULP2 0xa 286 | struc go4kFOP_val 287 | .flags resd 1 288 | .size 289 | endstruc 290 | struc go4kFOP_wrk 291 | .size 292 | endstruc 293 | GO4K_FST_ID equ 7 294 | %macro GO4K_FST 2 295 | db %1 296 | dw %2 297 | %endmacro 298 | %define AMOUNT(val) val 299 | %define DEST(val) val 300 | %define FST_SET 0x0000 301 | %define FST_ADD 0x4000 302 | %define FST_POP 0x8000 303 | struc go4kFST_val 304 | .amount resd 1 305 | .op1 resd 1 306 | .size 307 | endstruc 308 | struc go4kFST_wrk 309 | .size 310 | endstruc 311 | GO4K_PAN_ID equ 8 312 | %macro GO4K_PAN 1 313 | %ifdef GO4K_USE_PAN 314 | db %1 315 | %endif 316 | %endmacro 317 | %define PANNING(val) val 318 | struc go4kPAN_val 319 | %ifdef GO4K_USE_PAN 320 | .panning resd 1 321 | %endif 322 | .size 323 | endstruc 324 | struc go4kPAN_wrk 325 | .pm resd 1 326 | .size 327 | endstruc 328 | GO4K_OUT_ID equ 9 329 | %macro GO4K_OUT 2 330 | db %1 331 | %ifdef GO4K_USE_GLOBAL_DLL 332 | db %2 333 | %endif 334 | %endmacro 335 | %define AUXSEND(val) val 336 | struc go4kOUT_val 337 | .gain resd 1 338 | %ifdef GO4K_USE_GLOBAL_DLL 339 | .auxsend resd 1 340 | %endif 341 | .size 342 | endstruc 343 | struc go4kOUT_wrk 344 | .am resd 1 345 | .gm resd 1 346 | .size 347 | endstruc 348 | GO4K_ACC_ID equ 10 349 | %macro GO4K_ACC 1 350 | db %1 351 | %endmacro 352 | %define OUTPUT 0 353 | %define AUX 8 354 | %define ACCTYPE(val) val 355 | struc go4kACC_val 356 | .acctype resd 1 357 | .size 358 | endstruc 359 | struc go4kACC_wrk 360 | .size 361 | endstruc 362 | %ifdef GO4K_USE_FLD 363 | GO4K_FLD_ID equ 11 364 | %macro GO4K_FLD 1 365 | db %1 366 | %endmacro 367 | %define VALUE(val) val 368 | struc go4kFLD_val 369 | .value resd 1 370 | .size 371 | endstruc 372 | struc go4kFLD_wrk 373 | .vm resd 1 374 | .size 375 | endstruc 376 | %endif 377 | %ifdef GO4K_USE_FSTG 378 | GO4K_FSTG_ID equ 12 379 | %macro GO4K_FSTG 2 380 | db %1 381 | dw %2 382 | %endmacro 383 | struc go4kFSTG_val 384 | .amount resd 1 385 | .op1 resd 1 386 | .size 387 | endstruc 388 | struc go4kFSTG_wrk 389 | .size 390 | endstruc 391 | %endif 392 | struc go4k_instrument 393 | .release resd 1 394 | .note resd 1 395 | .workspace resd MAX_UNITS*MAX_UNIT_SLOTS 396 | .dlloutl resd 1 397 | .dlloutr resd 1 398 | .outl resd 1 399 | .outr resd 1 400 | .size 401 | endstruc 402 | struc go4k_synth 403 | .instruments resb go4k_instrument.size * MAX_INSTRUMENTS * MAX_VOICES 404 | .global resb go4k_instrument.size * MAX_VOICES 405 | .size 406 | endstruc 407 | %ifdef USE_SECTIONS 408 | section .g4kmuc1 data align=1 409 | %else 410 | section .data align=1 411 | %endif 412 | go4k_patterns 413 | db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 414 | db 48, 0, 0, 48, 0, 0, 0, 48, 0, 0, 48, 0, 0, 0, 0, 0, 415 | db 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 416 | db HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 417 | db HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 418 | db 0, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 419 | db 0, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 60, 420 | db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 43, 48, 39, 421 | db 0, 48, 0, 0, 36, 0, 0, 36, 0, 0, 0, 0, 39, 43, 48, 39, 422 | go4k_patterns_end 423 | %ifdef USE_SECTIONS 424 | section .g4kmuc2 data align=1 425 | %else 426 | section .data 427 | %endif 428 | go4k_pattern_lists 429 | Instrument4List db 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 430 | Instrument5List db 2, 2, 2, 2, 2, 2, 2, 2, 3, 4, 0, 0, 431 | Instrument6List db 0, 0, 5, 6, 5, 6, 5, 6, 0, 0, 0, 0, 432 | Instrument7List db 0, 0, 0, 7, 8, 8, 8, 8, 3, 4, 0, 0, 433 | go4k_pattern_lists_end 434 | %ifdef USE_SECTIONS 435 | section .g4kmuc3 data align=1 436 | %else 437 | section .data 438 | %endif 439 | go4k_synth_instructions 440 | GO4K_BEGIN_CMDDEF(Instrument4) 441 | db GO4K_ENV_ID 442 | db GO4K_FST_ID 443 | db GO4K_ENV_ID 444 | db GO4K_DST_ID 445 | db GO4K_FST_ID 446 | db GO4K_FOP_ID 447 | db GO4K_VCO_ID 448 | db GO4K_FOP_ID 449 | db GO4K_VCF_ID 450 | db GO4K_FOP_ID 451 | db GO4K_VCF_ID 452 | db GO4K_FOP_ID 453 | db GO4K_PAN_ID 454 | db GO4K_OUT_ID 455 | GO4K_END_CMDDEF 456 | GO4K_BEGIN_CMDDEF(Instrument5) 457 | db GO4K_ENV_ID 458 | db GO4K_VCO_ID 459 | db GO4K_FOP_ID 460 | db GO4K_VCF_ID 461 | db GO4K_PAN_ID 462 | db GO4K_DLL_ID 463 | db GO4K_FOP_ID 464 | db GO4K_DLL_ID 465 | db GO4K_FOP_ID 466 | db GO4K_OUT_ID 467 | GO4K_END_CMDDEF 468 | GO4K_BEGIN_CMDDEF(Instrument6) 469 | db GO4K_ENV_ID 470 | db GO4K_FST_ID 471 | db GO4K_ENV_ID 472 | db GO4K_FST_ID 473 | db GO4K_FST_ID 474 | db GO4K_FOP_ID 475 | db GO4K_VCO_ID 476 | db GO4K_VCO_ID 477 | db GO4K_VCO_ID 478 | db GO4K_VCF_ID 479 | db GO4K_VCO_ID 480 | db GO4K_FOP_ID 481 | db GO4K_FOP_ID 482 | db GO4K_FOP_ID 483 | db GO4K_FOP_ID 484 | db GO4K_VCF_ID 485 | db GO4K_PAN_ID 486 | db GO4K_OUT_ID 487 | GO4K_END_CMDDEF 488 | GO4K_BEGIN_CMDDEF(Instrument7) 489 | db GO4K_ENV_ID 490 | db GO4K_FST_ID 491 | db GO4K_FST_ID 492 | db GO4K_ENV_ID 493 | db GO4K_FST_ID 494 | db GO4K_FOP_ID 495 | db GO4K_VCO_ID 496 | db GO4K_VCO_ID 497 | db GO4K_FOP_ID 498 | db GO4K_VCF_ID 499 | db GO4K_DST_ID 500 | db GO4K_VCF_ID 501 | db GO4K_FOP_ID 502 | db GO4K_PAN_ID 503 | db GO4K_OUT_ID 504 | GO4K_END_CMDDEF 505 | GO4K_BEGIN_CMDDEF(Global) 506 | db GO4K_ACC_ID 507 | db GO4K_DLL_ID 508 | db GO4K_FOP_ID 509 | db GO4K_DLL_ID 510 | db GO4K_FOP_ID 511 | db GO4K_ACC_ID 512 | db GO4K_FOP_ID 513 | db GO4K_OUT_ID 514 | GO4K_END_CMDDEF 515 | go4k_synth_instructions_end 516 | %ifdef USE_SECTIONS 517 | section .g4kmuc4 data align=1 518 | %else 519 | section .data 520 | %endif 521 | go4k_synth_parameter_values 522 | GO4K_BEGIN_PARAMDEF(Instrument4) 523 | GO4K_ENV ATTAC(0),DECAY(49),SUSTAIN(81),RELEASE(75),GAIN(128) 524 | GO4K_FST AMOUNT(128),DEST(0*MAX_UNIT_SLOTS+2+FST_SET) 525 | GO4K_ENV ATTAC(0),DECAY(66),SUSTAIN(39),RELEASE(66),GAIN(128) 526 | GO4K_DST DRIVE(28), SNHFREQ(128), FLAGS(0) 527 | GO4K_FST AMOUNT(97),DEST(6*MAX_UNIT_SLOTS+1+FST_SET) 528 | GO4K_FOP OP(FOP_POP) 529 | GO4K_VCO TRANSPOSE(45),DETUNE(64),PHASE(32),GATES(85),COLOR(128),SHAPE(66),GAIN(128),FLAGS(SINE) 530 | GO4K_FOP OP(FOP_MULP) 531 | GO4K_VCF FREQUENCY(78),RESONANCE(121),VCFTYPE(LOWPASS) 532 | GO4K_FOP OP(FOP_PUSH) 533 | GO4K_VCF FREQUENCY(7),RESONANCE(107),VCFTYPE(LOWPASS) 534 | GO4K_FOP OP(FOP_ADDP) 535 | GO4K_PAN PANNING(64) 536 | GO4K_OUT GAIN(128), AUXSEND(0) 537 | GO4K_END_PARAMDEF 538 | GO4K_BEGIN_PARAMDEF(Instrument5) 539 | GO4K_ENV ATTAC(0),DECAY(64),SUSTAIN(0),RELEASE(0),GAIN(128) 540 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(64),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(NOISE) 541 | GO4K_FOP OP(FOP_MULP) 542 | GO4K_VCF FREQUENCY(128),RESONANCE(128),VCFTYPE(BANDPASS) 543 | GO4K_PAN PANNING(64) 544 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(96),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) 545 | GO4K_FOP OP(FOP_XCH) 546 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(18),COUNT(1) 547 | GO4K_FOP OP(FOP_XCH) 548 | GO4K_OUT GAIN(64), AUXSEND(0) 549 | GO4K_END_PARAMDEF 550 | GO4K_BEGIN_PARAMDEF(Instrument6) 551 | GO4K_ENV ATTAC(0),DECAY(72),SUSTAIN(0),RELEASE(72),GAIN(126) 552 | GO4K_FST AMOUNT(128),DEST(0*MAX_UNIT_SLOTS+2+FST_SET) 553 | GO4K_ENV ATTAC(0),DECAY(56),SUSTAIN(0),RELEASE(0),GAIN(128) 554 | GO4K_FST AMOUNT(108),DEST(6*MAX_UNIT_SLOTS+1+FST_SET) 555 | GO4K_FST AMOUNT(72),DEST(7*MAX_UNIT_SLOTS+1+FST_SET) 556 | GO4K_FOP OP(FOP_POP) 557 | GO4K_VCO TRANSPOSE(32),DETUNE(64),PHASE(0),GATES(85),COLOR(128),SHAPE(32),GAIN(64),FLAGS(SINE) 558 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(128),SHAPE(80),GAIN(64),FLAGS(SINE) 559 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(10),GAIN(64),FLAGS(NOISE) 560 | GO4K_VCF FREQUENCY(96),RESONANCE(128),VCFTYPE(LOWPASS) 561 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(16),FLAGS(NOISE) 562 | GO4K_FOP OP(FOP_ADDP) 563 | GO4K_FOP OP(FOP_ADDP) 564 | GO4K_FOP OP(FOP_ADDP) 565 | GO4K_FOP OP(FOP_MULP) 566 | GO4K_VCF FREQUENCY(22),RESONANCE(32),VCFTYPE(HIGHPASS) 567 | GO4K_PAN PANNING(64) 568 | GO4K_OUT GAIN(64), AUXSEND(0) 569 | GO4K_END_PARAMDEF 570 | GO4K_BEGIN_PARAMDEF(Instrument7) 571 | GO4K_ENV ATTAC(32),DECAY(76),SUSTAIN(0),RELEASE(32),GAIN(81) 572 | GO4K_FST AMOUNT(106),DEST(0*MAX_UNIT_SLOTS+2+FST_SET) 573 | GO4K_FST AMOUNT(84),DEST(11*MAX_UNIT_SLOTS+4+FST_SET) 574 | GO4K_ENV ATTAC(0),DECAY(60),SUSTAIN(0),RELEASE(0),GAIN(128) 575 | GO4K_FST AMOUNT(50),DEST(7*MAX_UNIT_SLOTS+6+FST_SET) 576 | GO4K_FOP OP(FOP_POP) 577 | GO4K_VCO TRANSPOSE(64),DETUNE(70),PHASE(40),GATES(85),COLOR(128),SHAPE(80),GAIN(126),FLAGS(SINE) 578 | GO4K_VCO TRANSPOSE(64),DETUNE(48),PHASE(48),GATES(85),COLOR(128),SHAPE(96),GAIN(128),FLAGS(SINE) 579 | GO4K_FOP OP(FOP_ADDP) 580 | GO4K_VCF FREQUENCY(30),RESONANCE(128),VCFTYPE(LOWPASS) 581 | GO4K_DST DRIVE(107), SNHFREQ(128), FLAGS(0) 582 | GO4K_VCF FREQUENCY(35),RESONANCE(128),VCFTYPE(ALLPASS) 583 | GO4K_FOP OP(FOP_MULP) 584 | GO4K_PAN PANNING(64) 585 | GO4K_OUT GAIN(64), AUXSEND(0) 586 | GO4K_END_PARAMDEF 587 | GO4K_BEGIN_PARAMDEF(Global) 588 | GO4K_ACC ACCTYPE(AUX) 589 | GO4K_DLL PREGAIN(40),DRY(128),FEEDBACK(125),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(1),COUNT(8) 590 | GO4K_FOP OP(FOP_XCH) 591 | GO4K_DLL PREGAIN(40),DRY(128),FEEDBACK(125),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(9),COUNT(8) 592 | GO4K_FOP OP(FOP_XCH) 593 | GO4K_ACC ACCTYPE(OUTPUT) 594 | GO4K_FOP OP(FOP_ADDP2) 595 | GO4K_OUT GAIN(64), AUXSEND(0) 596 | GO4K_END_PARAMDEF 597 | go4k_synth_parameter_values_end 598 | %ifdef USE_SECTIONS 599 | section .g4kmuc5 data align=1 600 | %else 601 | section .data 602 | %endif 603 | %ifdef GO4K_USE_DLL 604 | global _go4k_delay_times 605 | _go4k_delay_times 606 | dw 0 607 | dw 1116 608 | dw 1188 609 | dw 1276 610 | dw 1356 611 | dw 1422 612 | dw 1492 613 | dw 1556 614 | dw 1618 615 | dw 1140 616 | dw 1212 617 | dw 1300 618 | dw 1380 619 | dw 1446 620 | dw 1516 621 | dw 1580 622 | dw 1642 623 | dw 11025 624 | dw 22050 625 | %endif 626 | -------------------------------------------------------------------------------- /4klang.asm: -------------------------------------------------------------------------------- 1 | bits 32 2 | 3 | %define WRK ebp ; // alias for unit workspace 4 | %define VAL esi ; // alias for unit values (transformed/untransformed) 5 | %define COM ebx ; // alias for instrument opcodes 6 | 7 | %include "4klang.inc" 8 | 9 | ;// conditional defines 10 | 11 | %ifdef GO4K_USE_VCO_SHAPE 12 | %define INCLUDE_WAVESHAPER 13 | %endif 14 | %ifdef GO4K_USE_DST 15 | %define INCLUDE_WAVESHAPER 16 | %endif 17 | 18 | %ifdef GO4K_USE_VCO_MOD_PM 19 | %define PHASE_RENORMALIZE 20 | %endif 21 | %ifdef GO4K_USE_VCO_PHASE_OFFSET 22 | %define PHASE_RENORMALIZE 23 | %endif 24 | 25 | %ifdef GO4K_USE_ENVELOPE_RECORDINGS 26 | %define GO4K_USE_BUFFER_RECORDINGS 27 | %endif 28 | %ifdef GO4K_USE_NOTE_RECORDINGS 29 | %define GO4K_USE_BUFFER_RECORDINGS 30 | %endif 31 | 32 | ; //======================================================================================== 33 | ; // .bss section 34 | ; //======================================================================================== 35 | %ifdef USE_SECTIONS 36 | section .g4kbss1 bss align=1 37 | %else 38 | section .bss 39 | %endif 40 | 41 | ; // the one and only synth object 42 | %if MAX_VOICES > 1 43 | go4k_voiceindex resd 16 44 | %endif 45 | go4k_transformed_values resd 16 46 | go4k_synth_wrk resb go4k_synth.size 47 | global _go4k_delay_buffer_ofs 48 | _go4k_delay_buffer_ofs resd 1 49 | global _go4k_delay_buffer 50 | _go4k_delay_buffer resd 16*16*go4kDLL_wrk.size 51 | 52 | %ifdef AUTHORING 53 | global __4klang_current_tick 54 | __4klang_current_tick resd 0 55 | %endif 56 | 57 | %ifdef GO4K_USE_ENVELOPE_RECORDINGS 58 | global __4klang_envelope_buffer 59 | __4klang_envelope_buffer resd ((MAX_SAMPLES)/8) ; // samples every 256 samples and stores 16*2 = 32 values 60 | %endif 61 | %ifdef GO4K_USE_NOTE_RECORDINGS 62 | global __4klang_note_buffer 63 | __4klang_note_buffer resd ((MAX_SAMPLES)/8) ; // samples every 256 samples and stores 16*2 = 32 values 64 | %endif 65 | 66 | ; //======================================================================================== 67 | ; // .g4kdat section (initialized data for go4k) 68 | ; //======================================================================================== 69 | %ifdef USE_SECTIONS 70 | section .g4kdat1 data align=1 71 | %else 72 | section .data 73 | %endif 74 | 75 | ; // some synth constants 76 | go4k_synth_commands dd 0 77 | dd _go4kENV_func@0 78 | dd _go4kVCO_func@0 79 | dd _go4kVCF_func@0 80 | dd _go4kDST_func@0 81 | dd _go4kDLL_func@0 82 | dd _go4kFOP_func@0 83 | dd _go4kFST_func@0 84 | dd _go4kPAN_func@0 85 | dd _go4kOUT_func@0 86 | dd _go4kACC_func@0 87 | dd _go4kFLD_func@0 88 | %ifdef GO4K_USE_FSTG 89 | dd _go4kFSTG_func@0 90 | %endif 91 | 92 | %ifdef USE_SECTIONS 93 | section .g4kdat2 data align=1 94 | %else 95 | section .data 96 | %endif 97 | 98 | %ifdef GO4K_USE_16BIT_OUTPUT 99 | c_32767 dd 32767.0 100 | %endif 101 | c_i128 dd 0.0078125 102 | c_RandDiv dd 65536*32768 103 | c_0_5 dd 0.5 104 | %ifdef GO4K_USE_VCO_GATE 105 | c_16 dd 16.0 106 | %endif 107 | %ifdef GO4K_USE_DLL_CHORUS 108 | DLL_DEPTH dd 1024.0 109 | %endif 110 | %ifdef GO4K_USE_DLL_DC_FILTER 111 | c_dc_const dd 0.99609375 ; R = 1 - (pi*2 * frequency /samplerate) 112 | %else 113 | %ifdef GO4K_USE_VCO_GATE 114 | c_dc_const dd 0.99609375 ; R = 1 - (pi*2 * frequency /samplerate) 115 | %endif 116 | %endif 117 | global _RandSeed 118 | _RandSeed dd 1 119 | c_24 dd 24 120 | c_i12 dd 0x3DAAAAAA 121 | FREQ_NORMALIZE dd 0.000092696138 ; // 220.0/(2^(69/12)) / 44100.0 122 | global _LFO_NORMALIZE 123 | _LFO_NORMALIZE dd DEF_LFO_NORMALIZE 124 | %ifdef GO4K_USE_GROOVE_PATTERN 125 | go4k_groove_pattern dw 0011100111001110b 126 | %endif 127 | 128 | ; //======================================================================================== 129 | ; // .crtemui section (emulates crt functions) 130 | ; //======================================================================================== 131 | %ifdef USE_SECTIONS 132 | section .crtemui code align=1 133 | %else 134 | section .text 135 | %endif 136 | 137 | export_func FloatRandomNumber@0 138 | push eax 139 | imul eax,dword [_RandSeed],16007 140 | mov dword [_RandSeed], eax 141 | fild dword [_RandSeed] 142 | fidiv dword [c_RandDiv] 143 | pop eax 144 | ret 145 | 146 | ; //======================================================================================== 147 | ; // .g4kcod* sections (code for go4k) 148 | ; //======================================================================================== 149 | 150 | %ifdef INCLUDE_WAVESHAPER 151 | 152 | %ifdef USE_SECTIONS 153 | section .g4kcod2 code align=1 154 | %else 155 | section .text 156 | %endif 157 | ; //---------------------------------------------------------------------------------------- 158 | ; // Waveshaper function 159 | ; //---------------------------------------------------------------------------------------- 160 | ; // Input : st0 : shaping coeff 161 | ; // st1 : input 162 | ; // Output: st0 : result 163 | ; //---------------------------------------------------------------------------------------- 164 | 165 | go4kWaveshaper: 166 | %ifdef GO4K_USE_WAVESHAPER_CLIP 167 | fxch 168 | fld1 ; // 1 val 169 | fucomi st1 ; // 1 val 170 | jbe short go4kWaveshaper_clip 171 | fchs ; // -1 val 172 | fucomi st1 ; // -1 val 173 | fcmovb st0, st1 ; // val -1 (if val > -1) 174 | go4kWaveshaper_clip: 175 | fstp st1 ; // newval 176 | fxch 177 | %endif 178 | fsub dword [c_0_5] 179 | fadd st0 180 | fst dword [esp-4] ; // amnt in 181 | fadd st0 ; // 2*amnt in 182 | fld1 ; // 1 2*amnt in 183 | fsub dword [esp-4] ; // 1-amnt 2*amnt in 184 | fdivp st1, st0 ; // k in 185 | fld st1 ; // sin k in 186 | fabs ; // a(in) k in 187 | fmul st1 ; // k*a(in) k in 188 | fld1 189 | faddp st1, st0 ; // 1+k*a(in) k in 190 | fxch st1 ; // k 1+k*a(in) in 191 | fld1 192 | faddp st1, st0 ; // 1+k 1+k*a(in) in 193 | fmulp st2 ; // 1+k*a(in) (1+k)*in 194 | fdivp st1, st0 ; // out 195 | ret 196 | 197 | %endif 198 | 199 | %ifdef USE_SECTIONS 200 | section .g4kcod3 code align=1 201 | %else 202 | section .text 203 | %endif 204 | ; //---------------------------------------------------------------------------------------- 205 | ; // unit values preparation/transform 206 | ; //---------------------------------------------------------------------------------------- 207 | go4kTransformValues: 208 | push ecx 209 | xor ecx, ecx 210 | xor eax, eax 211 | mov edx, go4k_transformed_values 212 | go4kTransformValues_loop: 213 | lodsb 214 | push eax 215 | fild dword [esp] 216 | fmul dword [c_i128] 217 | fstp dword [edx+ecx*4] 218 | pop eax 219 | inc ecx 220 | cmp ecx, dword [esp+8] 221 | jl go4kTransformValues_loop 222 | pop ecx 223 | ret 4 224 | 225 | %ifdef USE_SECTIONS 226 | section .g4kcod4 code align=1 227 | %else 228 | section .text 229 | %endif 230 | ; //---------------------------------------------------------------------------------------- 231 | ; // Envelope param mapping 232 | ; //---------------------------------------------------------------------------------------- 233 | go4kENVMap: 234 | fld dword [edx+eax*4] 235 | %ifdef GO4K_USE_ENV_MOD_ADR 236 | fadd dword [WRK+go4kENV_wrk.am+eax*4] 237 | %endif 238 | fimul dword [c_24] 239 | fchs 240 | ; //---------------------------------------------------------------------------------------- 241 | ; // Power function (2^x) 242 | ; //---------------------------------------------------------------------------------------- 243 | ; // Input : st0 : base 244 | ; // st1 : exponent 245 | ; // Output: st0 : result 246 | ; //---------------------------------------------------------------------------------------- 247 | export_func Power@0 ; // base exp 248 | fld1 249 | fadd st0 250 | fyl2x ; // log2_base 251 | fld1 ; // 1 log2_base 252 | fld st1 ; // log2_base 1 log2_base 253 | fprem ; // (frac)log2_base 1 log2_base 254 | f2xm1 ; // 2 ^ '' - 1 1 log2_base 255 | faddp st1, st0 ; // 2 ^ '' (int)log2_base 256 | fscale 257 | fstp st1 258 | ret 259 | 260 | %ifdef USE_SECTIONS 261 | section .g4kcoda code align=1 262 | %else 263 | section .text 264 | %endif 265 | ; //---------------------------------------------------------------------------------------- 266 | ; // ENV Tick 267 | ; //---------------------------------------------------------------------------------------- 268 | ; // IN : WRK = unit workspace 269 | ; // IN : VAL = unit values 270 | ; // IN : ecx = global workspace 271 | ; // OUT : 272 | ; // DIRTY : eax 273 | ; //---------------------------------------------------------------------------------------- 274 | export_func go4kENV_func@0 275 | push 5 276 | call go4kTransformValues 277 | %ifdef GO4K_USE_ENV_CHECK 278 | ; check if current note still active 279 | mov eax, dword [ecx-4] 280 | test eax, eax 281 | jne go4kENV_func_do 282 | fldz 283 | ret 284 | %endif 285 | go4kENV_func_do: 286 | mov eax, dword [ecx-8] ; // is the instrument in release mode (note off)? 287 | test eax, eax 288 | je go4kENV_func_process 289 | mov dword [WRK+go4kENV_wrk.state], ENV_STATE_RELEASE 290 | go4kENV_func_process: 291 | mov eax, dword [WRK+go4kENV_wrk.state] 292 | fld dword [WRK+go4kENV_wrk.level] ; // val - 293 | ; // check for sustain state 294 | cmp al, ENV_STATE_SUSTAIN 295 | je short go4kENV_func_leave2 296 | go4kENV_func_attac: 297 | cmp al, ENV_STATE_ATTAC 298 | jne short go4kENV_func_decay 299 | call go4kENVMap ; // newval 300 | faddp st1, st0 301 | ; // check for end of attac 302 | fld1 ; // 1 newval 303 | fucomi st1 ; // 1 newval 304 | fcmovnb st0, st1 ; // newval 1 (if newval < 1) 305 | jbe short go4kENV_func_statechange 306 | go4kENV_func_decay: 307 | cmp al, ENV_STATE_DECAY 308 | jne short go4kENV_func_release 309 | call go4kENVMap ; // newval 310 | fsubp st1, st0 311 | ; // check for end of decay 312 | fld dword [edx+go4kENV_val.sustain] ; // sustain newval 313 | fucomi st1 ; // sustain newval 314 | fcmovb st0, st1 ; // newval sustain (if newval > sustain) 315 | jnc short go4kENV_func_statechange 316 | go4kENV_func_release: 317 | ; // release state? 318 | cmp al, ENV_STATE_RELEASE 319 | jne short go4kENV_func_leave 320 | call go4kENVMap ; // newval 321 | fsubp st1, st0 322 | ; // check for end of release 323 | fldz ; // 0 newval 324 | fucomi st1 ; // 0 newval 325 | fcmovb st0, st1 ; // newval 0 (if newval > 0) 326 | jc short go4kENV_func_leave 327 | go4kENV_func_statechange: ; // newval 328 | inc dword [WRK+go4kENV_wrk.state] 329 | go4kENV_func_leave: ; // newval bla 330 | fstp st1 331 | ; // store new env value 332 | fst dword [WRK+go4kENV_wrk.level] 333 | go4kENV_func_leave2: 334 | ; // mul by gain 335 | %ifdef GO4K_USE_ENV_MOD_GM 336 | fld dword [edx+go4kENV_val.gain] 337 | fadd dword [WRK+go4kENV_wrk.gm] 338 | fmulp st1, st0 339 | %else 340 | fmul dword [edx+go4kENV_val.gain] 341 | %endif 342 | ret 343 | 344 | 345 | ; //---------------------------------------------------------------------------------------- 346 | ; // VCO Tick 347 | ; //---------------------------------------------------------------------------------------- 348 | ; // IN : WRK = unit workspace 349 | ; // IN : VAL = unit values 350 | ; // IN : ecx = global workspace 351 | ; // OUT : 352 | ; // DIRTY : eax 353 | ; //---------------------------------------------------------------------------------------- 354 | %ifdef USE_SECTIONS 355 | section .g4kcodp code align=1 356 | %else 357 | section .text 358 | %endif 359 | go4kVCO_pulse: 360 | fucomi st1 ; // c p 361 | fld1 362 | jnc short go4kVCO_func_pulse_up ; // +1 c p 363 | fchs ; // -1 c p 364 | go4kVCO_func_pulse_up: 365 | fstp st1 ; // +-1 p 366 | fstp st1 ; // +-1 367 | ret 368 | 369 | %ifdef USE_SECTIONS 370 | section .g4kcodt code align=1 371 | %else 372 | section .text 373 | %endif 374 | go4kVCO_trisaw: 375 | fucomi st1 ; // c p 376 | jnc short go4kVCO_func_trisaw_up 377 | fld1 ; // 1 c p 378 | fsubr st2, st0 ; // 1 c 1-p 379 | fsubrp st1, st0 ; // 1-c 1-p 380 | go4kVCO_func_trisaw_up: 381 | fdivp st1, st0 ; // tp'/tc 382 | fadd st0 ; // 2*'' 383 | fld1 ; // 1 2*'' 384 | fsubp st1, st0 ; // 2*''-1 385 | ret 386 | 387 | %ifdef USE_SECTIONS 388 | section .g4kcods code align=1 389 | %else 390 | section .text 391 | %endif 392 | go4kVCO_sine: 393 | fucomi st1 ; // c p 394 | jnc short go4kVCO_func_sine_do 395 | fstp st1 396 | fsub st0, st0 ; // 0 397 | ret 398 | go4kVCO_func_sine_do 399 | fdivp st1, st0 ; // p/c 400 | fldpi ; // pi p 401 | fadd st0 ; // 2*pi p 402 | fmulp st1, st0 ; // 2*pi*p 403 | fsin ; // sin(2*pi*p) 404 | ret 405 | 406 | %ifdef GO4K_USE_VCO_GATE 407 | %ifdef USE_SECTIONS 408 | section .g4kcodq code align=1 409 | %else 410 | section .text 411 | %endif 412 | go4kVCO_gate: 413 | fxch ; // p c 414 | fstp st1 ; // p 415 | fmul dword [c_16] ; // p' 416 | push eax 417 | push eax 418 | fistp dword [esp] ; // - 419 | fld1 ; // 1 420 | pop eax 421 | and al, 0xf 422 | bt word [VAL-5],ax 423 | jc go4kVCO_gate_bit 424 | fsub st0, st0 ; // 0 425 | go4kVCO_gate_bit: 426 | fld dword [WRK+go4kVCO_wrk.cm] ; // f x 427 | fsub st1 ; // f-x x 428 | fmul dword [c_dc_const] ; // c(f-x) x 429 | faddp st1, st0 ; // x' 430 | fst dword [WRK+go4kVCO_wrk.cm] 431 | pop eax 432 | ret 433 | %endif 434 | 435 | %ifdef USE_SECTIONS 436 | section .g4kcodb code align=1 437 | %else 438 | section .text 439 | %endif 440 | export_func go4kVCO_func@0 441 | %ifdef GO4K_USE_VCO_PHASE_OFFSET 442 | %ifdef GO4K_USE_VCO_SHAPE 443 | %ifdef GO4K_USE_VCO_GATE 444 | push 8 445 | %else 446 | push 7 447 | %endif 448 | %else 449 | %ifdef GO4K_USE_VCO_GATE 450 | push 7 451 | %else 452 | push 6 453 | %endif 454 | %endif 455 | %else 456 | %ifdef GO4K_USE_VCO_SHAPE 457 | %ifdef GO4K_USE_VCO_GATE 458 | push 7 459 | %else 460 | push 6 461 | %endif 462 | %else 463 | %ifdef GO4K_USE_VCO_GATE 464 | push 6 465 | %else 466 | push 5 467 | %endif 468 | %endif 469 | %endif 470 | call go4kTransformValues 471 | %ifdef GO4K_USE_VCO_CHECK 472 | ; check if current note still active 473 | mov eax, dword [ecx-4] 474 | test eax, eax 475 | jne go4kVCO_func_do 476 | %ifdef GO4K_USE_VCO_STEREO 477 | movzx eax, byte [VAL-1] ; // get flags and check for stereo 478 | test al, byte VCO_STEREO 479 | jz short go4kVCO_func_nostereoout 480 | fldz 481 | go4kVCO_func_nostereoout: 482 | %endif 483 | fldz 484 | ret 485 | go4kVCO_func_do: 486 | %endif 487 | movzx eax, byte [VAL-1] ; // get flags 488 | %ifdef GO4K_USE_VCO_STEREO 489 | test al, byte VCO_STEREO 490 | jz short go4kVCO_func_nopswap 491 | fld dword [WRK+go4kVCO_wrk.phase] ;// swap left/right phase values for first stereo run 492 | fld dword [WRK+go4kVCO_wrk.phase2] 493 | fstp dword [WRK+go4kVCO_wrk.phase] 494 | fstp dword [WRK+go4kVCO_wrk.phase2] 495 | go4kVCO_func_nopswap: 496 | %endif 497 | go4kVCO_func_process: 498 | fld dword [edx+go4kVCO_val.transpose] 499 | fsub dword [c_0_5] 500 | %ifdef GO4K_USE_VCO_MOD_TM 501 | fadd dword [WRK+go4kVCO_wrk.tm] 502 | %endif 503 | fdiv dword [c_i128] 504 | fld dword [edx+go4kVCO_val.detune] 505 | fsub dword [c_0_5] 506 | fadd st0 507 | %ifdef GO4K_USE_VCO_STEREO 508 | test al, byte VCO_STEREO 509 | jz short go4kVCO_func_nodswap 510 | fchs ;// negate detune for stereo 511 | go4kVCO_func_nodswap: 512 | %endif 513 | faddp st1 514 | %ifdef GO4K_USE_VCO_MOD_DM 515 | fadd dword [WRK+go4kVCO_wrk.dm] 516 | %endif 517 | ; // st0 now contains the transpose+detune offset 518 | test al, byte LFO 519 | jnz go4kVCO_func_skipnote 520 | fiadd dword [ecx-4] ; // st0 is note, st1 is t+d offset 521 | go4kVCO_func_skipnote: 522 | fmul dword [c_i12] 523 | call _Power@0 524 | test al, byte LFO 525 | jz short go4kVCO_func_normalize_note 526 | fmul dword [_LFO_NORMALIZE] ; // st0 is now frequency for lfo 527 | jmp short go4kVCO_func_normalized 528 | go4kVCO_func_normalize_note: 529 | fmul dword [FREQ_NORMALIZE] ; // st0 is now frequency 530 | go4kVCO_func_normalized: 531 | fadd dword [WRK+go4kVCO_wrk.phase] 532 | %ifdef GO4K_USE_VCO_MOD_FM 533 | fadd dword [WRK+go4kVCO_wrk.fm] 534 | %endif 535 | fld1 536 | fadd st1, st0 537 | fxch 538 | fprem 539 | fstp st1 540 | fst dword [WRK+go4kVCO_wrk.phase] 541 | %ifdef GO4K_USE_VCO_MOD_PM 542 | fadd dword [WRK+go4kVCO_wrk.pm] 543 | %endif 544 | %ifdef GO4K_USE_VCO_PHASE_OFFSET 545 | fadd dword [edx+go4kVCO_val.phaseofs] 546 | %endif 547 | %ifdef PHASE_RENORMALIZE 548 | fld1 549 | fadd st1, st0 550 | fxch 551 | fprem 552 | fstp st1 ; // p 553 | %endif 554 | fld dword [edx+go4kVCO_val.color] ; // c p 555 | %ifdef GO4K_USE_VCO_MOD_CM 556 | fadd dword [WRK+go4kVCO_wrk.cm] ; // c p 557 | %endif 558 | go4kVCO_func_sine: 559 | test al, byte SINE 560 | jz short go4kVCO_func_trisaw 561 | call go4kVCO_sine 562 | go4kVCO_func_trisaw: 563 | test al, byte TRISAW 564 | jz short go4kVCO_func_pulse 565 | call go4kVCO_trisaw 566 | go4kVCO_func_pulse: 567 | test al, byte PULSE 568 | %ifdef GO4K_USE_VCO_GATE 569 | jz short go4kVCO_func_gate 570 | %else 571 | jz short go4kVCO_func_noise 572 | %endif 573 | call go4kVCO_pulse 574 | %ifdef GO4K_USE_VCO_GATE 575 | go4kVCO_func_gate: 576 | test al, byte GATE 577 | jz short go4kVCO_func_noise 578 | call go4kVCO_gate 579 | %endif 580 | go4kVCO_func_noise: 581 | test al, byte NOISE 582 | jz short go4kVCO_func_end 583 | call _FloatRandomNumber@0 584 | fstp st1 585 | fstp st1 586 | go4kVCO_func_end: 587 | %ifdef GO4K_USE_VCO_SHAPE 588 | fld dword [edx+go4kVCO_val.shape] 589 | %ifdef GO4K_USE_VCO_MOD_SM 590 | fadd dword [WRK+go4kVCO_wrk.sm] 591 | %endif 592 | call go4kWaveshaper 593 | %endif 594 | fld dword [edx+go4kVCO_val.gain] 595 | %ifdef GO4K_USE_VCO_MOD_GM 596 | fadd dword [WRK+go4kVCO_wrk.gm] 597 | %endif 598 | fmulp st1, st0 599 | 600 | %ifdef GO4K_USE_VCO_STEREO 601 | test al, byte VCO_STEREO 602 | jz short go4kVCO_func_stereodone 603 | sub al, byte VCO_STEREO 604 | fld dword [WRK+go4kVCO_wrk.phase] ;// swap left/right phase values again for second stereo run 605 | fld dword [WRK+go4kVCO_wrk.phase2] 606 | fstp dword [WRK+go4kVCO_wrk.phase] 607 | fstp dword [WRK+go4kVCO_wrk.phase2] 608 | jmp go4kVCO_func_process 609 | go4kVCO_func_stereodone: 610 | %endif 611 | ret 612 | 613 | %ifdef USE_SECTIONS 614 | section .g4kcodc code align=1 615 | %else 616 | section .text 617 | %endif 618 | ; //---------------------------------------------------------------------------------------- 619 | ; // VCF Tick 620 | ; //---------------------------------------------------------------------------------------- 621 | ; // IN : WRK = unit workspace 622 | ; // IN : VAL = unit values 623 | ; // IN : ecx = global workspace 624 | ; // OUT : 625 | ; // DIRTY : eax 626 | ; //---------------------------------------------------------------------------------------- 627 | export_func go4kVCF_func@0 628 | push 3 629 | call go4kTransformValues 630 | %ifdef GO4K_USE_VCF_CHECK 631 | ; check if current note still active 632 | mov eax, dword [ecx-4] 633 | test eax, eax 634 | jne go4kVCF_func_do 635 | ret 636 | go4kVCF_func_do: 637 | %endif 638 | movzx eax, byte [VAL-1] ; // get type flag 639 | 640 | fld dword [edx+go4kVCF_val.res] ; // r in 641 | %ifdef GO4K_USE_VCF_MOD_RM 642 | fadd dword [WRK+go4kVCF_wrk.rm] 643 | %endif 644 | fstp dword [esp-8] 645 | 646 | fld dword [edx+go4kVCF_val.freq] ; // f in 647 | %ifdef GO4K_USE_VCF_MOD_FM 648 | fadd dword [WRK+go4kVCF_wrk.fm] 649 | %endif 650 | fmul st0, st0 ; // square the input so we never get negative and also have a smoother behaviour in the lower frequencies 651 | fstp dword [esp-4] ; // in 652 | 653 | %ifdef GO4K_USE_VCF_STEREO 654 | test al, byte STEREO 655 | jz short go4kVCF_func_process 656 | add WRK, go4kVCF_wrk.low2 657 | go4kVCF_func_stereoloop: ; // switch channels 658 | fxch st1 ; // inr inl 659 | %endif 660 | 661 | go4kVCF_func_process: 662 | fld dword [esp-8] 663 | fld dword [esp-4] 664 | fmul dword [WRK+go4kVCF_wrk.band] ; // f*b r in 665 | fadd dword [WRK+go4kVCF_wrk.low] ; // l' r in 666 | fst dword [WRK+go4kVCF_wrk.low] ; // l' r in 667 | fsubp st2, st0 ; // r in-l' 668 | fmul dword [WRK+go4kVCF_wrk.band] ; // r*b in-l' 669 | fsubp st1, st0 ; // h' 670 | fst dword [WRK+go4kVCF_wrk.high] ; // h' 671 | fmul dword [esp-4] ; // h'*f 672 | fadd dword [WRK+go4kVCF_wrk.band] ; // b' 673 | fstp dword [WRK+go4kVCF_wrk.band] 674 | fldz 675 | go4kVCF_func_low: 676 | test al, byte LOWPASS 677 | jz short go4kVCF_func_high 678 | fadd dword [WRK+go4kVCF_wrk.low] 679 | go4kVCF_func_high: 680 | %ifdef GO4K_USE_VCF_HIGH 681 | test al, byte HIGHPASS 682 | jz short go4kVCF_func_band 683 | fadd dword [WRK+go4kVCF_wrk.high] 684 | %endif 685 | go4kVCF_func_band: 686 | %ifdef GO4K_USE_VCF_BAND 687 | test al, byte BANDPASS 688 | jz short go4kVCF_func_peak 689 | fadd dword [WRK+go4kVCF_wrk.band] 690 | %endif 691 | go4kVCF_func_peak: 692 | %ifdef GO4K_USE_VCF_PEAK 693 | test al, byte PEAK 694 | jz short go4kVCF_func_processdone 695 | fadd dword [WRK+go4kVCF_wrk.low] 696 | fsub dword [WRK+go4kVCF_wrk.high] 697 | %endif 698 | go4kVCF_func_processdone: 699 | 700 | %ifdef GO4K_USE_VCF_STEREO 701 | test al, byte STEREO ; // outr inl 702 | jz short go4kVCF_func_end 703 | sub al, byte STEREO 704 | sub WRK, go4kVCF_wrk.low2 705 | jmp go4kVCF_func_stereoloop 706 | %endif 707 | 708 | go4kVCF_func_end: ; // value - - - - 709 | ret 710 | 711 | %ifdef USE_SECTIONS 712 | section .g4kcodd code align=1 713 | %else 714 | section .text 715 | %endif 716 | ; //---------------------------------------------------------------------------------------- 717 | ; // DST Tick 718 | ; //---------------------------------------------------------------------------------------- 719 | ; // IN : WRK = unit workspace 720 | ; // IN : VAL = unit values 721 | ; // IN : ecx = global workspace 722 | ; // OUT : 723 | ; // DIRTY : eax 724 | ; //---------------------------------------------------------------------------------------- 725 | export_func go4kDST_func@0 726 | %ifdef GO4K_USE_DST 727 | %ifdef GO4K_USE_DST_SH 728 | %ifdef GO4K_USE_DST_STEREO 729 | push 3 730 | %else 731 | push 2 732 | %endif 733 | %else 734 | %ifdef GO4K_USE_DST_STEREO 735 | push 2 736 | %else 737 | push 1 738 | %endif 739 | %endif 740 | call go4kTransformValues 741 | %ifdef GO4K_USE_DST_CHECK 742 | ; check if current note still active 743 | mov eax, dword [ecx-4] 744 | test eax, eax 745 | jne go4kDST_func_do 746 | ret 747 | go4kDST_func_do: 748 | %endif 749 | movzx eax, byte [VAL-1] ; // get type flag 750 | %ifdef GO4K_USE_DST_SH 751 | fld dword [edx+go4kDST_val.snhfreq] ; // snh in 752 | %ifdef GO4K_USE_DST_MOD_SH 753 | fadd dword [WRK+go4kDST_wrk.sm] ; // snh' in 754 | %endif 755 | fmul st0, st0 ; // square the input so we never get negative and also have a smoother behaviour in the lower frequencies 756 | fchs 757 | fadd dword [WRK+go4kDST_wrk.snhphase]; // snh' in 758 | fst dword [WRK+go4kDST_wrk.snhphase] 759 | fldz ; // 0 snh' in 760 | fucomip st1 ; // 0 snh' in 761 | jc short go4kDST_func_hold 762 | fld1 ; // 1 snh' in 763 | faddp st1, st0 ; // 1+snh' in 764 | fstp dword [WRK+go4kDST_wrk.snhphase]; // in 765 | %endif 766 | ; // calc pregain and postgain 767 | %ifdef GO4K_USE_DST_STEREO 768 | test al, byte STEREO ; // outr inl 769 | jz short go4kDST_func_mono 770 | fxch st1 ; // inr inl 771 | fld dword [edx+go4kDST_val.drive] ; // drive inr inl 772 | %ifdef GO4K_USE_DST_MOD_DM 773 | fadd dword [WRK+go4kDST_wrk.dm] 774 | %endif 775 | call go4kWaveshaper ; // outr inl 776 | %ifdef GO4K_USE_DST_SH 777 | fst dword [WRK+go4kDST_wrk.out2] ; // outr inl 778 | %endif 779 | fxch st1 ; // inl outr 780 | go4kDST_func_mono: 781 | %endif 782 | fld dword [edx+go4kDST_val.drive] ; // drive in 783 | %ifdef GO4K_USE_DST_MOD_DM 784 | fadd dword [WRK+go4kDST_wrk.dm] 785 | %endif 786 | call go4kWaveshaper ; // out 787 | %ifdef GO4K_USE_DST_SH 788 | fst dword [WRK+go4kDST_wrk.out] ; // out' 789 | %endif 790 | ret ; // out' 791 | %ifdef GO4K_USE_DST_SH 792 | go4kDST_func_hold: 793 | fstp st0 ; // in 794 | fstp st0 795 | %ifdef GO4K_USE_DST_STEREO 796 | test al, byte STEREO ; // outr inl 797 | jz short go4kDST_func_monohold 798 | fld dword [WRK+go4kDST_wrk.out2] ; // out2 799 | go4kDST_func_monohold: 800 | %endif 801 | fld dword [WRK+go4kDST_wrk.out] ; // out 802 | ret 803 | %endif 804 | 805 | %endif 806 | 807 | %ifdef USE_SECTIONS 808 | section .g4kcodf code align=1 809 | %else 810 | section .text 811 | %endif 812 | ; //---------------------------------------------------------------------------------------- 813 | ; // DLL Tick 814 | ; //---------------------------------------------------------------------------------------- 815 | ; // IN : WRK = unit workspace 816 | ; // IN : VAL = unit values 817 | ; // IN : ecx = global workspace 818 | ; // OUT : 819 | ; // DIRTY : eax 820 | ; //---------------------------------------------------------------------------------------- 821 | export_func go4kDLL_func@0 822 | %ifdef GO4K_USE_DLL 823 | %ifdef GO4K_USE_DLL_CHORUS 824 | %ifdef GO4K_USE_DLL_DAMP 825 | push 8 826 | %else 827 | push 7 828 | %endif 829 | %else 830 | %ifdef GO4K_USE_DLL_DAMP 831 | push 6 832 | %else 833 | push 5 834 | %endif 835 | %endif 836 | call go4kTransformValues 837 | pushad 838 | movzx ebx, byte [VAL-(go4kDLL_val.size-go4kDLL_val.delay)/4] ;// delay length index 839 | %ifdef GO4K_USE_DLL_NOTE_SYNC 840 | test ebx, ebx 841 | jne go4kDLL_func_process 842 | fld1 843 | fild dword [ecx-4] ; // load note freq 844 | fmul dword [c_i12] 845 | call _Power@0 846 | fmul dword [FREQ_NORMALIZE] ; // normalize 847 | fdivp st1, st0 ; // invert to get numer of samples 848 | fistp word [_go4k_delay_times+ebx*2] ; store current comb size 849 | %endif 850 | go4kDLL_func_process: 851 | mov ecx, eax ;// ecx is delay counter 852 | %ifdef GO4K_USE_DLL_MOD 853 | mov edi, WRK ;// edi is modulation workspace 854 | %endif 855 | mov WRK, dword [_go4k_delay_buffer_ofs] ;// ebp is current delay 856 | fld st0 ;// in in 857 | %ifdef GO4K_USE_DLL_MOD_IM 858 | fld dword [edx+go4kDLL_val.dry] ;// dry in in 859 | fadd dword [edi+go4kDLL_wrk2.im] ;// dry' in in 860 | fmulp st1, st0 ;// out in 861 | %else 862 | fmul dword [edx+go4kDLL_val.dry] ;// out in 863 | %endif 864 | fxch ;// in out 865 | %ifdef GO4K_USE_DLL_MOD_PM 866 | fld dword [edx+go4kDLL_val.pregain] ;// pg in out 867 | fadd dword [edi+go4kDLL_wrk2.pm] ;// pg' in out 868 | fmul st0, st0 ;// pg'' in out 869 | fmulp st1, st0 ;// in' out 870 | %else 871 | fmul dword [edx+go4kDLL_val.pregain] ;// in' out 872 | fmul dword [edx+go4kDLL_val.pregain] ;// in' out 873 | %endif 874 | 875 | %ifdef GO4K_USE_DLL_CHORUS 876 | ;// update saw lfo for chorus/flanger 877 | fld dword [edx+go4kDLL_val.freq] ;// f in' out 878 | %ifdef GO4K_USE_DLL_MOD_SM 879 | fadd dword [edi+go4kDLL_wrk2.sm] ;// f' in' out 880 | %endif 881 | fmul st0, st0 882 | fmul st0, st0 883 | fdiv dword [DLL_DEPTH] 884 | fadd dword [WRK+go4kDLL_wrk.phase] ;// p' in' out 885 | ;// clamp phase to 0,1 (only in editor, since delay can be active quite long) 886 | %ifdef GO4K_USE_DLL_CHORUS_CLAMP 887 | fld1 ;// 1 p' in' out 888 | fadd st1, st0 ;// 1 1+p' in' out 889 | fxch ;// 1+p' 1 in' out 890 | fprem ;// p'' 1 in' out 891 | fstp st1 ;// p'' in' out 892 | %endif 893 | fst dword [WRK+go4kDLL_wrk.phase] 894 | ;// get current sine value 895 | fldpi ; // pi p in' out 896 | fadd st0 ; // 2*pi p in' out 897 | fmulp st1, st0 ; // 2*pi*p in' out 898 | fsin ; // sin in' out 899 | fld1 ; // 1 sin in' out 900 | faddp st1, st0 ; // 1+sin in' out 901 | ;// mul with depth and convert to samples 902 | fld dword [edx+go4kDLL_val.depth] ; // d 1+sin in' out 903 | %ifdef GO4K_USE_DLL_MOD_AM 904 | fadd dword [edi+go4kDLL_wrk2.am] ; // d' 1+sin in' out 905 | %endif 906 | fmul st0, st0 907 | fmul st0, st0 908 | fmul dword [DLL_DEPTH] 909 | fmulp st1, st0 910 | fistp dword [esp-4] ; // in' out 911 | %endif 912 | 913 | go4kDLL_func_loop: 914 | movzx esi, word [_go4k_delay_times+ebx*2] ; fetch comb size 915 | mov eax, dword [WRK+go4kDLL_wrk.index] ;// eax is current comb index 916 | 917 | %ifdef GO4K_USE_DLL_CHORUS 918 | ;// add lfo offset and wrap buffer 919 | add eax, dword [esp-4] 920 | cmp eax, esi 921 | jl short go4kDLL_func_buffer_nowrap1 922 | sub eax, esi 923 | go4kDLL_func_buffer_nowrap1: 924 | %endif 925 | 926 | fld dword [WRK+eax*4+go4kDLL_wrk.buffer];// cout in' out 927 | 928 | %ifdef GO4K_USE_DLL_CHORUS 929 | mov eax, dword [WRK+go4kDLL_wrk.index] 930 | %endif 931 | 932 | ;// add comb output to current output 933 | fadd st2, st0 ;// cout in' out' 934 | %ifdef GO4K_USE_DLL_DAMP 935 | fld1 ;// 1 cout in' out' 936 | fsub dword [edx+go4kDLL_val.damp] ;// 1-damp cout in' out' 937 | %ifdef GO4K_USE_DLL_MOD_DM 938 | fsub dword [edi+go4kDLL_wrk2.dm] ;// 1-damp' cout in' out' 939 | %endif 940 | fmulp st1, st0 ;// cout*d2 in' out' 941 | fld dword [edx+go4kDLL_val.damp] ;// d1 cout*d2 in' out' 942 | %ifdef GO4K_USE_DLL_MOD_DM 943 | fadd dword [edi+go4kDLL_wrk2.dm] ;// d1' cout*d2 in' out' 944 | %endif 945 | fmul dword [WRK+go4kDLL_wrk.store] ;// store*d1 cout*d2 in' out' 946 | faddp st1, st0 ;// store' in' out' 947 | fst dword [WRK+go4kDLL_wrk.store] ;// store' in' out' 948 | %endif 949 | %ifdef GO4K_USE_DLL_MOD_FM 950 | fld dword [edx+go4kDLL_val.feedback] ;// fb cout in' out' 951 | fadd dword [edi+go4kDLL_wrk2.fm] ;// fb' cout in' out' 952 | fmulp st1, st0 ;// cout*fb' in' out' 953 | %else 954 | fmul dword [edx+go4kDLL_val.feedback] ;// cout*fb in' out' 955 | %endif 956 | %ifdef GO4K_USE_DLL_DC_FILTER 957 | fadd st0, st1 ;// store in' out' 958 | fstp dword [WRK+eax*4+go4kDLL_wrk.buffer];// in' out' 959 | %else 960 | fsub st0, st1 ;// store in' out' 961 | fstp dword [WRK+eax*4+go4kDLL_wrk.buffer];// in' out' 962 | fneg 963 | %endif 964 | ;// wrap comb buffer pos 965 | inc eax 966 | cmp eax, esi 967 | jl short go4kDLL_func_buffer_nowrap2 968 | %ifdef GO4K_USE_DLL_CHORUS 969 | sub eax, esi 970 | %else 971 | xor eax, eax 972 | %endif 973 | go4kDLL_func_buffer_nowrap2: 974 | mov dword [WRK+go4kDLL_wrk.index], eax 975 | ;// increment buffer pointer to next buffer 976 | inc ebx ;// go to next delay length index 977 | add WRK, go4kDLL_wrk.size ;// go to next delay 978 | mov dword [_go4k_delay_buffer_ofs], WRK ;// store next delay offset 979 | loopne go4kDLL_func_loop 980 | fstp st0 ;// out' 981 | ;// process a dc filter to prevent heavy offsets in reverb 982 | ;// we're using the dc filter variables from the next delay line here, but doesnt hurt anyway 983 | %ifdef GO4K_USE_DLL_DC_FILTER 984 | ; y(n) = x(n) - x(n-1) + R * y(n-1) 985 | fld dword [WRK+go4kDLL_wrk.dcout] ;// dco out' 986 | fmul dword [c_dc_const] ;// dcc*dco out' 987 | fsub dword [WRK+go4kDLL_wrk.dcin] ;// dcc*dco-dci out' 988 | fxch ;// out' dcc*dco-dci 989 | fst dword [WRK+go4kDLL_wrk.dcin] ;// out' dcc*dco-dci 990 | faddp st1 ;// out' 991 | %ifdef GO4K_USE_UNDENORMALIZE 992 | fadd dword [c_0_5] ;// add and sub small offset to prevent denormalization 993 | fsub dword [c_0_5] 994 | %endif 995 | fst dword [WRK+go4kDLL_wrk.dcout] 996 | %endif 997 | popad 998 | ret 999 | %endif 1000 | 1001 | %ifdef USE_SECTIONS 1002 | section .g4kcodg code align=1 1003 | %else 1004 | section .text 1005 | %endif 1006 | ; //---------------------------------------------------------------------------------------- 1007 | ; // FOP Tick (various fp stack based operations) 1008 | ; //---------------------------------------------------------------------------------------- 1009 | ; // IN : WRK = unit workspace 1010 | ; // IN : VAL = unit values 1011 | ; // IN : ecx = global workspace 1012 | ; // OUT : 1013 | ; // DIRTY : 1014 | ; //---------------------------------------------------------------------------------------- 1015 | export_func go4kFOP_func@0 1016 | push 1 1017 | call go4kTransformValues 1018 | go4kFOP_func_pop: 1019 | dec eax 1020 | jnz go4kFOP_func_addp 1021 | fstp st0 1022 | ret 1023 | go4kFOP_func_addp: 1024 | dec eax 1025 | jnz go4kFOP_func_mulp 1026 | faddp st1, st0 1027 | ret 1028 | go4kFOP_func_mulp: 1029 | dec eax 1030 | jnz go4kFOP_func_push 1031 | fmulp st1, st0 1032 | ret 1033 | go4kFOP_func_push: 1034 | dec eax 1035 | jnz go4kFOP_func_xch 1036 | fld st0 1037 | ret 1038 | go4kFOP_func_xch: 1039 | dec eax 1040 | jnz go4kFOP_func_add 1041 | fxch 1042 | ret 1043 | go4kFOP_func_add: 1044 | dec eax 1045 | jnz go4kFOP_func_mul 1046 | fadd st1 1047 | ret 1048 | go4kFOP_func_mul: 1049 | dec eax 1050 | jnz go4kFOP_func_addp2 1051 | fmul st1 1052 | ret 1053 | go4kFOP_func_addp2: 1054 | dec eax 1055 | jnz go4kFOP_func_loadnote 1056 | faddp st2, st0 1057 | faddp st2, st0 1058 | ret 1059 | go4kFOP_func_loadnote: 1060 | dec eax 1061 | jnz go4kFOP_func_mulp2 1062 | fild dword [ecx-4] 1063 | fmul dword [c_i128] 1064 | ret 1065 | go4kFOP_func_mulp2: 1066 | fmulp st2, st0 1067 | fmulp st2, st0 1068 | ret 1069 | 1070 | %ifdef USE_SECTIONS 1071 | section .g4kcodh code align=1 1072 | %else 1073 | section .text 1074 | %endif 1075 | ; //---------------------------------------------------------------------------------------- 1076 | ; // FST Tick (stores a value somewhere in the local workspace) 1077 | ; //---------------------------------------------------------------------------------------- 1078 | ; // IN : WRK = unit workspace 1079 | ; // IN : VAL = unit values 1080 | ; // IN : ecx = global workspace 1081 | ; // OUT : 1082 | ; // DIRTY : 1083 | ; //---------------------------------------------------------------------------------------- 1084 | export_func go4kFST_func@0 1085 | push 1 1086 | call go4kTransformValues 1087 | fld dword [edx+go4kFST_val.amount] 1088 | fsub dword [c_0_5] 1089 | fadd st0 1090 | fmul st1 1091 | lodsw 1092 | and eax, 0x00003fff ; // eax is destination slot 1093 | test word [VAL-2], FST_ADD 1094 | jz go4kFST_func_set 1095 | fadd dword [ecx+eax*4] 1096 | go4kFST_func_set: 1097 | fstp dword [ecx+eax*4] 1098 | test word [VAL-2], FST_POP 1099 | jz go4kFST_func_done 1100 | fstp st0 1101 | go4kFST_func_done: 1102 | ret 1103 | 1104 | %ifdef USE_SECTIONS 1105 | section .g4kcodm code align=1 1106 | %else 1107 | section .text 1108 | %endif 1109 | ; //---------------------------------------------------------------------------------------- 1110 | ; // FLD Tick (load a value on stack, optionally add a modulation signal beforehand) 1111 | ; //---------------------------------------------------------------------------------------- 1112 | ; // IN : WRK = unit workspace 1113 | ; // IN : VAL = unit values 1114 | ; // IN : ecx = global workspace 1115 | ; // OUT : signal-signal*pan , signal*pan 1116 | ; // DIRTY : 1117 | ; //---------------------------------------------------------------------------------------- 1118 | export_func go4kFLD_func@0 ;// in main env 1119 | %ifdef GO4K_USE_FLD 1120 | push 1 1121 | call go4kTransformValues 1122 | fld dword [edx+go4kFLD_val.value] ;// value in 1123 | fsub dword [c_0_5] 1124 | fadd st0 1125 | %ifdef GO4K_USE_FLD_MOD_VM 1126 | fadd dword [WRK+go4kFLD_wrk.vm] ;// value' in 1127 | %endif 1128 | %endif 1129 | ret 1130 | 1131 | %ifdef GO4K_USE_FSTG 1132 | %ifdef USE_SECTIONS 1133 | section .g4kcodi code align=1 1134 | %else 1135 | section .text 1136 | %endif 1137 | ; //---------------------------------------------------------------------------------------- 1138 | ; // FSTG Tick (stores a value anywhere in the synth (and in each voice)) 1139 | ; //---------------------------------------------------------------------------------------- 1140 | ; // IN : WRK = unit workspace 1141 | ; // IN : VAL = unit values 1142 | ; // IN : ecx = global workspace 1143 | ; // OUT : 1144 | ; // DIRTY : 1145 | ; //---------------------------------------------------------------------------------------- 1146 | export_func go4kFSTG_func@0 1147 | push 1 1148 | call go4kTransformValues 1149 | %ifdef GO4K_USE_FSTG_CHECK 1150 | ; check if current note still active 1151 | mov eax, dword [ecx-4] 1152 | test eax, eax 1153 | jne go4kFSTG_func_do 1154 | lodsw 1155 | jmp go4kFSTG_func_testpop 1156 | go4kFSTG_func_do: 1157 | %endif 1158 | fld dword [edx+go4kFST_val.amount] 1159 | fsub dword [c_0_5] 1160 | fadd st0 1161 | fmul st1 1162 | lodsw 1163 | and eax, 0x00003fff ; // eax is destination slot 1164 | test word [VAL-2], FST_ADD 1165 | jz go4kFSTG_func_set 1166 | fadd dword [go4k_synth_wrk+eax*4] 1167 | go4kFSTG_func_set: 1168 | %if MAX_VOICES > 1 1169 | fst dword [go4k_synth_wrk+eax*4] 1170 | fstp dword [go4k_synth_wrk+eax*4+go4k_instrument.size] 1171 | %else 1172 | fstp dword [go4k_synth_wrk+eax*4] 1173 | %endif 1174 | go4kFSTG_func_testpop: 1175 | test word [VAL-2], FST_POP 1176 | jz go4kFSTG_func_done 1177 | fstp st0 1178 | go4kFSTG_func_done: 1179 | ret 1180 | %endif 1181 | 1182 | %ifdef USE_SECTIONS 1183 | section .g4kcodj code align=1 1184 | %else 1185 | section .text 1186 | %endif 1187 | ; //---------------------------------------------------------------------------------------- 1188 | ; // PAN Tick (multiplies signal with main envelope and converts to stereo) 1189 | ; //---------------------------------------------------------------------------------------- 1190 | ; // IN : WRK = unit workspace 1191 | ; // IN : VAL = unit values 1192 | ; // IN : ecx = global workspace 1193 | ; // OUT : signal-signal*pan , signal*pan 1194 | ; // DIRTY : 1195 | ; //---------------------------------------------------------------------------------------- 1196 | export_func go4kPAN_func@0 ;// in main env 1197 | %ifdef GO4K_USE_PAN 1198 | push 1 1199 | call go4kTransformValues 1200 | fld dword [edx+go4kPAN_val.panning] ;// pan in 1201 | %ifdef GO4K_USE_PAN_MOD 1202 | fadd dword [WRK+go4kPAN_wrk.pm] ;// pan in 1203 | %endif 1204 | fmul st1 ;// r in 1205 | fsub st1, st0 ;// r l 1206 | fxch ;// l r 1207 | %else 1208 | fmul dword [c_0_5] 1209 | fld st0 1210 | %endif 1211 | ret 1212 | 1213 | %ifdef USE_SECTIONS 1214 | section .g4kcodk code align=1 1215 | %else 1216 | section .text 1217 | %endif 1218 | ; //---------------------------------------------------------------------------------------- 1219 | ; // OUT Tick ( stores stereo signal pair in temp buffers of the instrument) 1220 | ; //---------------------------------------------------------------------------------------- 1221 | ; // IN : WRK = unit workspace 1222 | ; // IN : VAL = unit values 1223 | ; // IN : ecx = global workspace 1224 | ; // OUT : 1225 | ; // DIRTY : 1226 | ; //---------------------------------------------------------------------------------------- 1227 | export_func go4kOUT_func@0 ;// l r 1228 | %ifdef GO4K_USE_GLOBAL_DLL 1229 | push 2 1230 | call go4kTransformValues 1231 | pushad 1232 | lea edi, [ecx+MAX_UNITS*MAX_UNIT_SLOTS*4] 1233 | fld st1 ;// r l r 1234 | fld st1 ;// l r l r 1235 | fld dword [edx+go4kOUT_val.auxsend] ;// as l r l r 1236 | %ifdef GO4K_USE_OUT_MOD_AM 1237 | fadd dword [WRK+go4kOUT_wrk.am] ;// am l r l r 1238 | %endif 1239 | fmulp st1, st0 ;// l' r l r 1240 | fstp dword [edi] ;// r l r 1241 | scasd 1242 | fld dword [edx+go4kOUT_val.auxsend] ;// as r l r 1243 | %ifdef GO4K_USE_OUT_MOD_AM 1244 | fadd dword [WRK+go4kOUT_wrk.am] ;// am r l r 1245 | %endif 1246 | fmulp st1, st0 ;// r' l r 1247 | fstp dword [edi] ;// l r 1248 | scasd 1249 | fld dword [edx+go4kOUT_val.gain] ;// g l r 1250 | %ifdef GO4K_USE_OUT_MOD_GM 1251 | fadd dword [WRK+go4kOUT_wrk.gm] ;// gm l r 1252 | %endif 1253 | fmulp st1, st0 ;// l' r 1254 | fstp dword [edi] ;// r 1255 | scasd 1256 | fld dword [edx+go4kOUT_val.gain] ;// g r 1257 | %ifdef GO4K_USE_OUT_MOD_GM 1258 | fadd dword [WRK+go4kOUT_wrk.gm] ;// gm r 1259 | %endif 1260 | fmulp st1, st0 ;// r' 1261 | fstp dword [edi] ;// - 1262 | scasd 1263 | popad 1264 | %else 1265 | push 1 1266 | call go4kTransformValues 1267 | 1268 | fld dword [edx+go4kOUT_val.gain] ;// g l r 1269 | %ifdef GO4K_USE_OUT_MOD_GM 1270 | fadd dword [WRK+go4kOUT_wrk.gm] ;// gm l r 1271 | %endif 1272 | fmulp st1, st0 ;// l' r 1273 | fstp dword [ecx+MAX_UNITS*MAX_UNIT_SLOTS*4+0] ;// r 1274 | fld dword [edx+go4kOUT_val.gain] ;// g r 1275 | %ifdef GO4K_USE_OUT_MOD_GM 1276 | fadd dword [WRK+go4kOUT_wrk.gm] ;// gm r 1277 | %endif 1278 | fmulp st1, st0 ;// r' 1279 | fstp dword [ecx+MAX_UNITS*MAX_UNIT_SLOTS*4+4] ;// - 1280 | 1281 | %endif 1282 | ret 1283 | 1284 | %ifdef USE_SECTIONS 1285 | section .g4kcodl code align=1 1286 | %else 1287 | section .text 1288 | %endif 1289 | ; //---------------------------------------------------------------------------------------- 1290 | ; // ACC Tick (stereo signal accumulation for synth commands only -> dont use in instrument commands) 1291 | ; //---------------------------------------------------------------------------------------- 1292 | ; // IN : WRK = unit workspace 1293 | ; // IN : VAL = unit values 1294 | ; // IN : ecx = global workspace 1295 | ; // OUT : 1296 | ; // DIRTY : eax 1297 | ; //---------------------------------------------------------------------------------------- 1298 | export_func go4kACC_func@0 1299 | push 1 1300 | call go4kTransformValues 1301 | pushad 1302 | mov edi, go4k_synth_wrk 1303 | add edi, go4k_instrument.size 1304 | sub edi, eax ; // eax already contains the accumulation offset from the go4kTransformValues call 1305 | mov cl, MAX_INSTRUMENTS*MAX_VOICES 1306 | fldz ;// 0 1307 | fldz ;// 0 0 1308 | go4kACC_func_loop: 1309 | fadd dword [edi-8] ;// l 0 1310 | fxch ;// 0 l 1311 | fadd dword [edi-4] ;// r l 1312 | fxch ;// l r 1313 | add edi, go4k_instrument.size 1314 | dec cl 1315 | jnz go4kACC_func_loop 1316 | popad 1317 | ret 1318 | 1319 | %ifdef USE_SECTIONS 1320 | section .g4kcodw code align=1 1321 | %else 1322 | section .text 1323 | %endif 1324 | ; //---------------------------------------------------------------------------------------- 1325 | ; // Update Instrument (allocate voices, set voice to release) 1326 | ; //---------------------------------------------------------------------------------------- 1327 | ; // IN : 1328 | ; // IN : 1329 | ; // OUT : 1330 | ; // DIRTY : 1331 | ; //---------------------------------------------------------------------------------------- 1332 | go4kUpdateInstrument: 1333 | ; // get new note 1334 | mov eax, dword [esp+4+4] ; // eax = current tick 1335 | shr eax, PATTERN_SIZE_SHIFT ; // eax = current pattern 1336 | imul edx, ecx, dword MAX_PATTERNS ; // edx = instrument pattern list index 1337 | movzx edx, byte [edx+eax+go4k_pattern_lists] ; // edx = pattern index 1338 | mov eax, dword [esp+4+4] ; // eax = current tick 1339 | shl edx, PATTERN_SIZE_SHIFT 1340 | and eax, PATTERN_SIZE-1 1341 | movzx edx, byte [edx+eax+go4k_patterns] ; // edx = requested note in new patterntick 1342 | ; // apply note changes 1343 | cmp dl, HLD ; // anything but hold causes action 1344 | je short go4kUpdateInstrument_done 1345 | inc dword [edi] ; // set release flag if needed 1346 | %if MAX_VOICES > 1 1347 | inc dword [edi+go4k_instrument.size] ; // set release flag if needed 1348 | %endif 1349 | cmp dl, HLD ; // check for new note 1350 | jl short go4kUpdateInstrument_done 1351 | %if MAX_VOICES > 1 1352 | pushad 1353 | xchg eax, dword [go4k_voiceindex + ecx*4] 1354 | test eax, eax 1355 | je go4kUpdateInstrument_newNote 1356 | add edi, go4k_instrument.size 1357 | go4kUpdateInstrument_newNote: 1358 | xor al,1 1359 | xchg dword [go4k_voiceindex + ecx*4], eax 1360 | %endif 1361 | pushad 1362 | xor eax, eax 1363 | mov ecx, (8+MAX_UNITS*MAX_UNIT_SLOTS*4)/4 ; // clear only relase, note and workspace 1364 | rep stosd 1365 | popad 1366 | mov dword [edi+4], edx ; // set requested note as current note 1367 | %if MAX_VOICES > 1 1368 | popad 1369 | %endif 1370 | jmp short go4kUpdateInstrument_done 1371 | go4kUpdateInstrument_done: 1372 | ret 1373 | 1374 | %ifdef USE_SECTIONS 1375 | section .g4kcodx code align=1 1376 | %else 1377 | section .text 1378 | %endif 1379 | ; //---------------------------------------------------------------------------------------- 1380 | ; // Render Voices 1381 | ; //---------------------------------------------------------------------------------------- 1382 | ; // IN : 1383 | ; // IN : 1384 | ; // OUT : 1385 | ; // DIRTY : 1386 | ; //---------------------------------------------------------------------------------------- 1387 | go4kRenderVoices: 1388 | push ecx ; // save current instrument counter 1389 | %if MAX_VOICES > 1 1390 | push COM ; // save current instrument command index 1391 | push VAL ; // save current instrument values index 1392 | %endif 1393 | call go4k_VM_process ; // call synth vm for instrument voices 1394 | mov eax, dword [ecx+go4kENV_wrk.state] 1395 | cmp al, byte ENV_STATE_OFF 1396 | jne go4kRenderVoices_next 1397 | xor eax, eax 1398 | mov dword [ecx-4], eax ; // kill note if voice is done 1399 | go4kRenderVoices_next: 1400 | %if MAX_VOICES > 1 1401 | pop VAL ; // restore instrument value index 1402 | pop COM ; // restore instrument command index 1403 | %endif 1404 | 1405 | %ifdef GO4K_USE_BUFFER_RECORDINGS 1406 | mov eax, dword [esp+16] ; // get current tick 1407 | shr eax, 8 ; // every 256th sample = ~ 172 hz 1408 | shl eax, 5 ; // for 16 instruments a 2 voices 1409 | add eax, dword [esp] 1410 | add eax, dword [esp] ; // + 2*currentinstrument+0 1411 | %ifdef GO4K_USE_ENVELOPE_RECORDINGS 1412 | mov edx, dword [ecx+go4kENV_wrk.level] 1413 | mov dword [__4klang_envelope_buffer+eax*4], edx 1414 | %endif 1415 | %ifdef GO4K_USE_NOTE_RECORDINGS 1416 | mov edx, dword [ecx-4] 1417 | mov dword [__4klang_note_buffer+eax*4], edx 1418 | %endif 1419 | %endif 1420 | 1421 | %if MAX_VOICES > 1 1422 | call go4k_VM_process ; // call synth vm for instrument voices 1423 | mov eax, dword [ecx+go4kENV_wrk.state] 1424 | cmp al, byte ENV_STATE_OFF 1425 | jne go4k_render_instrument_next2 1426 | xor eax, eax 1427 | mov dword [ecx-4], eax ; // kill note if voice is done 1428 | go4k_render_instrument_next2: 1429 | 1430 | %ifdef GO4K_USE_BUFFER_RECORDINGS 1431 | mov eax, dword [esp+16] ; // get current tick 1432 | shr eax, 8 ; // every 256th sample = ~ 172 hz 1433 | shl eax, 5 ; // for 16 instruments a 2 voices 1434 | add eax, dword [esp] 1435 | add eax, dword [esp] ; // + 2*currentinstrument+0 1436 | %ifdef GO4K_USE_ENVELOPE_RECORDINGS 1437 | mov edx, dword [ecx+go4kENV_wrk.level] 1438 | mov dword [__4klang_envelope_buffer+eax*4+4], edx 1439 | %endif 1440 | %ifdef GO4K_USE_NOTE_RECORDINGS 1441 | mov edx, dword [ecx-4] 1442 | mov dword [__4klang_note_buffer+eax*4+4], edx 1443 | %endif 1444 | %endif 1445 | 1446 | %endif 1447 | pop ecx ; // restore instrument counter 1448 | ret 1449 | 1450 | %ifdef USE_SECTIONS 1451 | section .g4kcody code align=1 1452 | %else 1453 | section .text 1454 | %endif 1455 | ; //---------------------------------------------------------------------------------------- 1456 | ; // the entry point for the synth 1457 | ; //---------------------------------------------------------------------------------------- 1458 | %ifdef USE_SECTIONS 1459 | export_func _4klang_render@4 1460 | %else 1461 | export_func _4klang_render 1462 | %endif 1463 | pushad 1464 | xor ecx, ecx 1465 | %ifdef GO4K_USE_BUFFER_RECORDINGS 1466 | push ecx 1467 | %endif 1468 | ; loop all ticks 1469 | go4k_render_tickloop: 1470 | push ecx 1471 | xor ecx, ecx 1472 | ; loop all samples per tick 1473 | go4k_render_sampleloop: 1474 | push ecx 1475 | xor ecx, ecx 1476 | mov ebx, go4k_synth_instructions ; // ebx = instrument command index 1477 | mov VAL, go4k_synth_parameter_values; // VAL = instrument values index 1478 | mov edi, _go4k_delay_buffer ; // get offset of first delay buffer 1479 | mov dword [_go4k_delay_buffer_ofs], edi ; // store offset in delaybuffer offset variable 1480 | mov edi, go4k_synth_wrk ; // edi = first instrument 1481 | ; loop all instruments 1482 | go4k_render_instrumentloop: 1483 | mov eax, dword [esp] ; // eax = current tick sample 1484 | and eax, eax 1485 | jnz go4k_render_instrument_process ; // tick change? (first sample in current tick) 1486 | call go4kUpdateInstrument ; // update instrument state 1487 | ; process instrument 1488 | go4k_render_instrument_process: 1489 | call go4kRenderVoices 1490 | inc ecx 1491 | cmp cl, byte MAX_INSTRUMENTS 1492 | jl go4k_render_instrumentloop 1493 | mov dword [edi+4], ecx ; // move a value != 0 into note slot, so processing will be done 1494 | call go4k_VM_process ; // call synth vm for synth 1495 | go4k_render_output_sample: 1496 | %ifdef GO4K_USE_BUFFER_RECORDINGS 1497 | inc dword [esp+8] 1498 | xchg esi, dword [esp+48] ; // edx = destbuffer 1499 | %else 1500 | xchg esi, dword [esp+44] ; // edx = destbuffer 1501 | %endif 1502 | %ifdef GO4K_CLIP_OUTPUT 1503 | fld dword [edi-8] 1504 | fld1 ; // 1 val 1505 | fucomi st1 ; // 1 val 1506 | jbe short go4k_render_clip1 1507 | fchs ; // -1 val 1508 | fucomi st1 ; // -1 val 1509 | fcmovb st0, st1 ; // val -1 (if val > -1) 1510 | go4k_render_clip1: 1511 | fstp st1 ; // newval 1512 | %ifdef GO4K_USE_16BIT_OUTPUT 1513 | push eax 1514 | fmul dword [c_32767] 1515 | fistp dword [esp] 1516 | pop eax 1517 | mov word [esi],ax ; // store integer converted left sample 1518 | lodsw 1519 | %else 1520 | fstp dword [esi] ; // store left sample 1521 | lodsd 1522 | %endif 1523 | fld dword [edi-4] 1524 | fld1 ; // 1 val 1525 | fucomi st1 ; // 1 val 1526 | jbe short go4k_render_clip2 1527 | fchs ; // -1 val 1528 | fucomi st1 ; // -1 val 1529 | fcmovb st0, st1 ; // val -1 (if val > -1) 1530 | go4k_render_clip2: 1531 | fstp st1 ; // newval 1532 | %ifdef GO4K_USE_16BIT_OUTPUT 1533 | push eax 1534 | fmul dword [c_32767] 1535 | fistp dword [esp] 1536 | pop eax 1537 | mov word [esi],ax ; // store integer converted right sample 1538 | lodsw 1539 | %else 1540 | fstp dword [esi] ; // store right sample 1541 | lodsd 1542 | %endif 1543 | %else 1544 | fld dword [edi-8] 1545 | %ifdef GO4K_USE_16BIT_OUTPUT 1546 | push eax 1547 | fmul dword [c_32767] 1548 | fistp dword [esp] 1549 | pop eax 1550 | mov word [esi],ax ; // store integer converted left sample 1551 | lodsw 1552 | %else 1553 | fstp dword [esi] ; // store left sample 1554 | lodsd 1555 | %endif 1556 | fld dword [edi-4] 1557 | %ifdef GO4K_USE_16BIT_OUTPUT 1558 | push eax 1559 | fmul dword [c_32767] 1560 | fistp dword [esp] 1561 | pop eax 1562 | mov word [esi],ax ; // store integer converted right sample 1563 | lodsw 1564 | %else 1565 | fstp dword [esi] ; // store right sample 1566 | lodsd 1567 | %endif 1568 | %endif 1569 | %ifdef GO4K_USE_BUFFER_RECORDINGS 1570 | xchg esi, dword [esp+48] 1571 | %else 1572 | xchg esi, dword [esp+44] 1573 | %endif 1574 | pop ecx 1575 | inc ecx 1576 | %ifdef GO4K_USE_GROOVE_PATTERN 1577 | mov ebx, dword SAMPLES_PER_TICK 1578 | mov eax, dword [esp] 1579 | and eax, 0x0f 1580 | bt dword [go4k_groove_pattern],eax 1581 | jnc go4k_render_nogroove 1582 | sub ebx, dword 3000 1583 | go4k_render_nogroove: 1584 | cmp ecx, ebx 1585 | %else 1586 | cmp ecx, dword SAMPLES_PER_TICK 1587 | %endif 1588 | jl go4k_render_sampleloop 1589 | pop ecx 1590 | inc ecx 1591 | %ifdef AUTHORING 1592 | mov dword[__4klang_current_tick], ecx 1593 | %endif 1594 | cmp ecx, dword MAX_TICKS 1595 | jl go4k_render_tickloop 1596 | %ifdef GO4K_USE_BUFFER_RECORDINGS 1597 | pop ecx 1598 | %endif 1599 | popad 1600 | ret 4 1601 | 1602 | %ifdef USE_SECTIONS 1603 | section .g4kcodz code align=1 1604 | %else 1605 | section .text 1606 | %endif 1607 | ; //---------------------------------------------------------------------------------------- 1608 | ; // the magic behind it :) 1609 | ; //---------------------------------------------------------------------------------------- 1610 | ; // IN : edi = instrument pointer 1611 | ; // IN : esi = instrumet values 1612 | ; // IN : ebx = instrument instructions 1613 | ; // OUT : 1614 | ; // DIRTY : 1615 | ; //---------------------------------------------------------------------------------------- 1616 | go4k_VM_process: 1617 | lea WRK, [edi+8] ; // get current workspace pointer 1618 | mov ecx, WRK ; // ecx = workspace start 1619 | go4k_VM_process_loop: 1620 | movzx eax, byte [ebx] ; // get command byte 1621 | inc ebx 1622 | test eax, eax 1623 | je go4k_VM_process_done ; // command byte = 0? so done 1624 | call dword [eax*4+go4k_synth_commands] 1625 | add WRK, MAX_UNIT_SLOTS*4 ; // go to next workspace slot 1626 | jmp short go4k_VM_process_loop 1627 | go4k_VM_process_done: 1628 | add edi, go4k_instrument.size ; // go to next instrument voice 1629 | ret --------------------------------------------------------------------------------