diff --git a/4kdemo.compofiller b/4kdemo.compofiller index baa5bd0..a1db361 100644 --- a/4kdemo.compofiller +++ b/4kdemo.compofiller @@ -6,7 +6,7 @@ PROJECT_INFO_FILE=prod.nfo PROJECT_SCREENSHOT= # Currently active config file name -ACTIVE_CONFIG=minified.config +ACTIVE_CONFIG=base.config # ------- GUI options/preferences -------- # Automatically rebuild DLLs when things have changed? diff --git a/4klang.asm b/4klang.asm index abf9cc1..ff1c707 100644 --- a/4klang.asm +++ b/4klang.asm @@ -1,1758 +1,994 @@ -bits 32 +%define SU_LENGTH_IN_SAMPLES 3335488 +%define SU_SAMPLE_RATE 44100 +%define SU_BPM 165. -%define WRK ebp ; // alias for unit workspace -%define VAL esi ; // alias for unit values (transformed/untransformed) -%define COM ebx ; // alias for instrument opcodes +;------------------------------------------------------------------------------- +; unit struct +;------------------------------------------------------------------------------- +struc su_unit + .state resd 8 + .ports resd 8 + .size: +endstruc -%include "4klang.inc" +;------------------------------------------------------------------------------- +; voice struct +;------------------------------------------------------------------------------- +struc su_voice + .note resd 1 + .sustain resd 1 + .inputs resd 8 + .reserved resd 6 ; this is done to so the whole voice is 2^n long, see polyphonic player + .workspace resb 63 * su_unit.size + .size: +endstruc -;// conditional defines +;------------------------------------------------------------------------------- +; synthworkspace struct +;------------------------------------------------------------------------------- +struc su_synthworkspace + .curvoices resb 32 ; these are used by the multitrack player to store which voice is playing on which track + .left resd 1 + .right resd 1 + .aux resd 6 ; 3 auxiliary signals + .voices resb 32 * su_voice.size + .size: +endstruc -%ifdef GO4K_USE_VCO_SHAPE - %define INCLUDE_WAVESHAPER -%endif -%ifdef GO4K_USE_DST - %define INCLUDE_WAVESHAPER -%endif +;------------------------------------------------------------------------------- +; su_delayline_wrk struct +;------------------------------------------------------------------------------- +struc su_delayline_wrk + .dcin resd 1 + .dcout resd 1 + .filtstate resd 1 + .buffer resd 65536 + .size: +endstruc -%ifdef GO4K_USE_VCO_MOD_PM - %define PHASE_RENORMALIZE -%endif -%ifdef GO4K_USE_VCO_PHASE_OFFSET - %define PHASE_RENORMALIZE -%endif - -%ifdef GO4K_USE_ENVELOPE_RECORDINGS - %define GO4K_USE_BUFFER_RECORDINGS -%endif -%ifdef GO4K_USE_NOTE_RECORDINGS - %define GO4K_USE_BUFFER_RECORDINGS -%endif - -; //======================================================================================== -; // .bss section -; //======================================================================================== -%ifdef USE_SECTIONS -section .g4kbss1 bss align=1 -%else -section .bss -%endif - -; // the one and only synth object -%if MAX_VOICES > 1 -go4k_voiceindex resd 16 -%endif -go4k_transformed_values resd 16 -go4k_synth_wrk resb go4k_synth.size -global _go4k_delay_buffer_ofs -_go4k_delay_buffer_ofs resd 1 -global _go4k_delay_buffer -_go4k_delay_buffer resd 16*16*go4kDLL_wrk.size - -%ifdef AUTHORING -global __4klang_current_tick -__4klang_current_tick resd 0 -%endif - -%ifdef GO4K_USE_ENVELOPE_RECORDINGS -global __4klang_envelope_buffer -__4klang_envelope_buffer resd ((MAX_SAMPLES)/8) ; // samples every 256 samples and stores 16*2 = 32 values -%endif -%ifdef GO4K_USE_NOTE_RECORDINGS -global __4klang_note_buffer -__4klang_note_buffer resd ((MAX_SAMPLES)/8) ; // samples every 256 samples and stores 16*2 = 32 values -%endif - -; //======================================================================================== -; // .g4kdat section (initialized data for go4k) -; //======================================================================================== -%ifdef USE_SECTIONS -section .g4kdat1 data align=1 -%else -section .data -%endif - -; // some synth constants -go4k_synth_commands dd 0 - dd _go4kENV_func@0 - dd _go4kVCO_func@0 - dd _go4kVCF_func@0 - dd _go4kDST_func@0 - dd _go4kDLL_func@0 - dd _go4kFOP_func@0 - dd _go4kFST_func@0 - dd _go4kPAN_func@0 - dd _go4kOUT_func@0 - dd _go4kACC_func@0 - dd _go4kFLD_func@0 -%ifdef GO4K_USE_GLITCH - dd _go4kGLITCH_func@0 -%else - dd _go4kFLD_func@0 -%endif -%ifdef GO4K_USE_FSTG - dd _go4kFSTG_func@0 -%endif - -%ifdef USE_SECTIONS -section .g4kdat2 data align=1 -%else -section .data -%endif - -%ifdef GO4K_USE_16BIT_OUTPUT -c_32767 dd 32767.0 -%endif -c_i128 dd 0.0078125 -c_RandDiv dd 65536*32768 -c_0_5 dd 0.5 -%ifdef GO4K_USE_VCO_GATE -c_16 dd 16.0 -%endif -%ifdef GO4K_USE_DLL_CHORUS -DLL_DEPTH dd 1024.0 -%endif -%ifdef GO4K_USE_DLL_DC_FILTER -c_dc_const dd 0.99609375 ; R = 1 - (pi*2 * frequency /samplerate) -%else - %ifdef GO4K_USE_VCO_GATE -c_dc_const dd 0.99609375 ; R = 1 - (pi*2 * frequency /samplerate) - %endif -%endif -global _RandSeed -_RandSeed dd 1 -c_24 dd 24 -c_i12 dd 0x3DAAAAAA -FREQ_NORMALIZE dd 0.000092696138 ; // 220.0/(2^(69/12)) / 44100.0 -global _LFO_NORMALIZE -_LFO_NORMALIZE dd DEF_LFO_NORMALIZE -%ifdef GO4K_USE_GROOVE_PATTERN -go4k_groove_pattern dw 0011100111001110b -%endif - -; //======================================================================================== -; // .crtemui section (emulates crt functions) -; //======================================================================================== -%ifdef USE_SECTIONS -section .crtemui code align=1 -%else -section .text -%endif - -export_func FloatRandomNumber@0 - push eax - imul eax,dword [_RandSeed],16007 - mov dword [_RandSeed], eax - fild dword [_RandSeed] - fidiv dword [c_RandDiv] - pop eax - ret - -; //======================================================================================== -; // .g4kcod* sections (code for go4k) -; //======================================================================================== - -%ifdef INCLUDE_WAVESHAPER - -%ifdef USE_SECTIONS -section .g4kcod2 code align=1 -%else -section .text -%endif -; //---------------------------------------------------------------------------------------- -; // Waveshaper function -; //---------------------------------------------------------------------------------------- -; // Input : st0 : shaping coeff -; // st1 : input -; // Output: st0 : result -; //---------------------------------------------------------------------------------------- - -go4kWaveshaper: -%ifdef GO4K_USE_WAVESHAPER_CLIP - fxch - fld1 ; // 1 val - fucomi st1 ; // 1 val - jbe short go4kWaveshaper_clip - fchs ; // -1 val - fucomi st1 ; // -1 val - fcmovb st0, st1 ; // val -1 (if val > -1) -go4kWaveshaper_clip: - fstp st1 ; // newval - fxch -%endif - fsub dword [c_0_5] - fadd st0 - fst dword [esp-4] ; // amnt in - fadd st0 ; // 2*amnt in - fld1 ; // 1 2*amnt in - fsub dword [esp-4] ; // 1-amnt 2*amnt in - fdivp st1, st0 ; // k in - fld st1 ; // sin k in - fabs ; // a(in) k in - fmul st1 ; // k*a(in) k in - fld1 - faddp st1, st0 ; // 1+k*a(in) k in - fxch st1 ; // k 1+k*a(in) in - fld1 - faddp st1, st0 ; // 1+k 1+k*a(in) in - fmulp st2 ; // 1+k*a(in) (1+k)*in - fdivp st1, st0 ; // out - ret - -%endif - -%ifdef USE_SECTIONS -section .g4kcod3 code align=1 -%else -section .text -%endif -; //---------------------------------------------------------------------------------------- -; // unit values preparation/transform -; //---------------------------------------------------------------------------------------- -go4kTransformValues: - push ecx - xor ecx, ecx - xor eax, eax - mov edx, go4k_transformed_values -go4kTransformValues_loop: - lodsb - push eax - fild dword [esp] - fmul dword [c_i128] - fstp dword [edx+ecx*4] - pop eax - inc ecx - cmp ecx, dword [esp+8] - jl go4kTransformValues_loop - pop ecx - ret 4 - -%ifdef USE_SECTIONS -section .g4kcod4 code align=1 -%else -section .text -%endif -; //---------------------------------------------------------------------------------------- -; // Envelope param mapping -; //---------------------------------------------------------------------------------------- -go4kENVMap: - fld dword [edx+eax*4] -%ifdef GO4K_USE_ENV_MOD_ADR - fadd dword [WRK+go4kENV_wrk.am+eax*4] -%endif - fimul dword [c_24] - fchs -; //---------------------------------------------------------------------------------------- -; // Power function (2^x) -; //---------------------------------------------------------------------------------------- -; // Input : st0 : base -; // st1 : exponent -; // Output: st0 : result -; //---------------------------------------------------------------------------------------- -export_func Power@0 ; // base exp - fld1 - fadd st0 - fyl2x ; // log2_base - fld1 ; // 1 log2_base - fld st1 ; // log2_base 1 log2_base - fprem ; // (frac)log2_base 1 log2_base - f2xm1 ; // 2 ^ '' - 1 1 log2_base - faddp st1, st0 ; // 2 ^ '' (int)log2_base - fscale - fstp st1 - ret - -%ifdef USE_SECTIONS -section .g4kcoda code align=1 -%else -section .text -%endif -; //---------------------------------------------------------------------------------------- -; // ENV Tick -; //---------------------------------------------------------------------------------------- -; // IN : WRK = unit workspace -; // IN : VAL = unit values -; // IN : ecx = global workspace -; // OUT : -; // DIRTY : eax -; //---------------------------------------------------------------------------------------- -export_func go4kENV_func@0 - push 5 - call go4kTransformValues -%ifdef GO4K_USE_ENV_CHECK -; check if current note still active - mov eax, dword [ecx-4] - test eax, eax - jne go4kENV_func_do - fldz - ret -%endif -go4kENV_func_do: - mov eax, dword [ecx-8] ; // is the instrument in release mode (note off)? - test eax, eax - je go4kENV_func_process - mov dword [WRK+go4kENV_wrk.state], ENV_STATE_RELEASE -go4kENV_func_process: - mov eax, dword [WRK+go4kENV_wrk.state] - fld dword [WRK+go4kENV_wrk.level] ; // val - -; // check for sustain state - cmp al, ENV_STATE_SUSTAIN - je short go4kENV_func_leave2 -go4kENV_func_attac: - cmp al, ENV_STATE_ATTAC - jne short go4kENV_func_decay - call go4kENVMap ; // newval - faddp st1, st0 -; // check for end of attac - fld1 ; // 1 newval - fucomi st1 ; // 1 newval - fcmovnb st0, st1 ; // newval 1 (if newval < 1) - jbe short go4kENV_func_statechange -go4kENV_func_decay: - cmp al, ENV_STATE_DECAY - jne short go4kENV_func_release - call go4kENVMap ; // newval - fsubp st1, st0 -; // check for end of decay - fld dword [edx+go4kENV_val.sustain] ; // sustain newval - fucomi st1 ; // sustain newval - fcmovb st0, st1 ; // newval sustain (if newval > sustain) - jnc short go4kENV_func_statechange -go4kENV_func_release: -; // release state? - cmp al, ENV_STATE_RELEASE - jne short go4kENV_func_leave - call go4kENVMap ; // newval - fsubp st1, st0 -; // check for end of release - fldz ; // 0 newval - fucomi st1 ; // 0 newval - fcmovb st0, st1 ; // newval 0 (if newval > 0) - jc short go4kENV_func_leave -go4kENV_func_statechange: ; // newval - inc dword [WRK+go4kENV_wrk.state] -go4kENV_func_leave: ; // newval bla - fstp st1 -; // store new env value - fst dword [WRK+go4kENV_wrk.level] -go4kENV_func_leave2: -; // mul by gain -%ifdef GO4K_USE_ENV_MOD_GM - fld dword [edx+go4kENV_val.gain] - fadd dword [WRK+go4kENV_wrk.gm] - fmulp st1, st0 -%else - fmul dword [edx+go4kENV_val.gain] -%endif - ret +;------------------------------------------------------------------------------- +; su_sample_offset struct +;------------------------------------------------------------------------------- +struc su_sample_offset ; length conveniently 8 bytes, so easy to index + .start resd 1 + .loopstart resw 1 + .looplength resw 1 + .size: +endstruc +;------------------------------------------------------------------------------- +; Uninitialized data: The synth object +;------------------------------------------------------------------------------- +section .synth_object bss align=256 +su_synth_obj: + resb su_synthworkspace.size + resb 34*su_delayline_wrk.size -; //---------------------------------------------------------------------------------------- -; // VCO Tick -; //---------------------------------------------------------------------------------------- -; // IN : WRK = unit workspace -; // IN : VAL = unit values -; // IN : ecx = global workspace -; // OUT : -; // DIRTY : eax -; //---------------------------------------------------------------------------------------- -%ifdef USE_SECTIONS -section .g4kcodp code align=1 -%else -section .text -%endif -go4kVCO_pulse: - fucomi st1 ; // c p - fld1 - jnc short go4kVCO_func_pulse_up ; // +1 c p - fchs ; // -1 c p -go4kVCO_func_pulse_up: - fstp st1 ; // +-1 p - fstp st1 ; // +-1 - ret +;------------------------------------------------------------------------------- +; su_render_song function: the entry point for the synth +;------------------------------------------------------------------------------- +; Has the signature su_render_song(void *ptr), where ptr is a pointer to +; the output buffer. Renders the compile time hard-coded song to the buffer. +; Stack: output_ptr +;------------------------------------------------------------------------------- +section .su_render_song code align=1 +global _su_render_song@4 +_su_render_song@4: + pushad ; Stack: edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr + xor eax, eax + push 1 ; Stack: RandSeed, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr + push eax ; Stack: GlobalTick, RandSeed, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr +su_render_rowloop: ; loop through every row in the song + push eax ; Stack: Row, GlobalTick, RandSeed, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr + call su_update_voices ; update instruments for the new row + xor eax, eax ; ecx is the current sample within row +su_render_sampleloop: ; loop through every sample in the row + push eax ; Stack: Sample, Row, GlobalTick, RandSeed, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr + push 7 ; Stack: VoicesRemain, Sample, Row, GlobalTick, RandSeed, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr + mov edx, dword su_synth_obj ; edx points to the synth object + mov ebx, dword su_patch_opcodes ; COM points to vm code + mov esi, dword su_patch_operands ; VAL points to unit params + mov ecx, dword su_synth_obj + su_synthworkspace.size - su_delayline_wrk.filtstate + lea ebp, [edx + su_synthworkspace.voices] ; WRK points to the first voice + call su_run_vm ; run through the VM code + pop eax ; eax = VoicesRemain, Stack: Sample, Row, GlobalTick, RandSeed, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr + mov esi, [esp + 52] ; esi points to the output buffer + mov edi, dword su_synth_obj+su_synthworkspace.left + mov ecx, 2 + output_sound16bit_loop: ; loop over two channels, left & right + fld dword [edi] + call su_clip + fmul dword [FCONST_32767_0] + push eax + fistp dword [esp] + pop eax + mov word [esi],ax ; // store integer converted right sample + xor eax,eax + stosd + add esi,2 + loop output_sound16bit_loop + mov [esp + 52], esi ; save esi back to stack ; *ptr++ = left, *ptr++ = right + pop eax ; eax = Sample, Stack: Row, GlobalTick, RandSeed, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr + inc dword [esp + 4] ; increment global time, used by delays + inc eax + cmp eax, 4009 + jl su_render_sampleloop + pop eax ; eax = Row, Stack: GlobalTick, RandSeed, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr ; Stack: pushad ptr + inc eax + cmp eax, 832 + jl su_render_rowloop + ; rewind the stack the entropy of multiple pop eax is probably lower than add + pop eax ; eax = GlobalTick, Stack: RandSeed, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr + pop eax ; eax = RandSeed, Stack: edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr + popad ; Popped: eax, ecx, edx, ebx, esp, ebp, esi, edi. Stack: retaddr_su_render_song, OutputBufPtr + ret 4 -%ifdef USE_SECTIONS -section .g4kcodt code align=1 -%else -section .text -%endif -go4kVCO_trisaw: - fucomi st1 ; // c p - jnc short go4kVCO_func_trisaw_up - fld1 ; // 1 c p - fsubr st2, st0 ; // 1 c 1-p - fsubrp st1, st0 ; // 1-c 1-p -go4kVCO_func_trisaw_up: - fdivp st1, st0 ; // tp'/tc - fadd st0 ; // 2*'' - fld1 ; // 1 2*'' - fsubp st1, st0 ; // 2*''-1 - ret +;------------------------------------------------------------------------------- +; su_update_voices function: polyphonic & chord implementation +;------------------------------------------------------------------------------- +; Input: eax : current row within song +; Dirty: pretty much everything +;------------------------------------------------------------------------------- +section .su_update_voices code align=1 +su_update_voices: +; The simple implementation: each track triggers always the same voice + xor edx, edx + xor ebx, ebx + mov bl, 16 ; rows per pattern + div ebx ; eax = current pattern, edx = current row in pattern + lea esi, [su_tracks+eax]; esi points to the pattern data for current track + mov edi, dword su_synth_obj+su_synthworkspace.voices + mov bl, 6 ; MAX_TRACKS is always <= 32 so this is ok +su_update_voices_trackloop: + movzx eax, byte [esi] ; eax = current pattern + imul eax, 16 ; multiply by rows per pattern, eax = offset to current pattern data + movzx eax, byte [su_patterns + eax + edx] ; ecx = note + cmp al, 1 ; anything but hold causes action + je short su_update_voices_nexttrack + mov dword [edi+su_voice.sustain], eax ; set the voice currently active to release + jb su_update_voices_nexttrack ; if cl < HLD (no new note triggered) goto nexttrack +su_update_voices_retrigger: + stosd ; save note + stosd ; save sustain + mov ecx, (su_voice.size - su_voice.inputs)/4 ; could be xor ecx, ecx; mov ch,...>>8, but will it actually be smaller after compression? + xor eax, eax + rep stosd ; clear the workspace of the new voice, retriggering oscillators + jmp short su_update_voices_skipadd +su_update_voices_nexttrack: + add edi, su_voice.size +su_update_voices_skipadd: + add esi, 52 + dec ebx + jnz short su_update_voices_trackloop + ret -%ifdef USE_SECTIONS -section .g4kcods code align=1 -%else -section .text -%endif -go4kVCO_sine: - fucomi st1 ; // c p - jnc short go4kVCO_func_sine_do - fstp st1 - fsub st0, st0 ; // 0 - ret -go4kVCO_func_sine_do - fdivp st1, st0 ; // p/c - fldpi ; // pi p - fadd st0 ; // 2*pi p - fmulp st1, st0 ; // 2*pi*p - fsin ; // sin(2*pi*p) - ret +;------------------------------------------------------------------------------- +; su_run_vm function: runs the entire virtual machine once, creating 1 sample +;------------------------------------------------------------------------------- +; Input: su_synth_obj.left : Set to 0 before calling +; su_synth_obj.right : Set to 0 before calling +; _CX : Pointer to delay workspace (if needed) +; _DX : Pointer to synth object +; COM : Pointer to opcode stream +; VAL : Pointer to operand stream +; WRK : Pointer to the last workspace processed +; Output: su_synth_obj.left : left sample +; su_synth_obj.right : right sample +; Dirty: everything +;------------------------------------------------------------------------------- +section .su_run_vm code align=1 +su_run_vm: + pushad ; Stack: edi, OperandStream, Voice, esp, OpcodeStream, Synth, DelayWorkSpace, eax, retaddr_su_run_vm, VoicesRemain, Sample, Row, GlobalTick, RandSeed, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr +su_run_vm_loop: ; loop until all voices done + movzx edi, byte [ebx] ; edi = command byte + inc ebx ; move to next instruction + add ebp, su_unit.size ; move WRK to next unit + shr edi, 1 ; shift out the LSB bit = stereo bit + je su_run_vm_advance ; the opcode is zero, jump to advance + mov edx, [esp + 8] ; reset INP to point to the inputs part of voice + pushf ; push flags to save carry = stereo bit + add edx, su_voice.inputs + xor ecx, ecx ; counter = 0 + xor eax, eax ; clear out high bits of eax, as lodsb only sets al +su_transform_operands_loop: + cmp cl, byte [su_vm_transformcounts-1+edi] ; compare the counter to the value in the param count table + je su_transform_operands_out + lodsb ; load the operand from VAL stream + push eax ; push it to memory so FPU can read it + fild dword [esp] ; load the operand value to FPU stack + fmul dword [FCONST_0_00781250] ; divide it by 128 (0 => 0, 128 => 1.0) + fadd dword [ebp+su_unit.ports+ecx*4] ; add the modulations in the current workspace + fstp dword [edx+ecx*4] ; store the modulated value in the inputs section of voice + xor eax, eax + mov dword [ebp+su_unit.ports+ecx*4], eax ; clear out the modulation ports + pop eax + inc ecx + jmp su_transform_operands_loop +su_transform_operands_out: + popf ; pop flags for the carry bit = stereo bit + call [su_vm_jumptable-4+edi*4] ; call the function corresponding to the instruction + jmp su_run_vm_loop +su_run_vm_advance: + mov ebp, dword [esp + 8] ; load pointer to voice to register + add ebp, su_voice.size ; shift it to point to following voice + mov dword [esp + 8], ebp ; save back to stack + dec dword [esp + 36] ; voices-- + jne su_run_vm_loop ; if there's more voices to process, goto vm_loop + popad ; Popped: eax, ecx = DelayWorkSpace, edx = Synth, ebx = OpcodeStream, esp, ebp = Voice, esi = OperandStream, edi. Stack: retaddr_su_run_vm, VoicesRemain, Sample, Row, GlobalTick, RandSeed, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr + ret +;------------------------------------------------------------------------------- +; ADDP opcode: add the two top most signals on the stack and pop +;------------------------------------------------------------------------------- +; Mono: a b -> a+b +; Stereo: a b c d -> a+c b+d +;------------------------------------------------------------------------------- +section .su_op_addp code align=1 +su_op_addp: + jnc su_op_addp_mono + faddp st2, st0 + faddp st2, st0 + ret +su_op_addp_mono: + faddp st1, st0 + ret -%ifdef GO4K_USE_VCO_GATE -%ifdef USE_SECTIONS -section .g4kcodq code align=1 -%else -section .text -%endif -go4kVCO_gate: - fxch ; // p c - fstp st1 ; // p - fmul dword [c_16] ; // p' - push eax - push eax - fistp dword [esp] ; // - - fld1 ; // 1 - pop eax - and al, 0xf - bt word [VAL-5],ax - jc go4kVCO_gate_bit - fsub st0, st0 ; // 0 -go4kVCO_gate_bit: - fld dword [WRK+go4kVCO_wrk.cm] ; // f x - fsub st1 ; // f-x x - fmul dword [c_dc_const] ; // c(f-x) x - faddp st1, st0 ; // x' - fst dword [WRK+go4kVCO_wrk.cm] - pop eax - ret -%endif +;------------------------------------------------------------------------------- +; MULP opcode: multiply the two top most signals on the stack and pop +;------------------------------------------------------------------------------- +; Mono: a b -> a*b +; Stereo: a b c d -> a*c b*d +;------------------------------------------------------------------------------- +section .su_op_mulp code align=1 +su_op_mulp: + jnc su_op_mulp_mono + fmulp st2, st0 + fmulp st2, st0 + ret +su_op_mulp_mono: + fmulp st1 + ret -%ifdef USE_SECTIONS -section .g4kcodb code align=1 -%else -section .text -%endif -export_func go4kVCO_func@0 -%ifdef GO4K_USE_VCO_PHASE_OFFSET - %ifdef GO4K_USE_VCO_SHAPE - %ifdef GO4K_USE_VCO_GATE - push 8 - %else - push 7 - %endif - %else - %ifdef GO4K_USE_VCO_GATE - push 7 - %else - push 6 - %endif - %endif -%else - %ifdef GO4K_USE_VCO_SHAPE - %ifdef GO4K_USE_VCO_GATE - push 7 - %else - push 6 - %endif - %else - %ifdef GO4K_USE_VCO_GATE - push 6 - %else - push 5 - %endif - %endif -%endif - call go4kTransformValues -%ifdef GO4K_USE_VCO_CHECK -; check if current note still active - mov eax, dword [ecx-4] - test eax, eax - jne go4kVCO_func_do -%ifdef GO4K_USE_VCO_STEREO - movzx eax, byte [VAL-1] ; // get flags and check for stereo - test al, byte VCO_STEREO - jz short go4kVCO_func_nostereoout - fldz -go4kVCO_func_nostereoout: -%endif - fldz - ret -go4kVCO_func_do: -%endif - movzx eax, byte [VAL-1] ; // get flags -%ifdef GO4K_USE_VCO_STEREO - test al, byte VCO_STEREO - jz short go4kVCO_func_nopswap - fld dword [WRK+go4kVCO_wrk.phase] ;// swap left/right phase values for first stereo run - fld dword [WRK+go4kVCO_wrk.phase2] - fstp dword [WRK+go4kVCO_wrk.phase] - fstp dword [WRK+go4kVCO_wrk.phase2] -go4kVCO_func_nopswap: -%endif -go4kVCO_func_process: - fld dword [edx+go4kVCO_val.transpose] - fsub dword [c_0_5] -%ifdef GO4K_USE_VCO_MOD_TM - fadd dword [WRK+go4kVCO_wrk.tm] -%endif - fdiv dword [c_i128] - fld dword [edx+go4kVCO_val.detune] - fsub dword [c_0_5] - fadd st0 -%ifdef GO4K_USE_VCO_STEREO - test al, byte VCO_STEREO - jz short go4kVCO_func_nodswap - fchs ;// negate detune for stereo -go4kVCO_func_nodswap: -%endif - faddp st1 -%ifdef GO4K_USE_VCO_MOD_DM - fadd dword [WRK+go4kVCO_wrk.dm] -%endif - ; // st0 now contains the transpose+detune offset - test al, byte LFO - jnz go4kVCO_func_skipnote - fiadd dword [ecx-4] ; // st0 is note, st1 is t+d offset -go4kVCO_func_skipnote: - fmul dword [c_i12] - call _Power@0 - test al, byte LFO - jz short go4kVCO_func_normalize_note - fmul dword [_LFO_NORMALIZE] ; // st0 is now frequency for lfo - jmp short go4kVCO_func_normalized -go4kVCO_func_normalize_note: - fmul dword [FREQ_NORMALIZE] ; // st0 is now frequency -go4kVCO_func_normalized: - fadd dword [WRK+go4kVCO_wrk.phase] -%ifdef GO4K_USE_VCO_MOD_FM - fadd dword [WRK+go4kVCO_wrk.fm] -%endif - fld1 - fadd st1, st0 - fxch - fprem - fstp st1 - fst dword [WRK+go4kVCO_wrk.phase] -%ifdef GO4K_USE_VCO_MOD_PM - fadd dword [WRK+go4kVCO_wrk.pm] -%endif -%ifdef GO4K_USE_VCO_PHASE_OFFSET - fadd dword [edx+go4kVCO_val.phaseofs] -%endif -%ifdef PHASE_RENORMALIZE - fld1 - fadd st1, st0 - fxch - fprem - fstp st1 ; // p -%endif - fld dword [edx+go4kVCO_val.color] ; // c p -%ifdef GO4K_USE_VCO_MOD_CM - fadd dword [WRK+go4kVCO_wrk.cm] ; // c p -%endif -go4kVCO_func_sine: - test al, byte SINE - jz short go4kVCO_func_trisaw - call go4kVCO_sine -go4kVCO_func_trisaw: - test al, byte TRISAW - jz short go4kVCO_func_pulse - call go4kVCO_trisaw -go4kVCO_func_pulse: - test al, byte PULSE -%ifdef GO4K_USE_VCO_GATE - jz short go4kVCO_func_gate -%else - jz short go4kVCO_func_noise -%endif - call go4kVCO_pulse -%ifdef GO4K_USE_VCO_GATE -go4kVCO_func_gate: - test al, byte GATE - jz short go4kVCO_func_noise - call go4kVCO_gate -%endif -go4kVCO_func_noise: - test al, byte NOISE - jz short go4kVCO_func_end - call _FloatRandomNumber@0 - fstp st1 - fstp st1 -go4kVCO_func_end: -%ifdef GO4K_USE_VCO_SHAPE - fld dword [edx+go4kVCO_val.shape] -%ifdef GO4K_USE_VCO_MOD_SM - fadd dword [WRK+go4kVCO_wrk.sm] -%endif - call go4kWaveshaper -%endif - fld dword [edx+go4kVCO_val.gain] -%ifdef GO4K_USE_VCO_MOD_GM - fadd dword [WRK+go4kVCO_wrk.gm] -%endif - fmulp st1, st0 - -%ifdef GO4K_USE_VCO_STEREO - test al, byte VCO_STEREO - jz short go4kVCO_func_stereodone - sub al, byte VCO_STEREO - fld dword [WRK+go4kVCO_wrk.phase] ;// swap left/right phase values again for second stereo run - fld dword [WRK+go4kVCO_wrk.phase2] - fstp dword [WRK+go4kVCO_wrk.phase] - fstp dword [WRK+go4kVCO_wrk.phase2] - jmp go4kVCO_func_process -go4kVCO_func_stereodone: -%endif - ret - -%ifdef USE_SECTIONS -section .g4kcodc code align=1 -%else -section .text -%endif -; //---------------------------------------------------------------------------------------- -; // VCF Tick -; //---------------------------------------------------------------------------------------- -; // IN : WRK = unit workspace -; // IN : VAL = unit values -; // IN : ecx = global workspace -; // OUT : -; // DIRTY : eax -; //---------------------------------------------------------------------------------------- -export_func go4kVCF_func@0 - push 3 - call go4kTransformValues -%ifdef GO4K_USE_VCF_CHECK -; check if current note still active - mov eax, dword [ecx-4] - test eax, eax - jne go4kVCF_func_do - ret -go4kVCF_func_do: -%endif - movzx eax, byte [VAL-1] ; // get type flag - - fld dword [edx+go4kVCF_val.res] ; // r in -%ifdef GO4K_USE_VCF_MOD_RM - fadd dword [WRK+go4kVCF_wrk.rm] -%endif - fstp dword [esp-8] - - fld dword [edx+go4kVCF_val.freq] ; // f in -%ifdef GO4K_USE_VCF_MOD_FM - fadd dword [WRK+go4kVCF_wrk.fm] -%endif - fmul st0, st0 ; // square the input so we never get negative and also have a smoother behaviour in the lower frequencies - fstp dword [esp-4] ; // in - -%ifdef GO4K_USE_VCF_STEREO - test al, byte STEREO - jz short go4kVCF_func_process - add WRK, go4kVCF_wrk.low2 -go4kVCF_func_stereoloop: ; // switch channels - fxch st1 ; // inr inl -%endif - -go4kVCF_func_process: - fld dword [esp-8] - fld dword [esp-4] - fmul dword [WRK+go4kVCF_wrk.band] ; // f*b r in - fadd dword [WRK+go4kVCF_wrk.low] ; // l' r in - fst dword [WRK+go4kVCF_wrk.low] ; // l' r in - fsubp st2, st0 ; // r in-l' - fmul dword [WRK+go4kVCF_wrk.band] ; // r*b in-l' - fsubp st1, st0 ; // h' - fst dword [WRK+go4kVCF_wrk.high] ; // h' - fmul dword [esp-4] ; // h'*f - fadd dword [WRK+go4kVCF_wrk.band] ; // b' - fstp dword [WRK+go4kVCF_wrk.band] - fldz -go4kVCF_func_low: - test al, byte LOWPASS - jz short go4kVCF_func_high - fadd dword [WRK+go4kVCF_wrk.low] -go4kVCF_func_high: -%ifdef GO4K_USE_VCF_HIGH - test al, byte HIGHPASS - jz short go4kVCF_func_band - fadd dword [WRK+go4kVCF_wrk.high] -%endif -go4kVCF_func_band: -%ifdef GO4K_USE_VCF_BAND - test al, byte BANDPASS - jz short go4kVCF_func_peak - fadd dword [WRK+go4kVCF_wrk.band] -%endif -go4kVCF_func_peak: -%ifdef GO4K_USE_VCF_PEAK - test al, byte PEAK - jz short go4kVCF_func_processdone - fadd dword [WRK+go4kVCF_wrk.low] - fsub dword [WRK+go4kVCF_wrk.high] -%endif -go4kVCF_func_processdone: - -%ifdef GO4K_USE_VCF_STEREO - test al, byte STEREO ; // outr inl - jz short go4kVCF_func_end - sub al, byte STEREO - sub WRK, go4kVCF_wrk.low2 - jmp go4kVCF_func_stereoloop -%endif - -go4kVCF_func_end: ; // value - - - - - ret - -%ifdef USE_SECTIONS -section .g4kcodd code align=1 -%else -section .text -%endif -; //---------------------------------------------------------------------------------------- -; // DST Tick -; //---------------------------------------------------------------------------------------- -; // IN : WRK = unit workspace -; // IN : VAL = unit values -; // IN : ecx = global workspace -; // OUT : -; // DIRTY : eax -; //---------------------------------------------------------------------------------------- -export_func go4kDST_func@0 -%ifdef GO4K_USE_DST -%ifdef GO4K_USE_DST_SH - %ifdef GO4K_USE_DST_STEREO - push 3 - %else - push 2 - %endif -%else - %ifdef GO4K_USE_DST_STEREO - push 2 - %else - push 1 - %endif -%endif - call go4kTransformValues -%ifdef GO4K_USE_DST_CHECK -; check if current note still active - mov eax, dword [ecx-4] - test eax, eax - jne go4kDST_func_do - ret -go4kDST_func_do: -%endif - movzx eax, byte [VAL-1] ; // get type flag -%ifdef GO4K_USE_DST_SH - fld dword [edx+go4kDST_val.snhfreq] ; // snh in (inr) -%ifdef GO4K_USE_DST_MOD_SH - fadd dword [WRK+go4kDST_wrk.sm] ; // snh' in (inr) -%endif - fmul st0, st0 ; // square the input so we never get negative and also have a smoother behaviour in the lower frequencies - fchs - fadd dword [WRK+go4kDST_wrk.snhphase]; // snh' in (inr) - fst dword [WRK+go4kDST_wrk.snhphase] - fldz ; // 0 snh' in (inr) - fucomip st1 ; // snh' in (inr) - fstp dword [esp-4] ; // in (inr) - jc short go4kDST_func_hold - fld1 ; // 1 in (inr) - fadd dword [esp-4] ; // 1+snh' in (inr) - fstp dword [WRK+go4kDST_wrk.snhphase]; // in (inr) -%endif -; // calc pregain and postgain -%ifdef GO4K_USE_DST_STEREO - test al, byte STEREO - jz short go4kDST_func_mono - fxch st1 ; // inr inl - fld dword [edx+go4kDST_val.drive] ; // drive inr inl -%ifdef GO4K_USE_DST_MOD_DM - fadd dword [WRK+go4kDST_wrk.dm] -%endif - call go4kWaveshaper ; // outr inl -%ifdef GO4K_USE_DST_SH - fst dword [WRK+go4kDST_wrk.out2] ; // outr inl -%endif - fxch st1 ; // inl outr -go4kDST_func_mono: -%endif - fld dword [edx+go4kDST_val.drive] ; // drive in (outr) -%ifdef GO4K_USE_DST_MOD_DM - fadd dword [WRK+go4kDST_wrk.dm] -%endif - call go4kWaveshaper ; // out (outr) -%ifdef GO4K_USE_DST_SH - fst dword [WRK+go4kDST_wrk.out] ; // out' (outr) -%endif - ret ; // out' (outr) -%ifdef GO4K_USE_DST_SH -go4kDST_func_hold: ; // in (inr) - fstp st0 ; // (inr) -%ifdef GO4K_USE_DST_STEREO - test al, byte STEREO - jz short go4kDST_func_monohold ; // (inr) - fstp st0 ; // - fld dword [WRK+go4kDST_wrk.out2] ; // outr -go4kDST_func_monohold: -%endif - fld dword [WRK+go4kDST_wrk.out] ; // out (outr) - ret -%endif - -%endif - -%ifdef USE_SECTIONS -section .g4kcodf code align=1 -%else -section .text -%endif -; //---------------------------------------------------------------------------------------- -; // DLL Tick -; //---------------------------------------------------------------------------------------- -; // IN : WRK = unit workspace -; // IN : VAL = unit values -; // IN : ecx = global workspace -; // OUT : -; // DIRTY : eax -; //---------------------------------------------------------------------------------------- -export_func go4kDLL_func@0 -%ifdef GO4K_USE_DLL -%ifdef GO4K_USE_DLL_CHORUS - %ifdef GO4K_USE_DLL_DAMP - push 8 - %else - push 7 - %endif -%else - %ifdef GO4K_USE_DLL_DAMP - push 6 - %else - push 5 - %endif -%endif - call go4kTransformValues - pushad - movzx ebx, byte [VAL-(go4kDLL_val.size-go4kDLL_val.delay)/4] ;// delay length index -%ifdef GO4K_USE_DLL_NOTE_SYNC - test ebx, ebx - jne go4kDLL_func_process - fld1 - fild dword [ecx-4] ; // load note freq - fmul dword [c_i12] - call _Power@0 - fmul dword [FREQ_NORMALIZE] ; // normalize - fdivp st1, st0 ; // invert to get numer of samples - fistp word [_go4k_delay_times+ebx*2] ; store current comb size -%endif -go4kDLL_func_process: - mov ecx, eax ;// ecx is delay counter -%ifdef GO4K_USE_DLL_MOD - mov edi, WRK ;// edi is modulation workspace -%endif - mov WRK, dword [_go4k_delay_buffer_ofs] ;// ebp is current delay - fld st0 ;// in in -%ifdef GO4K_USE_DLL_MOD_IM - fld dword [edx+go4kDLL_val.dry] ;// dry in in - fadd dword [edi+go4kDLL_wrk2.im] ;// dry' in in - fmulp st1, st0 ;// out in -%else - fmul dword [edx+go4kDLL_val.dry] ;// out in -%endif - fxch ;// in out -%ifdef GO4K_USE_DLL_MOD_PM - fld dword [edx+go4kDLL_val.pregain] ;// pg in out - fadd dword [edi+go4kDLL_wrk2.pm] ;// pg' in out - fmul st0, st0 ;// pg'' in out - fmulp st1, st0 ;// in' out -%else - fmul dword [edx+go4kDLL_val.pregain] ;// in' out - fmul dword [edx+go4kDLL_val.pregain] ;// in' out -%endif - -%ifdef GO4K_USE_DLL_CHORUS -;// update saw lfo for chorus/flanger - fld dword [edx+go4kDLL_val.freq] ;// f in' out -%ifdef GO4K_USE_DLL_MOD_SM - fadd dword [edi+go4kDLL_wrk2.sm] ;// f' in' out -%endif - fmul st0, st0 - fmul st0, st0 - fdiv dword [DLL_DEPTH] - fadd dword [WRK+go4kDLL_wrk.phase] ;// p' in' out -;// clamp phase to 0,1 (only in editor, since delay can be active quite long) -%ifdef GO4K_USE_DLL_CHORUS_CLAMP - fld1 ;// 1 p' in' out - fadd st1, st0 ;// 1 1+p' in' out - fxch ;// 1+p' 1 in' out - fprem ;// p'' 1 in' out - fstp st1 ;// p'' in' out -%endif - fst dword [WRK+go4kDLL_wrk.phase] -;// get current sine value - fldpi ; // pi p in' out - fadd st0 ; // 2*pi p in' out - fmulp st1, st0 ; // 2*pi*p in' out - fsin ; // sin in' out - fld1 ; // 1 sin in' out - faddp st1, st0 ; // 1+sin in' out -;// mul with depth and convert to samples - fld dword [edx+go4kDLL_val.depth] ; // d 1+sin in' out -%ifdef GO4K_USE_DLL_MOD_AM - fadd dword [edi+go4kDLL_wrk2.am] ; // d' 1+sin in' out -%endif - fmul st0, st0 - fmul st0, st0 - fmul dword [DLL_DEPTH] - fmulp st1, st0 - fistp dword [esp-4] ; // in' out -%endif - -go4kDLL_func_loop: - movzx esi, word [_go4k_delay_times+ebx*2] ; fetch comb size - mov eax, dword [WRK+go4kDLL_wrk.index] ;// eax is current comb index - -%ifdef GO4K_USE_DLL_CHORUS - ;// add lfo offset and wrap buffer - add eax, dword [esp-4] - cmp eax, esi - jl short go4kDLL_func_buffer_nowrap1 - sub eax, esi -go4kDLL_func_buffer_nowrap1: -%endif - - fld dword [WRK+eax*4+go4kDLL_wrk.buffer];// cout in' out - -%ifdef GO4K_USE_DLL_CHORUS - mov eax, dword [WRK+go4kDLL_wrk.index] -%endif - - ;// add comb output to current output - fadd st2, st0 ;// cout in' out' -%ifdef GO4K_USE_DLL_DAMP - fld1 ;// 1 cout in' out' - fsub dword [edx+go4kDLL_val.damp] ;// 1-damp cout in' out' -%ifdef GO4K_USE_DLL_MOD_DM - fsub dword [edi+go4kDLL_wrk2.dm] ;// 1-damp' cout in' out' -%endif - fmulp st1, st0 ;// cout*d2 in' out' - fld dword [edx+go4kDLL_val.damp] ;// d1 cout*d2 in' out' -%ifdef GO4K_USE_DLL_MOD_DM - fadd dword [edi+go4kDLL_wrk2.dm] ;// d1' cout*d2 in' out' -%endif - fmul dword [WRK+go4kDLL_wrk.store] ;// store*d1 cout*d2 in' out' - faddp st1, st0 ;// store' in' out' - fst dword [WRK+go4kDLL_wrk.store] ;// store' in' out' -%endif -%ifdef GO4K_USE_DLL_MOD_FM - fld dword [edx+go4kDLL_val.feedback] ;// fb cout in' out' - fadd dword [edi+go4kDLL_wrk2.fm] ;// fb' cout in' out' - fmulp st1, st0 ;// cout*fb' in' out' -%else - fmul dword [edx+go4kDLL_val.feedback] ;// cout*fb in' out' -%endif -%ifdef GO4K_USE_DLL_DC_FILTER - fadd st0, st1 ;// store in' out' - fstp dword [WRK+eax*4+go4kDLL_wrk.buffer];// in' out' -%else - fsub st0, st1 ;// store in' out' - fstp dword [WRK+eax*4+go4kDLL_wrk.buffer];// in' out' - fneg -%endif - ;// wrap comb buffer pos - inc eax - cmp eax, esi - jl short go4kDLL_func_buffer_nowrap2 -%ifdef GO4K_USE_DLL_CHORUS - sub eax, esi -%else - xor eax, eax -%endif -go4kDLL_func_buffer_nowrap2: - mov dword [WRK+go4kDLL_wrk.index], eax - ;// increment buffer pointer to next buffer - inc ebx ;// go to next delay length index - add WRK, go4kDLL_wrk.size ;// go to next delay - mov dword [_go4k_delay_buffer_ofs], WRK ;// store next delay offset - loopne go4kDLL_func_loop - fstp st0 ;// out' - ;// process a dc filter to prevent heavy offsets in reverb -%ifdef GO4K_USE_DLL_DC_FILTER -; y(n) = x(n) - x(n-1) + R * y(n-1) - sub WRK, go4kDLL_wrk.size - fld dword [WRK+go4kDLL_wrk.dcout] ;// dco out' - fmul dword [c_dc_const] ;// dcc*dco out' - fsub dword [WRK+go4kDLL_wrk.dcin] ;// dcc*dco-dci out' - fxch ;// out' dcc*dco-dci - fst dword [WRK+go4kDLL_wrk.dcin] ;// out' dcc*dco-dci - faddp st1 ;// out' -%ifdef GO4K_USE_UNDENORMALIZE - fadd dword [c_0_5] ;// add and sub small offset to prevent denormalization - fsub dword [c_0_5] -%endif - fst dword [WRK+go4kDLL_wrk.dcout] -%endif - popad - ret -%endif +;------------------------------------------------------------------------------- +; XCH opcode: exchange the signals on the stack +;------------------------------------------------------------------------------- +; Mono: a b -> b a +; stereo: a b c d -> c d a b +;------------------------------------------------------------------------------- +section .su_op_xch code align=1 +su_op_xch: + fxch st0, st1 + ret -%ifdef USE_SECTIONS -section .g4kcodu code align=1 -%else -section .text -%endif -; //---------------------------------------------------------------------------------------- -; // GLITCH Tick -; //---------------------------------------------------------------------------------------- -; // IN : WRK = unit workspace -; // IN : VAL = unit values -; // IN : ecx = global workspace -; // OUT : -; // DIRTY : eax -; //---------------------------------------------------------------------------------------- -export_func go4kGLITCH_func@0 -%ifdef GO4K_USE_GLITCH - push 5 - call go4kTransformValues - pushad - - mov edi, WRK - mov WRK, dword [_go4k_delay_buffer_ofs] ;// ebp is current delay - -; mov eax, dword [edx+go4kGLITCH_val.active] -; or eax, dword [edi+go4kGLITCH_wrk2.am] -; test eax, eax -; je go4kGLITCH_func_notactive ;// out - - fld dword [edx+go4kGLITCH_val.active] ;// a in - fadd dword [edi+go4kGLITCH_wrk2.am] ;// a' in - ; // check for activity - fldz ;// 0 a' in - fucomip st1 ;// a' in - fstp st0 ;// in - jnc go4kGLITCH_func_notactive ;// out - - ;// check for first call and init if so init (using slizesize slot) - mov eax, dword [WRK+go4kGLITCH_wrk.slizesize] - and eax, eax - jnz go4kGLITCH_func_process - mov dword [WRK+go4kGLITCH_wrk.index], eax - mov dword [WRK+go4kGLITCH_wrk.store], eax - movzx ebx, byte [VAL-(go4kGLITCH_val.size-go4kGLITCH_val.slicesize)/4] ;// slicesize index - movzx eax, word [_go4k_delay_times+ebx*2] ;// fetch slicesize - push eax - fld1 - fild dword [esp] - fstp dword [WRK+go4kGLITCH_wrk.slizesize] - fstp dword [WRK+go4kGLITCH_wrk.slicepitch] - pop eax -go4kGLITCH_func_process: - ;// fill buffer until full - mov eax, dword [WRK+go4kGLITCH_wrk.store] - cmp eax, MAX_DELAY - jae go4kGLITCH_func_filldone - fst dword [WRK+eax*4+go4kDLL_wrk.buffer] ;// in - inc dword [WRK+go4kGLITCH_wrk.store] -go4kGLITCH_func_filldone: - ;// save input - push eax - fstp dword [esp] ;// - - - ;// read from buffer - push eax - fld dword [WRK+go4kGLITCH_wrk.index] ;// idx - fist dword [esp] - pop eax - fld dword [WRK+eax*4+go4kDLL_wrk.buffer] ;// out idx - fxch ;// idx out - ;// progress readindex with current play speed - fadd dword [WRK+go4kGLITCH_wrk.slicepitch] ;// idx' out - fst dword [WRK+go4kGLITCH_wrk.index] - - ;// check for slice done - fld dword [WRK+go4kGLITCH_wrk.slizesize] ;// size idx' out - fxch ;// idx' size out - fucomip st1 ;// idx' out - fstp st0 ;// out - jc go4kGLITCH_func_process_done - ;// reinit for next loop - xor eax, eax - mov dword [WRK+go4kGLITCH_wrk.index], eax - - fld dword [edx+go4kGLITCH_val.dsize] - fadd dword [edi+go4kGLITCH_wrk2.sm] - fsub dword [c_0_5] - fmul dword [c_0_5] - call _Power@0 - fmul dword [WRK+go4kGLITCH_wrk.slizesize] - fstp dword [WRK+go4kGLITCH_wrk.slizesize] - - fld dword [edx+go4kGLITCH_val.dpitch] - fadd dword [edi+go4kGLITCH_wrk2.pm] - fsub dword [c_0_5] - fmul dword [c_0_5] - call _Power@0 - fmul dword [WRK+go4kGLITCH_wrk.slicepitch] - fstp dword [WRK+go4kGLITCH_wrk.slicepitch] -go4kGLITCH_func_process_done: - - ;// dry wet mix - fld dword [edx+go4kGLITCH_val.dry] ;// dry out - fadd dword [edi+go4kGLITCH_wrk2.dm] ;// dry' out - fld1 ;// 1 dry' out - fsub st1 ;// 1-dry' dry' out - fmulp st2 ;// dry' out' - fmul dword [esp] ;// in' out' - faddp st1, st0 ;// out' - - pop eax - jmp go4kGLITCH_func_leave -go4kGLITCH_func_notactive: - ;// mark as uninitialized again (using slizesize slot) - xor eax,eax - mov dword [WRK+go4kGLITCH_wrk.slizesize], eax -go4kGLITCH_func_leave: - add WRK, go4kDLL_wrk.size ;// go to next delay - mov dword [_go4k_delay_buffer_ofs], WRK ;// store next delay offset - popad - ret -%endif +;------------------------------------------------------------------------------- +; HOLD opcode: sample and hold the signal, reducing sample rate +;------------------------------------------------------------------------------- +; Mono version: holds the signal at a rate defined by the freq parameter +; Stereo version: holds both channels +;------------------------------------------------------------------------------- +section .su_op_hold code align=1 +su_op_hold: + fld dword [edx] ; f x + fmul st0, st0 ; f^2 x + fchs ; -f^2 x + fadd dword [ebp] ; p-f^2 x + fst dword [ebp] ; p <- p-f^2 + fldz ; 0 p x + fucomip st1 ; p x + fstp dword [esp-4] ; t=p, x + jc short su_op_hold_holding ; if (0 < p) goto holding + fld1 ; 1 x + fadd dword [esp-4] ; 1+t x + fstp dword [ebp] ; x + fst dword [ebp+4] ; save holded value + ret ; x +su_op_hold_holding: + fstp st0 ; + fld dword [ebp+4] ; x + ret -%ifdef USE_SECTIONS -section .g4kcodg code align=1 -%else -section .text -%endif -; //---------------------------------------------------------------------------------------- -; // FOP Tick (various fp stack based operations) -; //---------------------------------------------------------------------------------------- -; // IN : WRK = unit workspace -; // IN : VAL = unit values -; // IN : ecx = global workspace -; // OUT : -; // DIRTY : -; //---------------------------------------------------------------------------------------- -export_func go4kFOP_func@0 - push 1 - call go4kTransformValues -go4kFOP_func_pop: - dec eax - jnz go4kFOP_func_addp - fstp st0 - ret -go4kFOP_func_addp: - dec eax - jnz go4kFOP_func_mulp - faddp st1, st0 - ret -go4kFOP_func_mulp: - dec eax - jnz go4kFOP_func_push - fmulp st1, st0 - ret -go4kFOP_func_push: - dec eax - jnz go4kFOP_func_xch - fld st0 - ret -go4kFOP_func_xch: - dec eax - jnz go4kFOP_func_add - fxch - ret -go4kFOP_func_add: - dec eax - jnz go4kFOP_func_mul - fadd st1 - ret -go4kFOP_func_mul: - dec eax - jnz go4kFOP_func_addp2 - fmul st1 - ret -go4kFOP_func_addp2: - dec eax - jnz go4kFOP_func_loadnote - faddp st2, st0 - faddp st2, st0 - ret -go4kFOP_func_loadnote: - dec eax - jnz go4kFOP_func_mulp2 - fild dword [ecx-4] - fmul dword [c_i128] - ret -go4kFOP_func_mulp2: - fmulp st2, st0 - fmulp st2, st0 - ret - -%ifdef USE_SECTIONS -section .g4kcodh code align=1 -%else -section .text -%endif -; //---------------------------------------------------------------------------------------- -; // FST Tick (stores a value somewhere in the local workspace) -; //---------------------------------------------------------------------------------------- -; // IN : WRK = unit workspace -; // IN : VAL = unit values -; // IN : ecx = global workspace -; // OUT : -; // DIRTY : -; //---------------------------------------------------------------------------------------- -export_func go4kFST_func@0 - push 1 - call go4kTransformValues - fld dword [edx+go4kFST_val.amount] - fsub dword [c_0_5] - fadd st0 - fmul st1 - lodsw - and eax, 0x00003fff ; // eax is destination slot - test word [VAL-2], FST_ADD - jz go4kFST_func_set - fadd dword [ecx+eax*4] -go4kFST_func_set: - fstp dword [ecx+eax*4] - test word [VAL-2], FST_POP - jz go4kFST_func_done - fstp st0 -go4kFST_func_done: - ret +;------------------------------------------------------------------------------- +; FILTER opcode: perform low/high/band-pass/notch etc. filtering on the signal +;------------------------------------------------------------------------------- +; Mono: x -> filtered(x) +; Stereo: l r -> filtered(l) filtered(r) +;------------------------------------------------------------------------------- +section .su_op_filter code align=1 +su_op_filter: + lodsb ; load the flags to al + call su_effects_stereohelper + fld dword [edx + 4] ; r x + fld dword [edx]; f r x + fmul st0, st0 ; f2 x (square the input so we never get negative and also have a smoother behaviour in the lower frequencies) + fst dword [ebp+12] ; f2 r x + fmul dword [ebp+8] ; f2*b r x + fadd dword [ebp] ; f2*b+l r x + fst dword [ebp] ; l'=f2*b+l r x + fsubp st2, st0 ; r x-l' + fmul dword [ebp+8] ; r*b x-l' + fsubp st1, st0 ; x-l'-r*b + fst dword [ebp+4] ; h'=x-l'-r*b + fmul dword [ebp+12] ; f2*h' + fadd dword [ebp+8] ; f2*h'+b + fstp dword [ebp+8] ; b'=f2*h'+b + fldz ; 0 + test al, byte 0x40 + jz short su_op_filter_skiplowpass + fadd dword [ebp] +su_op_filter_skiplowpass: + test al, byte 0x20 + jz short su_op_filter_skipbandpass + fadd dword [ebp+8] +su_op_filter_skipbandpass: + test al, byte 0x10 + jz short su_op_filter_skiphighpass + fadd dword [ebp+4] +su_op_filter_skiphighpass: + test al, byte 0x08 + jz short su_op_filter_skipnegbandpass + fsub dword [ebp+8] +su_op_filter_skipnegbandpass: + test al, byte 0x04 + jz short su_op_filter_skipneghighpass + fsub dword [ebp+4] +su_op_filter_skipneghighpass: + ret +;------------------------------------------------------------------------------- +; PAN opcode: pan the signal +;------------------------------------------------------------------------------- +; Mono: s -> s*(1-p) s*p +; Stereo: l r -> l*(1-p) r*p +; +; where p is the panning in [0,1] range +;------------------------------------------------------------------------------- +section .su_op_pan code align=1 +su_op_pan: + fld dword [edx] ; p s + fmul st1 ; p*s s + fsub st1, st0 ; p*s s-p*s + ; Equal to + ; s*p s*(1-p) + fxch ; s*(1-p) s*p SHOULD PROBABLY DELETE, WHY BOTHER + ret -%ifdef USE_SECTIONS -section .g4kcodm code align=1 -%else -section .text -%endif -; //---------------------------------------------------------------------------------------- -; // FLD Tick (load a value on stack, optionally add a modulation signal beforehand) -; //---------------------------------------------------------------------------------------- -; // IN : WRK = unit workspace -; // IN : VAL = unit values -; // IN : ecx = global workspace -; // OUT : signal-signal*pan , signal*pan -; // DIRTY : -; //---------------------------------------------------------------------------------------- -export_func go4kFLD_func@0 ;// in main env -%ifdef GO4K_USE_FLD - push 1 - call go4kTransformValues - fld dword [edx+go4kFLD_val.value] ;// value in - fsub dword [c_0_5] - fadd st0 -%ifdef GO4K_USE_FLD_MOD_VM - fadd dword [WRK+go4kFLD_wrk.vm] ;// value' in -%endif -%endif - ret - -%ifdef GO4K_USE_FSTG -%ifdef USE_SECTIONS -section .g4kcodi code align=1 -%else -section .text -%endif -; //---------------------------------------------------------------------------------------- -; // FSTG Tick (stores a value anywhere in the synth (and in each voice)) -; //---------------------------------------------------------------------------------------- -; // IN : WRK = unit workspace -; // IN : VAL = unit values -; // IN : ecx = global workspace -; // OUT : -; // DIRTY : -; //---------------------------------------------------------------------------------------- -export_func go4kFSTG_func@0 - push 1 - call go4kTransformValues -%ifdef GO4K_USE_FSTG_CHECK -; check if current note still active - mov eax, dword [ecx-4] - test eax, eax - jne go4kFSTG_func_do - lodsw - jmp go4kFSTG_func_testpop -go4kFSTG_func_do: -%endif - fld dword [edx+go4kFST_val.amount] - fsub dword [c_0_5] - fadd st0 - fmul st1 - lodsw - and eax, 0x00003fff ; // eax is destination slot - test word [VAL-2], FST_ADD - jz go4kFSTG_func_set - fadd dword [go4k_synth_wrk+eax*4] -go4kFSTG_func_set: -%if MAX_VOICES > 1 - fst dword [go4k_synth_wrk+eax*4] - fstp dword [go4k_synth_wrk+eax*4+go4k_instrument.size] -%else - fstp dword [go4k_synth_wrk+eax*4] -%endif -go4kFSTG_func_testpop: - test word [VAL-2], FST_POP - jz go4kFSTG_func_done - fstp st0 -go4kFSTG_func_done: - ret -%endif +;------------------------------------------------------------------------------- +; DELAY opcode: adds delay effect to the signal +;------------------------------------------------------------------------------- +; Mono: perform delay on ST0, using delaycount delaylines starting +; at delayindex from the delaytable +; Stereo: perform delay on ST1, using delaycount delaylines starting +; at delayindex + delaycount from the delaytable (so the right delays +; can be different) +;------------------------------------------------------------------------------- +section .su_op_delay code align=1 +su_op_delay: + lodsw ; al = delay index, ah = delay count + pushad ; Stack: edi, DelayVal, ebp, esp, DelayCom, edx, ecx, eax, retaddr_su_op_delay, edi, OperandStream, Voice, esp, OpcodeStream, Synth, DelayWorkSpace, eax, retaddr_su_run_vm, VoicesRemain, Sample, Row, GlobalTick, RandSeed, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr + movzx ebx, al + lea ebx,[su_delay_times + ebx*2] ; BX now points to the right position within delay time table + movzx esi, word [esp + 84] ; notice that we load word, so we wrap at 65536 + mov ecx, dword [esp + 60] ; ebp is now the separate delay workspace, as they require a lot more space + jnc su_op_delay_mono + push eax ; save _ah (delay count) + fxch ; r l + call su_op_delay_do ; D(r) l process delay for the right channel + pop eax ; restore the count for second run + fxch ; l D(r) +su_op_delay_mono: ; flow into mono delay + call su_op_delay_do ; when stereo delay is not enabled, we could inline this to save 5 bytes, but I expect stereo delay to be farely popular so maybe not worth the hassle + mov dword [esp + 60],ecx ; move delay workspace pointer back to stack. + popad ; Popped: eax, ecx, edx, ebx = DelayCom, esp, ebp, esi = DelayVal, edi. Stack: retaddr_su_op_delay, edi, OperandStream, Voice, esp, OpcodeStream, Synth, DelayWorkSpace, eax, retaddr_su_run_vm, VoicesRemain, Sample, Row, GlobalTick, RandSeed, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr + ret -%ifdef USE_SECTIONS -section .g4kcodj code align=1 -%else -section .text -%endif -; //---------------------------------------------------------------------------------------- -; // PAN Tick (multiplies signal with main envelope and converts to stereo) -; //---------------------------------------------------------------------------------------- -; // IN : WRK = unit workspace -; // IN : VAL = unit values -; // IN : ecx = global workspace -; // OUT : signal-signal*pan , signal*pan -; // DIRTY : -; //---------------------------------------------------------------------------------------- -export_func go4kPAN_func@0 ;// in main env -%ifdef GO4K_USE_PAN - push 1 - call go4kTransformValues - fld dword [edx+go4kPAN_val.panning] ;// pan in -%ifdef GO4K_USE_PAN_MOD - fadd dword [WRK+go4kPAN_wrk.pm] ;// pan in -%endif - fmul st1 ;// r in - fsub st1, st0 ;// r l - fxch ;// l r -%else - fmul dword [c_0_5] - fld st0 -%endif - ret +;------------------------------------------------------------------------------- +; su_op_delay_do: executes the actual delay +;------------------------------------------------------------------------------- +; Pseudocode: +; q = dr*x +; for (i = 0;i < count;i++) +; s = b[(t-delaytime[i+offset])&65535] +; q += s +; o[i] = o[i]*da+s*(1-da) +; b[t] = f*o[i] +p^2*x +; Perform dc-filtering q and output q +;------------------------------------------------------------------------------- +section .su_op_delay_do code align=1 +su_op_delay_do: ; x y + fld st0 + fmul dword [edx] ; p*x y + fmul dword [edx] ; p*p*x y + fxch ; y p*p*x + fmul dword [edx + 4] ; dr*y p*p*x +su_op_delay_loop: + mov edi, esi + sub di, word [ebx] ; we perform the math in 16-bit to wrap around + fld dword [ecx+su_delayline_wrk.buffer+edi*4]; s dr*y p*p*x, where s is the sample from delay buffer + fadd st1, st0 ; s dr*y+s p*p*x (add comb output to current output) + fld1 ; 1 s dr*y+s p*p*x + fsub dword [edx + 12] ; 1-da s dr*y+s p*p*x + fmulp st1, st0 ; s*(1-da) dr*y+s p*p*x + fld dword [edx + 12] ; da s*(1-da) dr*y+s p*p*x + fmul dword [ecx+su_delayline_wrk.filtstate] ; o*da s*(1-da) dr*y+s p*p*x, where o is stored + faddp st1, st0 ; o*da+s*(1-da) dr*y+s p*p*x + fadd dword [FCONST_0_500000] ; add and sub small offset to prevent denormalization. WARNING: this is highly important, as the damp filters might denormalize and give 100x CPU penalty + fsub dword [FCONST_0_500000] ; See for example: https://stackoverflow.com/questions/36781881/why-denormalized-floats-are-so-much-slower-than-other-floats-from-hardware-arch + fst dword [ecx+su_delayline_wrk.filtstate] ; o'=o*da+s*(1-da), o' dr*y+s p*p*x + fmul dword [edx + 8] ; f*o' dr*y+s p*p*x + fadd st0, st2 ; f*o'+p*p*x dr*y+s p*p*x + fstp dword [ecx+su_delayline_wrk.buffer+esi*4]; save f*o'+p*p*x to delay buffer + add ebx,2 ; move to next index + add ecx, su_delayline_wrk.size ; go to next delay delay workspace + sub ah, 2 + jg su_op_delay_loop ; if ah > 0, goto loop + fstp st1 ; dr*y+s1+s2+s3+... + ; DC-filtering + fld dword [ecx+su_delayline_wrk.dcout] ; o s + fmul dword [FCONST_0_99609375] ; c*o s + fsub dword [ecx+su_delayline_wrk.dcin] ; c*o-i s + fxch ; s c*o-i + fst dword [ecx+su_delayline_wrk.dcin] ; i'=s, s c*o-i + faddp st1 ; s+c*o-i + fadd dword [FCONST_0_500000] ; add and sub small offset to prevent denormalization. WARNING: this is highly important, as low pass filters might denormalize and give 100x CPU penalty + fsub dword [FCONST_0_500000] ; See for example: https://stackoverflow.com/questions/36781881/why-denormalized-floats-are-so-much-slower-than-other-floats-from-hardware-arch + fst dword [ecx+su_delayline_wrk.dcout] ; o'=s+c*o-i + ret -%ifdef USE_SECTIONS -section .g4kcodk code align=1 -%else -section .text -%endif -; //---------------------------------------------------------------------------------------- -; // OUT Tick ( stores stereo signal pair in temp buffers of the instrument) -; //---------------------------------------------------------------------------------------- -; // IN : WRK = unit workspace -; // IN : VAL = unit values -; // IN : ecx = global workspace -; // OUT : -; // DIRTY : -; //---------------------------------------------------------------------------------------- -export_func go4kOUT_func@0 ;// l r -%ifdef GO4K_USE_GLOBAL_DLL - push 2 - call go4kTransformValues - pushad - lea edi, [ecx+MAX_UNITS*MAX_UNIT_SLOTS*4] - fld st1 ;// r l r - fld st1 ;// l r l r - fld dword [edx+go4kOUT_val.auxsend] ;// as l r l r -%ifdef GO4K_USE_OUT_MOD_AM - fadd dword [WRK+go4kOUT_wrk.am] ;// am l r l r -%endif - fmulp st1, st0 ;// l' r l r - fstp dword [edi] ;// r l r - scasd - fld dword [edx+go4kOUT_val.auxsend] ;// as r l r -%ifdef GO4K_USE_OUT_MOD_AM - fadd dword [WRK+go4kOUT_wrk.am] ;// am r l r -%endif - fmulp st1, st0 ;// r' l r - fstp dword [edi] ;// l r - scasd - fld dword [edx+go4kOUT_val.gain] ;// g l r -%ifdef GO4K_USE_OUT_MOD_GM - fadd dword [WRK+go4kOUT_wrk.gm] ;// gm l r -%endif - fmulp st1, st0 ;// l' r - fstp dword [edi] ;// r - scasd - fld dword [edx+go4kOUT_val.gain] ;// g r -%ifdef GO4K_USE_OUT_MOD_GM - fadd dword [WRK+go4kOUT_wrk.gm] ;// gm r -%endif - fmulp st1, st0 ;// r' - fstp dword [edi] ;// - - scasd - popad -%else - push 1 - call go4kTransformValues +;------------------------------------------------------------------------------- +; COMPRES opcode: push compressor gain to stack +;------------------------------------------------------------------------------- +; Mono: push g on stack, where g is a suitable gain for the signal +; you can either MULP to compress the signal or SEND it to a GAIN +; somewhere else for compressor side-chaining. +; Stereo: push g g on stack, where g is calculated using l^2 + r^2 +;------------------------------------------------------------------------------- +section .su_op_compressor code align=1 +su_op_compressor: + fld st0 ; x x + fmul st0, st0 ; x^2 x + fld st2 ; r x^2 l r + fst st3 ; y x^2 l r + fmul st0, st0 ; y^2 x^2 l r + faddp st1, st0 ; y^2+x^2 l r + fld dword [ebp] ; l x^2 x + fucomi st0, st1 + setnb al ; if (st0 >= st1) al = 1; else al = 0; + fsubp st1, st0 ; x^2-l x + call su_nonlinear_map ; c x^2-l x, c is either attack or release parameter mapped in a nonlinear way + fmulp st1, st0 ; c*(x^2-l) x + fadd dword [ebp] ; l+c*(x^2-l) x // we could've kept level in the stack and save a few bytes, but su_env_map uses 3 stack (c + 2 temp), so the stack was getting quite big. + ; TODO: make this denormalization optional, if the user wants to save some space + fadd dword [FCONST_0_500000] ; add and sub small offset to prevent denormalization. WARNING: this is highly important, as the damp filters might denormalize and give 100x CPU penalty + fsub dword [FCONST_0_500000] ; See for example: https://stackoverflow.com/questions/36781881/why-denormalized-floats-are-so-much-slower-than-other-floats-from-hardware-arch + fst dword [ebp] ; l'=l+c*(x^2-l), l' x + fld dword [edx + 12] ; t l' x + fmul st0, st0 ; t*t l' x + fxch ; l' t*t x + fucomi st0, st1 ; if l' < t*t + fcmovb st0, st1 ; l'=t*t + fdivp st1, st0 ; t*t/l' x + fld dword [edx + 16] ; r t*t/l' x + + fmul dword [FCONST_0_500000] ; p=r/2 t*t/l' x + fxch ; t*t/l' p x + fyl2x ; p*log2(t*t/l') x + call su_power ; 2^(p*log2(t*t/l')) x + ; Equal to: + ; (t*t/l')^p x + ; if ratio is at minimum => p=0 => 1 x + ; if ratio is at maximum => p=0.5 => t/x => t/x*x=t + fdiv dword [edx + 8]; this used to be pregain but that ran into problems with getting back up to 0 dB so postgain should be better at that + fld st0 ; and return the computed gain two times, ready for MULP STEREO + ret - fld dword [edx+go4kOUT_val.gain] ;// g l r -%ifdef GO4K_USE_OUT_MOD_GM - fadd dword [WRK+go4kOUT_wrk.gm] ;// gm l r -%endif - fmulp st1, st0 ;// l' r - fstp dword [ecx+MAX_UNITS*MAX_UNIT_SLOTS*4+0] ;// r - fld dword [edx+go4kOUT_val.gain] ;// g r -%ifdef GO4K_USE_OUT_MOD_GM - fadd dword [WRK+go4kOUT_wrk.gm] ;// gm r -%endif - fmulp st1, st0 ;// r' - fstp dword [ecx+MAX_UNITS*MAX_UNIT_SLOTS*4+4] ;// - - -%endif - ret -%ifdef USE_SECTIONS -section .g4kcodl code align=1 -%else -section .text -%endif -; //---------------------------------------------------------------------------------------- -; // ACC Tick (stereo signal accumulation for synth commands only -> dont use in instrument commands) -; //---------------------------------------------------------------------------------------- -; // IN : WRK = unit workspace -; // IN : VAL = unit values -; // IN : ecx = global workspace -; // OUT : -; // DIRTY : eax -; //---------------------------------------------------------------------------------------- -export_func go4kACC_func@0 - push 1 - call go4kTransformValues - pushad - mov edi, go4k_synth_wrk - add edi, go4k_instrument.size - sub edi, eax ; // eax already contains the accumulation offset from the go4kTransformValues call - mov cl, MAX_INSTRUMENTS*MAX_VOICES - fldz ;// 0 - fldz ;// 0 0 -go4kACC_func_loop: - fadd dword [edi-8] ;// l 0 - fxch ;// 0 l - fadd dword [edi-4] ;// r l - fxch ;// l r - add edi, go4k_instrument.size - dec cl - jnz go4kACC_func_loop - popad - ret - -%ifdef USE_SECTIONS -section .g4kcodw code align=1 -%else -section .text -%endif -; //---------------------------------------------------------------------------------------- -; // Update Instrument (allocate voices, set voice to release) -; //---------------------------------------------------------------------------------------- -; // IN : -; // IN : -; // OUT : -; // DIRTY : -; //---------------------------------------------------------------------------------------- -go4kUpdateInstrument: -; // get new note - mov eax, dword [esp+4+4] ; // eax = current tick - shr eax, PATTERN_SIZE_SHIFT ; // eax = current pattern - imul edx, ecx, dword MAX_PATTERNS ; // edx = instrument pattern list index - movzx edx, byte [edx+eax+go4k_pattern_lists] ; // edx = pattern index - mov eax, dword [esp+4+4] ; // eax = current tick - shl edx, PATTERN_SIZE_SHIFT - and eax, PATTERN_SIZE-1 - movzx edx, byte [edx+eax+go4k_patterns] ; // edx = requested note in new patterntick -; // apply note changes - cmp dl, HLD ; // anything but hold causes action - je short go4kUpdateInstrument_done - inc dword [edi] ; // set release flag if needed -%if MAX_VOICES > 1 - inc dword [edi+go4k_instrument.size] ; // set release flag if needed -%endif - cmp dl, HLD ; // check for new note - jl short go4kUpdateInstrument_done -%if MAX_VOICES > 1 - pushad - xchg eax, dword [go4k_voiceindex + ecx*4] - test eax, eax - je go4kUpdateInstrument_newNote - add edi, go4k_instrument.size -go4kUpdateInstrument_newNote: - xor al,1 - xchg dword [go4k_voiceindex + ecx*4], eax -%endif - pushad - xor eax, eax - mov ecx, (8+MAX_UNITS*MAX_UNIT_SLOTS*4)/4 ; // clear only relase, note and workspace - rep stosd - popad - mov dword [edi+4], edx ; // set requested note as current note -%if MAX_VOICES > 1 - popad -%endif - jmp short go4kUpdateInstrument_done -go4kUpdateInstrument_done: - ret +;------------------------------------------------------------------------------- +; OUT opcode: outputs and pops the signal +;------------------------------------------------------------------------------- +; Stereo: add ST0 to left out and ST1 to right out, then pop +;------------------------------------------------------------------------------- +section .su_op_out code align=1 +su_op_out: ; l r + mov edi, [esp + 24] ; DI points to the synth object, use DI consistently in sinks/sources presumably to increase compression rate + call su_op_out_mono + add edi, 4 ; shift from left to right channel +su_op_out_mono: + fmul dword [edx] ; multiply by gain + fadd dword [edi + su_synthworkspace.left] ; add current value of the output + fstp dword [edi + su_synthworkspace.left] ; store the new value of the output + ret -%ifdef USE_SECTIONS -section .g4kcodx code align=1 -%else -section .text -%endif -; //---------------------------------------------------------------------------------------- -; // Render Voices -; //---------------------------------------------------------------------------------------- -; // IN : -; // IN : -; // OUT : -; // DIRTY : -; //---------------------------------------------------------------------------------------- -go4kRenderVoices: - push ecx ; // save current instrument counter -%if MAX_VOICES > 1 - push COM ; // save current instrument command index - push VAL ; // save current instrument values index -%endif - call go4k_VM_process ; // call synth vm for instrument voices - mov eax, dword [ecx+go4kENV_wrk.state] - cmp al, byte ENV_STATE_OFF - jne go4kRenderVoices_next - xor eax, eax - mov dword [ecx-4], eax ; // kill note if voice is done -go4kRenderVoices_next: -%if MAX_VOICES > 1 - pop VAL ; // restore instrument value index - pop COM ; // restore instrument command index -%endif +;------------------------------------------------------------------------------- +; OUTAUX opcode: outputs to main and aux1 outputs and pops the signal +;------------------------------------------------------------------------------- +; Mono: add outgain*ST0 to main left port and auxgain*ST0 to aux1 left +; Stereo: also add outgain*ST1 to main right port and auxgain*ST1 to aux1 right +;------------------------------------------------------------------------------- +section .su_op_outaux code align=1 +su_op_outaux: ; l r + mov edi, [esp + 24] + call su_op_outaux_mono + add edi, 4 +su_op_outaux_mono: + fld st0 ; l l + fmul dword [edx] ; g*l + fadd dword [edi + su_synthworkspace.left] ; g*l+o + fstp dword [edi + su_synthworkspace.left] ; o'=g*l+o + fmul dword [edx + 4] ; h*l + fadd dword [edi + su_synthworkspace.aux] ; h*l+a + fstp dword [edi + su_synthworkspace.aux] ; a'=h*l+a + ret -%ifdef GO4K_USE_BUFFER_RECORDINGS - mov eax, dword [esp+16] ; // get current tick - shr eax, 8 ; // every 256th sample = ~ 172 hz - shl eax, 5 ; // for 16 instruments a 2 voices - add eax, dword [esp] - add eax, dword [esp] ; // + 2*currentinstrument+0 -%ifdef GO4K_USE_ENVELOPE_RECORDINGS - mov edx, dword [ecx+go4kENV_wrk.level] - mov dword [__4klang_envelope_buffer+eax*4], edx -%endif -%ifdef GO4K_USE_NOTE_RECORDINGS - mov edx, dword [ecx-4] - mov dword [__4klang_note_buffer+eax*4], edx -%endif -%endif +;------------------------------------------------------------------------------- +; SEND opcode: adds the signal to a port +;------------------------------------------------------------------------------- +; Mono: adds signal to a memory address, defined by a word in VAL stream +; Stereo: also add right signal to the following address +;------------------------------------------------------------------------------- +section .su_op_send code align=1 +su_op_send: + lodsw + mov ecx, [esp + 12] ; load pointer to voice + pushf ; uh ugly: we save the flags just for the stereo carry bit. Doing the .CX loading later crashed the synth for stereo sends as loading the synth address from stack was f'd up by the "call su_op_send_mono" + test ah, 0x80 + jz su_op_send_skipglobal + mov ecx, [esp + 24 + 4] +su_op_send_skipglobal: + popf + test al, 0x8 ; if the SEND_POP bit is not set + jnz su_op_send_skippush + fld st0 ; duplicate the signal on stack: s s +su_op_send_skippush: ; there is signal s, but maybe also another: s (s) + fld dword [edx] ; a l (l) + fsub dword [FCONST_0_500000] ; a-.5 l (l) + fadd st0 ; g=2*a-1 l (l) + and ah, 0x7f ; eax = send address, clear the global bit + or al, 0x8 ; set the POP bit always, at the same time shifting to ports instead of wrk + fmulp st1, st0 ; g*l (l) + fadd dword [ecx + eax*4] ; g*l+L (l),where L is the current value + fstp dword [ecx + eax*4] ; (l) + ret -%if MAX_VOICES > 1 - call go4k_VM_process ; // call synth vm for instrument voices - mov eax, dword [ecx+go4kENV_wrk.state] - cmp al, byte ENV_STATE_OFF - jne go4k_render_instrument_next2 - xor eax, eax - mov dword [ecx-4], eax ; // kill note if voice is done -go4k_render_instrument_next2: +;------------------------------------------------------------------------------- +; ENVELOPE opcode: pushes an ADSR envelope value on stack [0,1] +;------------------------------------------------------------------------------- +; Mono: push the envelope value on stack +; Stereo: push the envelope valeu on stack twice +;------------------------------------------------------------------------------- +section .su_op_envelope code align=1 +su_op_envelope: + jnc su_op_envelope_mono + call su_op_envelope_mono + fld st0 + ret +su_op_envelope_mono: + mov eax, dword [edx-su_voice.inputs+su_voice.sustain] ; eax = su_instrument.sustain + test eax, eax ; if (eax != 0) + jne su_op_envelope_process ; goto process + mov al, 3 ; [state]=RELEASE + mov dword [ebp], eax ; note that mov al, XXX; mov ..., eax is less bytes than doing it directly +su_op_envelope_process: + mov eax, dword [ebp] ; al=[state] + fld dword [ebp+4] ; x=[level] + cmp al, 2 ; if (al==SUSTAIN) + je short su_op_envelope_leave2 ; goto leave2 +su_op_envelope_attac: + cmp al, 0 ; if (al!=ATTAC) + jne short su_op_envelope_decay ; goto decay + call su_nonlinear_map ; a x, where a=attack + faddp st1, st0 ; a+x + fld1 ; 1 a+x + fucomi st1 ; if (a+x<=1) // is attack complete? + fcmovnb st0, st1 ; a+x a+x + jbe short su_op_envelope_statechange ; else goto statechange +su_op_envelope_decay: + cmp al, 1 ; if (al!=DECAY) + jne short su_op_envelope_release ; goto release + call su_nonlinear_map ; d x, where d=decay + fsubp st1, st0 ; x-d + fld dword [edx + 8] ; s x-d, where s=sustain + fucomi st1 ; if (x-d>s) // is decay complete? + fcmovb st0, st1 ; x-d x-d + jnc short su_op_envelope_statechange ; else goto statechange +su_op_envelope_release: + cmp al, 3 ; if (al!=RELEASE) + jne short su_op_envelope_leave ; goto leave + call su_nonlinear_map ; r x, where r=release + fsubp st1, st0 ; x-r + fldz ; 0 x-r + fucomi st1 ; if (x-r>0) // is release complete? + fcmovb st0, st1 ; x-r x-r, then goto leave + jc short su_op_envelope_leave +su_op_envelope_statechange: + inc dword [ebp] ; [state]++ +su_op_envelope_leave: + fstp st1 ; x', where x' is the new value + fst dword [ebp+4] ; [level]=x' +su_op_envelope_leave2: + fmul dword [edx + 16] ; [gain]*x' + ret -%ifdef GO4K_USE_BUFFER_RECORDINGS - mov eax, dword [esp+16] ; // get current tick - shr eax, 8 ; // every 256th sample = ~ 172 hz - shl eax, 5 ; // for 16 instruments a 2 voices - add eax, dword [esp] - add eax, dword [esp] ; // + 2*currentinstrument+0 -%ifdef GO4K_USE_ENVELOPE_RECORDINGS - mov edx, dword [ecx+go4kENV_wrk.level] - mov dword [__4klang_envelope_buffer+eax*4+4], edx -%endif -%ifdef GO4K_USE_NOTE_RECORDINGS - mov edx, dword [ecx-4] - mov dword [__4klang_note_buffer+eax*4+4], edx -%endif -%endif +;------------------------------------------------------------------------------- +; NOISE opcode: creates noise +;------------------------------------------------------------------------------- +; Mono: push a random value [-1,1] value on stack +; Stereo: push two (differeent) random values on stack +;------------------------------------------------------------------------------- +section .su_op_noise code align=1 +su_op_noise: + lea ecx,[esp + 56] + imul eax, [ecx],16007 + mov [ecx],eax + fild dword [ecx] + fidiv dword [ICONST_2147483648] ; 65536*32768 + fld dword [edx] + call su_waveshaper + fmul dword [edx + 4] + ret -%endif - pop ecx ; // restore instrument counter - ret +;------------------------------------------------------------------------------- +; OSCILLAT opcode: oscillator, the heart of the synth +;------------------------------------------------------------------------------- +; Mono: push oscillator value on stack +; Stereo: push l r on stack, where l has opposite detune compared to r +;------------------------------------------------------------------------------- +section .su_op_oscillator code align=1 +su_op_oscillator: + lodsb ; load the flags + fld dword [edx + 4] ; e, where e is the detune [0,1] + fsub dword [FCONST_0_500000] ; e-.5 + fadd st0, st0 ; d=2*e-.5, where d is the detune [-1,1] + jnc su_op_oscillat_mono + fld st0 ; d d + add ebp, 4 ; move wrk... + call su_op_oscillat_mono ; r d + sub ebp, 4 ; ...restore wrk + fxch ; d r + fchs ; -d r, negate the detune for second round +su_op_oscillat_mono: + +pushad ; Stack: edi, esi, OscWRK, esp, ebx, edx, ecx, , retaddr_su_op_oscillator, edi, OperandStream, Voice, esp, OpcodeStream, Synth, DelayWorkSpace, eax, retaddr_su_run_vm, VoicesRemain, Sample, Row, GlobalTick, RandSeed, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr + fldz ; 0 d + fxch ; d a=0, "accumulated signal" +su_op_oscillat_unison_loop: + fst dword [esp] ; save the current detune, d. We could keep it in fpu stack but it was getting big. + call su_op_oscillat_single ; s a + faddp st1, st0 ; a+=s + test al, 3 + je su_op_oscillat_unison_out + add ebp, 8 ; this is ok after all, as there's a pop in the end of unison loop + fld dword [edx + 8] ; p s -%ifdef USE_SECTIONS -section .g4kcody code align=1 -%else -section .text -%endif -; //---------------------------------------------------------------------------------------- -; // the entry point for the synth -; //---------------------------------------------------------------------------------------- -%ifdef USE_SECTIONS -export_func _4klang_render@4 -%else -export_func _4klang_render -%endif - pushad - xor ecx, ecx -%ifdef GO4K_USE_BUFFER_RECORDINGS - push ecx -%endif -; loop all ticks -go4k_render_tickloop: - push ecx - xor ecx, ecx -; loop all samples per tick -go4k_render_sampleloop: - push ecx - xor ecx, ecx - mov ebx, go4k_synth_instructions ; // ebx = instrument command index - mov VAL, go4k_synth_parameter_values; // VAL = instrument values index - mov edi, _go4k_delay_buffer ; // get offset of first delay buffer - mov dword [_go4k_delay_buffer_ofs], edi ; // store offset in delaybuffer offset variable - mov edi, go4k_synth_wrk ; // edi = first instrument -; loop all instruments -go4k_render_instrumentloop: - mov eax, dword [esp] ; // eax = current tick sample - and eax, eax - jnz go4k_render_instrument_process ; // tick change? (first sample in current tick) - call go4kUpdateInstrument ; // update instrument state -; process instrument -go4k_render_instrument_process: - call go4kRenderVoices - inc ecx - cmp cl, byte MAX_INSTRUMENTS - jl go4k_render_instrumentloop - mov dword [edi+4], ecx ; // move a value != 0 into note slot, so processing will be done - call go4k_VM_process ; // call synth vm for synth -go4k_render_output_sample: -%ifdef GO4K_USE_BUFFER_RECORDINGS - inc dword [esp+8] - xchg esi, dword [esp+48] ; // edx = destbuffer -%else - xchg esi, dword [esp+44] ; // edx = destbuffer -%endif -%ifdef GO4K_CLIP_OUTPUT - fld dword [edi-8] - fld1 ; // 1 val - fucomi st1 ; // 1 val - jbe short go4k_render_clip1 - fchs ; // -1 val - fucomi st1 ; // -1 val - fcmovb st0, st1 ; // val -1 (if val > -1) -go4k_render_clip1: - fstp st1 ; // newval -%ifdef GO4K_USE_16BIT_OUTPUT - push eax - fmul dword [c_32767] - fistp dword [esp] - pop eax - mov word [esi],ax ; // store integer converted left sample - lodsw -%else - fstp dword [esi] ; // store left sample - lodsd -%endif - fld dword [edi-4] - fld1 ; // 1 val - fucomi st1 ; // 1 val - jbe short go4k_render_clip2 - fchs ; // -1 val - fucomi st1 ; // -1 val - fcmovb st0, st1 ; // val -1 (if val > -1) -go4k_render_clip2: - fstp st1 ; // newval -%ifdef GO4K_USE_16BIT_OUTPUT - push eax - fmul dword [c_32767] - fistp dword [esp] - pop eax - mov word [esi],ax ; // store integer converted right sample - lodsw -%else - fstp dword [esi] ; // store right sample - lodsd -%endif -%else - fld dword [edi-8] -%ifdef GO4K_USE_16BIT_OUTPUT - push eax - fmul dword [c_32767] - fistp dword [esp] - pop eax - mov word [esi],ax ; // store integer converted left sample - lodsw -%else - fstp dword [esi] ; // store left sample - lodsd -%endif - fld dword [edi-4] -%ifdef GO4K_USE_16BIT_OUTPUT - push eax - fmul dword [c_32767] - fistp dword [esp] - pop eax - mov word [esi],ax ; // store integer converted right sample - lodsw -%else - fstp dword [esi] ; // store right sample - lodsd -%endif -%endif -%ifdef GO4K_USE_BUFFER_RECORDINGS - xchg esi, dword [esp+48] -%else - xchg esi, dword [esp+44] -%endif - pop ecx - inc ecx -%ifdef GO4K_USE_GROOVE_PATTERN - mov ebx, dword SAMPLES_PER_TICK - mov eax, dword [esp] - and eax, 0x0f - bt dword [go4k_groove_pattern],eax - jnc go4k_render_nogroove - sub ebx, dword 3000 -go4k_render_nogroove: - cmp ecx, ebx -%else - cmp ecx, dword SAMPLES_PER_TICK -%endif - jl go4k_render_sampleloop - pop ecx - inc ecx -%ifdef AUTHORING - mov dword[__4klang_current_tick], ecx -%endif - cmp ecx, dword MAX_TICKS - jl go4k_render_tickloop -%ifdef GO4K_USE_BUFFER_RECORDINGS - pop ecx -%endif - popad - ret 4 + fadd dword [ICONST_1034594986] ; 1/12 p s, add some little phase offset to unison oscillators so they don't start in sync + fstp dword [edx + 8] ; s note that this changes the phase for second, possible stereo run. That's probably ok + fld dword [esp] ; d s + + fmul dword [FCONST_0_500000] ; .5*d s // negate and halve the detune of each oscillator + fchs ; -.5*d s // negate and halve the detune of each oscillator + dec eax + jmp short su_op_oscillat_unison_loop +su_op_oscillat_unison_out: + +popad ; Popped: eax = , ecx, edx, ebx, esp, ebp = OscWRK, esi, edi. Stack: retaddr_su_op_oscillator, edi, OperandStream, Voice, esp, OpcodeStream, Synth, DelayWorkSpace, eax, retaddr_su_run_vm, VoicesRemain, Sample, Row, GlobalTick, RandSeed, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr + ret +su_op_oscillat_single: + fld dword [edx] + fsub dword [FCONST_0_500000] + fdiv dword [FCONST_0_00781250] + faddp st1 + test al, byte 0x08 + jnz su_op_oscillat_skipnote + fiadd dword [edx-su_voice.inputs+su_voice.note] ; // st0 is note, st1 is t+d offset +su_op_oscillat_skipnote: + fmul dword [ICONST_1034594986] + call su_power + test al, byte 0x08 + jz short su_op_oscillat_normalize_note + fmul dword [FCONST_3_80000em05] ; // st0 is now frequency for lfo + jmp short su_op_oscillat_normalized +su_op_oscillat_normalize_note: + fmul dword [FCONST_9_269614em05] ; // st0 is now frequency +su_op_oscillat_normalized: + fadd dword [ebp] + fld1 ; we need to take mod(p,1) so the frequency does not drift as the float + fadd st1, st0 ; make no mistake: without this, there is audible drifts in oscillator pitch + fxch ; as the actual period changes once the phase becomes too big + fprem ; we actually computed mod(p+1,1) instead of mod(p,1) as the fprem takes mod + fstp st1 ; towards zero + fst dword [ebp] ; store back the updated phase + fadd dword [edx + 8] + fld1 ; this is a bit stupid, but we need to take mod(x,1) again after phase modulations + fadd st1, st0 ; as the actual oscillator functions expect x in [0,1] + fxch + fprem + fstp st1 + fld dword [edx + 12] ; // c p + ; every oscillator test included if needed + test al, byte 0x40 + jz short su_op_oscillat_notsine + call su_oscillat_sine +su_op_oscillat_notsine: + test al, byte 0x20 + jz short su_op_oscillat_not_trisaw + call su_oscillat_trisaw +su_op_oscillat_not_trisaw: +su_op_oscillat_shaping: + ; finally, shape the oscillator and apply gain + fld dword [edx + 16] + call su_waveshaper +su_op_oscillat_gain: + fmul dword [edx + 20] + ret + +section .su_oscillat_trisaw code align=1 +su_oscillat_trisaw: + fucomi st1 ; // c p + jnc short su_oscillat_trisaw_up + fld1 ; // 1 c p + fsubr st2, st0 ; // 1 c 1-p + fsubrp st1, st0 ; // 1-c 1-p +su_oscillat_trisaw_up: + fdivp st1, st0 ; // tp'/tc + fadd st0 ; // 2*'' + fld1 ; // 1 2*'' + fsubp st1, st0 ; // 2*''-1 + ret + +section .su_oscillat_sine code align=1 +su_oscillat_sine: + fucomi st1 ; // c p + jnc short su_oscillat_sine_do + fstp st1 + fsub st0, st0 ; // 0 + ret +su_oscillat_sine_do: + fdivp st1, st0 ; // p/c + fldpi ; // pi p + fadd st0 ; // 2*pi p + fmulp st1, st0 ; // 2*pi*p + fsin ; // sin(2*pi*p) + ret + +;------------------------------------------------------------------------------- +; IN opcode: inputs and clears a global port +;------------------------------------------------------------------------------- +; Mono: push the left channel of a global port (out or aux) +; Stereo: also push the right channel (stack in l r order) +;------------------------------------------------------------------------------- +section .su_op_in code align=1 +su_op_in: + lodsb + mov edi, [esp + 24] + xor ecx, ecx ; we cannot xor before jnc, so we have to do it mono & stereo. LAHF / SAHF could do it, but is the same number of bytes with more entropy + fld dword [edi + su_synthworkspace.right + eax*4] + mov dword [edi + su_synthworkspace.right + eax*4], ecx + fld dword [edi + su_synthworkspace.left + eax*4] + mov dword [edi + su_synthworkspace.left + eax*4], ecx + ret + + + +;------------------------------------------------------------------------------- +; su_nonlinear_map function: returns 2^(-24*x) of parameter number _AX +;------------------------------------------------------------------------------- +; Input: _AX : parameter number (e.g. for envelope: 0 = attac, 1 = decay...) +; INP : pointer to transformed operands +; Output: st0 : 2^(-24*x), where x is the parameter in the range 0-1 +;------------------------------------------------------------------------------- +section .su_nonlinear_map code align=1 +su_nonlinear_map: + fld dword [edx+eax*4] ; x, where x is the parameter in the range 0-1 + + fimul dword [ICONST_24] ; 24*x + fchs ; -24*x + + +;------------------------------------------------------------------------------- +; su_power function: computes 2^x +;------------------------------------------------------------------------------- +; Input: st0 : x +; Output: st0 : 2^x +;------------------------------------------------------------------------------- +global _su_pow@0 +_su_pow@0: +su_power: + fld1 ; 1 x + fld st1 ; x 1 x + fprem ; mod(x,1) 1 x + f2xm1 ; 2^mod(x,1)-1 1 x + faddp st1,st0 ; 2^mod(x,1) x + fscale ; 2^mod(x,1)*2^trunc(x) x + ; Equal to: + ; 2^x x + fstp st1 ; 2^x + ret + + +;------------------------------------------------------------------------------- +; DISTORT opcode: apply distortion on the signal +;------------------------------------------------------------------------------- +; Mono: x -> x*a/(1-a+(2*a-1)*abs(x)) where x is clamped first +; Stereo: l r -> l*a/(1-a+(2*a-1)*abs(l)) r*a/(1-a+(2*a-1)*abs(r)) +; This is placed here to be able to flow into waveshaper & also include +; wave shaper if needed by some other function; need to investigate the +; best way to do this +;------------------------------------------------------------------------------- +section .su_op_distort code align=1 +su_op_distort:call su_effects_stereohelper + fld dword [edx] + +su_waveshaper: + fld st0 ; a a x + + fsub dword [FCONST_0_500000] ; a-.5 a x + fadd st0 ; 2*a-1 a x + fld st2 ; x 2*a-1 a x + fabs ; abs(x) 2*a-1 a x + fmulp st1 ; (2*a-1)*abs(x) a x + fld1 ; 1 (2*a-1)*abs(x) a x + faddp st1 ; 1+(2*a-1)*abs(x) a x + fsub st1 ; 1-a+(2*a-1)*abs(x) a x + fdivp st1, st0 ; a/(1-a+(2*a-1)*abs(x)) x + fmulp st1 ; x*a/(1-a+(2*a-1)*abs(x)) + ret + +;------------------------------------------------------------------------------- +; su_effects_stereohelper: moves the workspace to next, does the filtering for +; right channel (pulling the calling address from stack), rewinds the +; workspace and returns +;------------------------------------------------------------------------------- +section .su_effects_stereohelper code align=1 +su_effects_stereohelper: + jnc su_effects_stereohelper_mono ; carry is still the stereo bit + add ebp, 16 + fxch ; r l + call [esp] ; call whoever called me... + fxch ; l r + sub ebp, 16 ; move WRK back to where it was +su_effects_stereohelper_mono: + ret ; return to process l/mono sound + + +section .su_clip code align=1 +su_clip: + fld1 ; 1 x a + fucomi st1 ; if (1 <= x) + jbe short su_clip_do ; goto Clip_Do + fchs ; -1 x a + fucomi st1 ; if (-1 < x) + fcmovb st0, st1 ; x x a +su_clip_do: + fstp st1 ; x' a, where x' = clamp(x) + ret + + +;------------------------------------------------------------------------------- +; The opcode table jump table. This is constructed to only include the opcodes +; that are used so that the jump table is as small as possible. +;------------------------------------------------------------------------------- +section .su_vm_jumptable data align=1 +su_vm_jumptable: + dd su_op_envelope + dd su_op_oscillator + dd su_op_distort + dd su_op_hold + dd su_op_mulp + dd su_op_noise + dd su_op_filter + dd su_op_addp + dd su_op_pan + dd su_op_delay + dd su_op_xch + dd su_op_outaux + dd su_op_send + dd su_op_compressor + dd su_op_in + dd su_op_out + +;------------------------------------------------------------------------------- +; The number of transformed parameters each opcode takes +;------------------------------------------------------------------------------- +section .su_vm_transformcounts data align=1 +su_vm_transformcounts: + db 5 + db 6 + db 1 + db 1 + db 0 + db 2 + db 2 + db 0 + db 1 + db 4 + db 0 + db 2 + db 1 + db 5 + db 0 + db 1 + + +;------------------------------------------------------------------------------- +; Patterns +;------------------------------------------------------------------------------- +section .su_patterns data align=1 +su_patterns: + db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + db 0,0,0,0,0,0,0,0,57,1,1,1,57,1,1,1 + db 1,1,1,1,57,1,1,1,1,1,1,1,57,1,1,1 + db 1,1,1,1,57,1,1,1,1,1,1,57,57,1,1,1 + db 1,1,1,1,57,57,57,57,1,1,1,1,57,1,1,1 + db 1,1,1,1,57,1,1,1,57,57,1,1,57,1,1,1 + db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 + db 1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0 + db 0,0,0,0,0,0,0,0,0,0,46,1,1,1,46,1 + db 46,1,1,1,1,1,1,1,1,1,46,1,1,1,1,1 + db 46,1,1,1,1,1,1,1,46,1,46,1,1,1,1,1 + db 46,1,1,1,1,1,1,1,46,46,46,46,1,1,1,1 + db 46,1,1,1,1,1,1,1,1,1,46,46,1,1,1,1 + db 48,1,48,1,1,1,48,1,48,1,48,1,1,1,48,1 + db 50,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 + db 48,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 + db 46,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 + db 1,1,1,1,1,1,1,1,1,1,48,1,50,1,53,1 + db 43,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 + db 1,1,1,1,1,1,1,1,43,1,45,1,46,1,48,1 + db 1,1,1,1,1,1,1,1,1,1,1,1,48,1,50,1 + db 52,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 + db 45,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 + db 1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0 + db 62,0,64,0,65,0,69,0,65,0,64,0,62,0,64,0 + db 65,0,72,0,65,0,64,0,62,0,64,0,65,0,71,0 + db 65,0,69,0,65,0,64,0,62,0,64,0,65,0,72,0 + db 50,0,50,1,50,0,50,1,50,0,50,1,50,0,50,1 + db 48,0,48,1,48,0,48,1,48,0,48,1,48,0,48,1 + db 46,0,46,1,46,0,46,1,46,0,46,1,46,0,46,1 + db 43,0,43,1,43,0,43,1,43,0,43,1,43,0,43,1 + db 43,0,43,1,43,0,43,1,43,0,43,1,43,0,43,0 + +;------------------------------------------------------------------------------- +; Tracks +;------------------------------------------------------------------------------- +section .su_tracks data align=1 +su_tracks: + db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,2,2,3,2,2,2,4,2,2,2,2,2,2,2,5,2,2,2,3,2,2,2,4,2,2,2,2,2,2,2,5,6,6,7,0 + db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,9,9,9,10,9,9,9,11,9,9,9,10,9,9,9,12,9,9,9,10,9,9,9,11,9,9,9,10,9,9,9,12,6,6,7,0 + db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,6,6,7,0 + db 14,6,15,6,16,17,18,19,16,20,21,22,14,6,6,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + db 24,25,24,26,24,25,24,26,24,25,24,26,24,25,24,26,24,25,24,26,24,25,24,26,24,25,24,26,24,25,24,26,24,25,24,26,24,25,24,26,24,25,24,26,24,25,24,26,0,0,0,0 + db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27,27,28,28,29,29,30,30,27,27,28,28,29,29,30,30,27,27,28,28,29,29,30,30,27,27,28,28,29,29,30,31,0,0,0,0 +;------------------------------------------------------------------------------- +; Delay times +;------------------------------------------------------------------------------- +section .su_delay_times data align=1 +su_delay_times: + dw 1116,1188,1276,1356,1422,1492,1556,1618,1140,1212,1300,1380,1446,1516,1580,1642,21381,12027 + + +;------------------------------------------------------------------------------- +; The code for this patch, basically indices to vm jump table +;------------------------------------------------------------------------------- +section .su_patch_opcodes data align=1 +su_patch_opcodes: + db 2,4,6,8,10,2,12,10,14,14,16,14,14,14,14,14,6,8,18,20,22,20,22,25,2,26,2,26,2,26,0,2,26,4,6,8,10,2,12,14,14,10,16,14,14,14,14,18,25,2,2,10,26,0,2,12,10,14,14,14,18,25,0,5,5,17,5,17,15,14,22,14,7,15,3,11,25,4,26,4,26,26,4,26,4,26,4,26,0,2,4,4,16,10,18,20,22,20,25,0,2,26,26,2,4,4,16,10,14,14,18,7,29,11,25,0,31,21,33,0 + +;------------------------------------------------------------------------------- +; The parameters / inputs to each opcode +;------------------------------------------------------------------------------- +section .su_patch_operands data align=1 +su_patch_operands: + db 22,64,0,0,38,77,64,19,128,66,70,64,78,128,0,68,0,0,70,2,128,97,128,68,108,128,68,100,128,68,19,98,16,15,128,80,40,74,80,23,42,16,97,128,59,27,128,112,0,0,15,27,128,112,0,8,15,128,0,38,49,0,0,128,70,40,0,47,54,0,0,128,19,152,0,54,68,70,0,128,26,200,0,39,71,0,0,128,7,0,149,88,64,42,115,114,54,33,58,100,28,45,0,0,128,84,102,93,63,68,75,87,64,11,66,68,53,128,80,7,76,16,13,54,16,64,128,0,71,128,74,0,128,49,71,121,0,128,0,56,0,4,53,0,0,128,102,128,87,128,16,95,128,80,89,71,32,61,45,0,64,87,64,128,65,128,34,64,60,87,128,64,128,35,64,69,0,128,106,128,66,62,91,64,103,26,88,85,31,88,96,12,128,88,25,59,106,60,64,29,21,64,64,4,128,64,128,40,52,25,0,41,42,55,54,63,128,72,75,33,0,59,43,0,74,64,0,128,64,128,72,69,73,0,40,64,0,128,64,128,72,71,120,0,40,64,0,128,64,128,72,86,152,0,39,62,0,30,71,88,62,14,53,27,72,64,88,66,64,113,64,128,64,64,71,128,86,0,16,1,73,128,90,0,17,1,84,52,39,81,0,0,128,87,144,0,90,107,0,33,57,67,57,128,76,64,0,98,64,101,64,64,48,85,78,97,64,35,39,82,64,15,128,68,64,80,48,56,87,46,54,85,35,2,50,128,114,77,0,15,128 + +;------------------------------------------------------------------------------- +; Constants +;------------------------------------------------------------------------------- +section .constants data align=1 +FCONST_32767_0 dd 0x46fffe00 +FCONST_0_00781250 dd 0x3c000000 +FCONST_0_500000 dd 0x3f000000 +FCONST_0_99609375 dd 0x3f7f0000 +FCONST_3_80000em05 dd 0x381f6230 +FCONST_9_269614em05 dd 0x38c265dc +ICONST_2147483648 dd 0x80000000 +ICONST_1034594986 dd 0x3daaaaaa +ICONST_24 dd 0x18 -%ifdef USE_SECTIONS -section .g4kcodz code align=1 -%else -section .text -%endif -; //---------------------------------------------------------------------------------------- -; // the magic behind it :) -; //---------------------------------------------------------------------------------------- -; // IN : edi = instrument pointer -; // IN : esi = instrumet values -; // IN : ebx = instrument instructions -; // OUT : -; // DIRTY : -; //---------------------------------------------------------------------------------------- -go4k_VM_process: - lea WRK, [edi+8] ; // get current workspace pointer - mov ecx, WRK ; // ecx = workspace start -go4k_VM_process_loop: - movzx eax, byte [ebx] ; // get command byte - inc ebx - test eax, eax - je go4k_VM_process_done ; // command byte = 0? so done - call dword [eax*4+go4k_synth_commands] - add WRK, MAX_UNIT_SLOTS*4 ; // go to next workspace slot - jmp short go4k_VM_process_loop -go4k_VM_process_done: - add edi, go4k_instrument.size ; // go to next instrument voice - ret \ No newline at end of file diff --git a/4klang.h b/4klang.h index 4f9a334..57fca4a 100644 --- a/4klang.h +++ b/4klang.h @@ -1,22 +1,43 @@ -// some useful song defines for 4klang -#define SAMPLE_RATE 44100 -#define BPM 165.000000 -#define MAX_INSTRUMENTS 6 -#define MAX_PATTERNS 25 -#define PATTERN_SIZE_SHIFT 5 -#define PATTERN_SIZE (1 << PATTERN_SIZE_SHIFT) -#define MAX_TICKS (MAX_PATTERNS*PATTERN_SIZE) -#define SAMPLES_PER_TICK 4009 -#define MAX_SAMPLES (SAMPLES_PER_TICK*MAX_TICKS) -#define POLYPHONY 1 -#define FLOAT_32BIT -#define SAMPLE_TYPE float +// auto-generated by Sointu, editing not recommended +#ifndef SU_RENDER_H +#define SU_RENDER_H -#define WINDOWS_OBJECT +#define SU_CHANNEL_COUNT 2 +#define SU_LENGTH_IN_SAMPLES 3335488 +#define SU_BUFFER_LENGTH (SU_LENGTH_IN_SAMPLES*SU_CHANNEL_COUNT) -// declaration of the external synth render function, you'll always need that -extern "C" void __stdcall _4klang_render(void*); -// declaration of the external envelope buffer. access only if you're song was exported with that option -extern "C" float _4klang_envelope_buffer; -// declaration of the external note buffer. access only if you're song was exported with that option -extern "C" int _4klang_note_buffer; +#define SU_SAMPLE_RATE 44100 +#define SU_BPM 165 +#define SU_ROWS_PER_BEAT 4 +#define SU_ROWS_PER_PATTERN 16 +#define SU_LENGTH_IN_PATTERNS 52 +#define SU_LENGTH_IN_ROWS (SU_LENGTH_IN_PATTERNS*SU_PATTERN_SIZE) +#define SU_SAMPLES_PER_ROW (SU_SAMPLE_RATE*60/(SU_BPM*SU_ROWS_PER_BEAT)) + +#include +#if UINTPTR_MAX == 0xffffffff + #if defined(__clang__) || defined(__GNUC__) + #define SU_CALLCONV __attribute__ ((stdcall)) + #elif defined(_WIN32) + #define SU_CALLCONV __stdcall + #endif +#else + #define SU_CALLCONV +#endif +typedef short SUsample; +#define SU_SAMPLE_RANGE 32767.0 +#define SU_SAMPLE_PCM16 +#define SU_SAMPLE_SIZE 2 + + +#ifdef __cplusplus +extern "C" { +#endif +void SU_CALLCONV su_render_song(SUsample *buffer); + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/4klang.inc b/4klang.inc index eae70c1..c1c0054 100644 --- a/4klang.inc +++ b/4klang.inc @@ -1,791 +1,27 @@ -%macro export_func 1 - global _%1 - _%1: -%endmacro -%define USE_SECTIONS -%define SAMPLE_RATE 44100 -%define MAX_INSTRUMENTS 6 -%define MAX_VOICES 1 -%define HLD 1 -%define BPM 165.000000 -%define MAX_PATTERNS 25 -%define PATTERN_SIZE_SHIFT 5 -%define PATTERN_SIZE (1 << PATTERN_SIZE_SHIFT) -%define MAX_TICKS (MAX_PATTERNS*PATTERN_SIZE) -%define SAMPLES_PER_TICK 4009 -%define DEF_LFO_NORMALIZE 0.0000623583 -%define MAX_SAMPLES (SAMPLES_PER_TICK*MAX_TICKS) -;%define GO4K_USE_16BIT_OUTPUT -;%define GO4K_USE_GROOVE_PATTERN -;%define GO4K_USE_ENVELOPE_RECORDINGS -;%define GO4K_USE_NOTE_RECORDINGS -%define GO4K_CLIP_OUTPUT -%define GO4K_USE_DST -%define GO4K_USE_DLL -%define GO4K_USE_PAN -%define GO4K_USE_GLOBAL_DLL -%define GO4K_USE_ENV_CHECK -%define GO4K_USE_VCO_CHECK -%define GO4K_USE_VCO_PHASE_OFFSET -%define GO4K_USE_VCO_SHAPE -%define GO4K_USE_VCO_MOD_TM -%define GO4K_USE_VCO_MOD_DM -%define GO4K_USE_VCO_MOD_CM -%define GO4K_USE_VCF_CHECK -%define GO4K_USE_VCF_MOD_FM -%define GO4K_USE_VCF_HIGH -%define GO4K_USE_VCF_BAND -%define GO4K_USE_VCF_PEAK -%define GO4K_USE_VCF_STEREO -%define GO4K_USE_DST_CHECK -%define GO4K_USE_DST_SH -%define GO4K_USE_DLL_CHORUS -%define GO4K_USE_DLL_CHORUS_CLAMP -%define GO4K_USE_DLL_DAMP -%define GO4K_USE_DLL_DC_FILTER -%define GO4K_USE_FSTG_CHECK -%define GO4K_USE_WAVESHAPER_CLIP -%define MAX_DELAY 65536 -%define MAX_UNITS 64 -%define MAX_UNIT_SLOTS 16 -%define GO4K_BEGIN_CMDDEF(def_name) -%define GO4K_END_CMDDEF db 0 -%define GO4K_BEGIN_PARAMDEF(def_name) -%define GO4K_END_PARAMDEF -GO4K_ENV_ID equ 1 -%macro GO4K_ENV 5 - db %1 - db %2 - db %3 - db %4 - db %5 -%endmacro -%define ATTAC(val) val -%define DECAY(val) val -%define SUSTAIN(val) val -%define RELEASE(val) val -%define GAIN(val) val -struc go4kENV_val - .attac resd 1 - .decay resd 1 - .sustain resd 1 - .release resd 1 - .gain resd 1 - .size -endstruc -struc go4kENV_wrk - .state resd 1 - .level resd 1 - .gm resd 1 - .am resd 1 - .dm resd 1 - .sm resd 1 - .rm resd 1 - .size -endstruc -%define ENV_STATE_ATTAC 0 -%define ENV_STATE_DECAY 1 -%define ENV_STATE_SUSTAIN 2 -%define ENV_STATE_RELEASE 3 -%define ENV_STATE_OFF 4 -GO4K_VCO_ID equ 2 -%macro GO4K_VCO 8 - db %1 - db %2 -%ifdef GO4K_USE_VCO_PHASE_OFFSET - db %3 -%endif -%ifdef GO4K_USE_VCO_GATE - db %4 -%endif - db %5 -%ifdef GO4K_USE_VCO_SHAPE - db %6 -%endif - db %7 - db %8 -%endmacro -%define TRANSPOSE(val) val -%define DETUNE(val) val -%define PHASE(val) val -%define GATES(val) val -%define COLOR(val) val -%define SHAPE(val) val -%define FLAGS(val) val -%define SINE 0x01 -%define TRISAW 0x02 -%define PULSE 0x04 -%define NOISE 0x08 -%define LFO 0x10 -%define GATE 0x20 -%define VCO_STEREO 0x40 -struc go4kVCO_val - .transpose resd 1 - .detune resd 1 -%ifdef GO4K_USE_VCO_PHASE_OFFSET - .phaseofs resd 1 -%endif -%ifdef GO4K_USE_VCO_GATE - .gate resd 1 -%endif - .color resd 1 -%ifdef GO4K_USE_VCO_SHAPE - .shape resd 1 -%endif - .gain resd 1 - .flags resd 1 - .size -endstruc -struc go4kVCO_wrk - .phase resd 1 - .tm resd 1 - .dm resd 1 - .fm resd 1 - .pm resd 1 - .cm resd 1 - .sm resd 1 - .gm resd 1 - .phase2 resd 1 - .size -endstruc -GO4K_VCF_ID equ 3 -%macro GO4K_VCF 3 - db %1 - db %2 - db %3 -%endmacro -%define LOWPASS 0x1 -%define HIGHPASS 0x2 -%define BANDPASS 0x4 -%define BANDSTOP 0x3 -%define ALLPASS 0x7 -%define PEAK 0x8 -%define STEREO 0x10 -%define FREQUENCY(val) val -%define RESONANCE(val) val -%define VCFTYPE(val) val -struc go4kVCF_val - .freq resd 1 - .res resd 1 - .type resd 1 - .size -endstruc -struc go4kVCF_wrk - .low resd 1 - .high resd 1 - .band resd 1 - .freq resd 1 - .fm resd 1 - .rm resd 1 - .low2 resd 1 - .high2 resd 1 - .band2 resd 1 - .size -endstruc -GO4K_DST_ID equ 4 -%macro GO4K_DST 3 - db %1 -%ifdef GO4K_USE_DST_SH - db %2 -%ifdef GO4K_USE_DST_STEREO - db %3 -%endif -%else -%ifdef GO4K_USE_DST_STEREO - db %3 -%endif -%endif -%endmacro -%define DRIVE(val) val -%define SNHFREQ(val) val -%define FLAGS(val) val -struc go4kDST_val - .drive resd 1 -%ifdef GO4K_USE_DST_SH - .snhfreq resd 1 -%endif - .flags resd 1 - .size -endstruc -struc go4kDST_wrk - .out resd 1 - .snhphase resd 1 - .dm resd 1 - .sm resd 1 - .out2 resd 1 - .size -endstruc -GO4K_DLL_ID equ 5 -%macro GO4K_DLL 8 - db %1 - db %2 - db %3 -%ifdef GO4K_USE_DLL_DAMP - db %4 -%endif -%ifdef GO4K_USE_DLL_CHORUS - db %5 - db %6 -%endif - db %7 - db %8 -%endmacro -%define PREGAIN(val) val -%define DRY(val) val -%define FEEDBACK(val) val -%define DEPTH(val) val -%define DAMP(val) val -%define DELAY(val) val -%define COUNT(val) val -struc go4kDLL_val - .pregain resd 1 - .dry resd 1 - .feedback resd 1 -%ifdef GO4K_USE_DLL_DAMP - .damp resd 1 -%endif -%ifdef GO4K_USE_DLL_CHORUS - .freq resd 1 - .depth -%endif - .delay resd 1 - .count resd 1 - .size -endstruc -struc go4kDLL_wrk - .index resd 1 - .store resd 1 - .dcin resd 1 - .dcout resd 1 -%ifdef GO4K_USE_DLL_CHORUS - .phase resd 1 -%endif - .buffer resd MAX_DELAY - .size -endstruc -struc go4kDLL_wrk2 - .pm resd 1 - .fm resd 1 - .im resd 1 - .dm resd 1 - .sm resd 1 - .am resd 1 - .size -endstruc -GO4K_FOP_ID equ 6 -%macro GO4K_FOP 1 - db %1 -%endmacro -%define OP(val) val -%define FOP_POP 0x1 -%define FOP_ADDP 0x2 -%define FOP_MULP 0x3 -%define FOP_PUSH 0x4 -%define FOP_XCH 0x5 -%define FOP_ADD 0x6 -%define FOP_MUL 0x7 -%define FOP_ADDP2 0x8 -%define FOP_LOADNOTE 0x9 -%define FOP_MULP2 0xa -struc go4kFOP_val - .flags resd 1 - .size -endstruc -struc go4kFOP_wrk - .size -endstruc -GO4K_FST_ID equ 7 -%macro GO4K_FST 2 - db %1 - dw %2 -%endmacro -%define AMOUNT(val) val -%define DEST(val) val -%define FST_SET 0x0000 -%define FST_ADD 0x4000 -%define FST_POP 0x8000 -struc go4kFST_val - .amount resd 1 - .op1 resd 1 - .size -endstruc -struc go4kFST_wrk - .size -endstruc -GO4K_PAN_ID equ 8 -%macro GO4K_PAN 1 -%ifdef GO4K_USE_PAN - db %1 -%endif -%endmacro -%define PANNING(val) val -struc go4kPAN_val -%ifdef GO4K_USE_PAN - .panning resd 1 -%endif - .size -endstruc -struc go4kPAN_wrk - .pm resd 1 - .size -endstruc -GO4K_OUT_ID equ 9 -%macro GO4K_OUT 2 - db %1 -%ifdef GO4K_USE_GLOBAL_DLL - db %2 -%endif -%endmacro -%define AUXSEND(val) val -struc go4kOUT_val - .gain resd 1 -%ifdef GO4K_USE_GLOBAL_DLL - .auxsend resd 1 -%endif - .size -endstruc -struc go4kOUT_wrk - .am resd 1 - .gm resd 1 - .size -endstruc -GO4K_ACC_ID equ 10 -%macro GO4K_ACC 1 - db %1 -%endmacro -%define OUTPUT 0 -%define AUX 8 -%define ACCTYPE(val) val -struc go4kACC_val - .acctype resd 1 - .size -endstruc -struc go4kACC_wrk - .size -endstruc -%ifdef GO4K_USE_FLD -GO4K_FLD_ID equ 11 -%macro GO4K_FLD 1 - db %1 -%endmacro -%define VALUE(val) val -struc go4kFLD_val - .value resd 1 - .size -endstruc -struc go4kFLD_wrk - .vm resd 1 - .size -endstruc -%endif -%ifdef GO4K_USE_GLITCH -GO4K_GLITCH_ID equ 12 -%macro GO4K_GLITCH 5 - db %1 - db %2 - db %3 - db %4 - db %5 -%endmacro -%define ACTIVE(val) val -%define SLICEFACTOR(val)val -%define PITCHFACTOR(val)val -%define SLICESIZE(val) val -struc go4kGLITCH_val - .active resd 1 - .dry resd 1 - .dsize resd 1 - .dpitch resd 1 - .slicesize resd 1 - .size -endstruc -struc go4kGLITCH_wrk - .index resd 1 - .store resd 1 - .slizesize resd 1 - .slicepitch resd 1 - .unused resd 1 - .buffer resd MAX_DELAY - .size -endstruc -struc go4kGLITCH_wrk2 - .am resd 1 - .dm resd 1 - .sm resd 1 - .pm resd 1 - .size -endstruc -%endif -%ifdef GO4K_USE_FSTG -GO4K_FSTG_ID equ 13 -%macro GO4K_FSTG 2 - db %1 - dw %2 -%endmacro -struc go4kFSTG_val - .amount resd 1 - .op1 resd 1 - .size -endstruc -struc go4kFSTG_wrk - .size -endstruc -%endif -struc go4k_instrument - .release resd 1 - .note resd 1 - .workspace resd MAX_UNITS*MAX_UNIT_SLOTS - .dlloutl resd 1 - .dlloutr resd 1 - .outl resd 1 - .outr resd 1 - .size -endstruc -struc go4k_synth - .instruments resb go4k_instrument.size * MAX_INSTRUMENTS * MAX_VOICES - .global resb go4k_instrument.size * MAX_VOICES - .size -endstruc -%ifdef USE_SECTIONS -section .g4kmuc1 data align=1 -%else -section .data align=1 -%endif -go4k_patterns - db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, HLD, HLD, HLD, 57, HLD, HLD, HLD, - db HLD, HLD, HLD, HLD, 57, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 57, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 57, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 57, HLD, HLD, HLD, - db HLD, HLD, HLD, HLD, 57, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 57, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 57, HLD, HLD, HLD, HLD, HLD, HLD, 57, 57, HLD, HLD, HLD, - db HLD, HLD, HLD, HLD, 57, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 57, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 57, 57, 57, 57, HLD, HLD, HLD, HLD, 57, HLD, HLD, HLD, - db HLD, HLD, HLD, HLD, 57, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 57, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 57, HLD, HLD, HLD, 57, 57, HLD, HLD, 57, HLD, HLD, HLD, - db HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, - db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, HLD, HLD, HLD, 46, HLD, - db 46, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 46, HLD, HLD, HLD, HLD, HLD, 46, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 46, HLD, HLD, HLD, HLD, HLD, - db 46, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 46, HLD, HLD, HLD, HLD, HLD, 46, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 46, HLD, 46, HLD, HLD, HLD, HLD, HLD, - db 46, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 46, HLD, HLD, HLD, HLD, HLD, 46, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 46, 46, 46, 46, HLD, HLD, HLD, HLD, - db 46, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 46, HLD, HLD, HLD, HLD, HLD, 46, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 46, 46, HLD, HLD, HLD, HLD, - db 48, HLD, 48, HLD, HLD, HLD, 48, HLD, 48, HLD, 48, HLD, HLD, HLD, 48, HLD, 48, HLD, 48, HLD, HLD, HLD, 48, HLD, 48, HLD, 48, HLD, HLD, HLD, 48, HLD, - db 50, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, - db 48, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, - db 46, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 48, HLD, 50, HLD, 53, HLD, - db 43, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 43, HLD, 45, HLD, 46, HLD, 48, HLD, - db 46, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 48, HLD, 50, HLD, - db 52, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 45, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, - db HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0, - db 62, 0, 64, 0, 65, 0, 69, 0, 65, 0, 64, 0, 62, 0, 64, 0, 65, 0, 72, 0, 65, 0, 64, 0, 62, 0, 64, 0, 65, 0, 71, 0, - db 62, 0, 64, 0, 65, 0, 69, 0, 65, 0, 64, 0, 62, 0, 64, 0, 65, 0, 69, 0, 65, 0, 64, 0, 62, 0, 64, 0, 65, 0, 72, 0, - db 50, 0, 50, HLD, 50, 0, 50, HLD, 50, 0, 50, HLD, 50, 0, 50, HLD, 50, 0, 50, HLD, 50, 0, 50, HLD, 50, 0, 50, HLD, 50, 0, 50, HLD, - db 48, 0, 48, HLD, 48, 0, 48, HLD, 48, 0, 48, HLD, 48, 0, 48, HLD, 48, 0, 48, HLD, 48, 0, 48, HLD, 48, 0, 48, HLD, 48, 0, 48, HLD, - db 46, 0, 46, HLD, 46, 0, 46, HLD, 46, 0, 46, HLD, 46, 0, 46, HLD, 46, 0, 46, HLD, 46, 0, 46, HLD, 46, 0, 46, HLD, 46, 0, 46, HLD, - db 43, 0, 43, HLD, 43, 0, 43, HLD, 43, 0, 43, HLD, 43, 0, 43, HLD, 43, 0, 43, HLD, 43, 0, 43, HLD, 43, 0, 43, HLD, 43, 0, 43, HLD, - db 43, 0, 43, HLD, 43, 0, 43, HLD, 43, 0, 43, HLD, 43, 0, 43, HLD, 43, 0, 43, HLD, 43, 0, 43, HLD, 43, 0, 43, HLD, 43, 0, 43, 0, -go4k_patterns_end -%ifdef USE_SECTIONS -section .g4kmuc2 data align=1 -%else -section .data -%endif -go4k_pattern_lists -Instrument0List db 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 4, 2, 2, 2, 5, 2, 3, 2, 4, 2, 2, 2, 5, 6, -Instrument1List db 0, 0, 0, 0, 0, 0, 0, 7, 8, 9, 8, 10, 8, 9, 8, 11, 8, 9, 8, 10, 8, 9, 8, 11, 6, -Instrument2List db 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 6, -Instrument3List db 13, 14, 15, 16, 17, 18, 13, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -Instrument4List db 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 0, -Instrument5List db 0, 0, 0, 0, 0, 0, 0, 0, 22, 23, 24, 25, 22, 23, 24, 25, 22, 23, 24, 25, 22, 23, 24, 26, 0, -go4k_pattern_lists_end -%ifdef USE_SECTIONS -section .g4kmuc3 data align=1 -%else -section .data -%endif -go4k_synth_instructions -GO4K_BEGIN_CMDDEF(Instrument0) - db GO4K_ENV_ID - db GO4K_VCO_ID - db GO4K_DST_ID - db GO4K_FOP_ID - db GO4K_ENV_ID - db GO4K_VCO_ID - db GO4K_FOP_ID - db GO4K_VCF_ID - db GO4K_VCF_ID - db GO4K_FOP_ID - db GO4K_VCF_ID - db GO4K_VCF_ID - db GO4K_VCF_ID - db GO4K_VCF_ID - db GO4K_VCF_ID - db GO4K_DST_ID - db GO4K_PAN_ID - db GO4K_DLL_ID - db GO4K_FOP_ID - db GO4K_DLL_ID - db GO4K_FOP_ID - db GO4K_OUT_ID - db GO4K_ENV_ID - db GO4K_FST_ID - db GO4K_ENV_ID - db GO4K_FST_ID - db GO4K_ENV_ID - db GO4K_FST_ID -GO4K_END_CMDDEF -GO4K_BEGIN_CMDDEF(Instrument1) - db GO4K_ENV_ID - db GO4K_VCO_ID - db GO4K_DST_ID - db GO4K_FOP_ID - db GO4K_ENV_ID - db GO4K_VCO_ID - db GO4K_VCF_ID - db GO4K_VCF_ID - db GO4K_FOP_ID - db GO4K_FOP_ID - db GO4K_VCF_ID - db GO4K_VCF_ID - db GO4K_VCF_ID - db GO4K_VCF_ID - db GO4K_PAN_ID - db GO4K_DLL_ID - db GO4K_FOP_ID - db GO4K_DLL_ID - db GO4K_OUT_ID - db GO4K_ENV_ID - db GO4K_ENV_ID - db GO4K_FOP_ID - db GO4K_FST_ID -GO4K_END_CMDDEF -GO4K_BEGIN_CMDDEF(Instrument2) - db GO4K_ENV_ID - db GO4K_VCO_ID - db GO4K_FOP_ID - db GO4K_VCF_ID - db GO4K_VCF_ID - db GO4K_VCF_ID - db GO4K_PAN_ID - db GO4K_DLL_ID - db GO4K_FOP_ID - db GO4K_DLL_ID - db GO4K_OUT_ID -GO4K_END_CMDDEF -GO4K_BEGIN_CMDDEF(Instrument3) - db GO4K_ENV_ID - db GO4K_VCO_ID - db GO4K_FST_ID - db GO4K_FOP_ID - db GO4K_VCO_ID - db GO4K_FST_ID - db GO4K_FST_ID - db GO4K_FOP_ID - db GO4K_VCO_ID - db GO4K_FST_ID - db GO4K_FOP_ID - db GO4K_VCO_ID - db GO4K_VCO_ID - db GO4K_VCO_ID - db GO4K_FOP_ID - db GO4K_FOP_ID - db GO4K_VCF_ID - db GO4K_VCF_ID - db GO4K_FOP_ID - db GO4K_PAN_ID - db GO4K_OUT_ID -GO4K_END_CMDDEF -GO4K_BEGIN_CMDDEF(Instrument4) - db GO4K_ENV_ID - db GO4K_VCO_ID - db GO4K_VCO_ID - db GO4K_FOP_ID - db GO4K_FOP_ID - db GO4K_PAN_ID - db GO4K_DLL_ID - db GO4K_FOP_ID - db GO4K_DLL_ID - db GO4K_OUT_ID -GO4K_END_CMDDEF -GO4K_BEGIN_CMDDEF(Instrument5) - db GO4K_ENV_ID - db GO4K_FST_ID - db GO4K_FST_ID - db GO4K_ENV_ID - db GO4K_VCO_ID - db GO4K_FOP_ID - db GO4K_VCO_ID - db GO4K_FOP_ID - db GO4K_VCF_ID - db GO4K_VCF_ID - db GO4K_PAN_ID - db GO4K_OUT_ID -GO4K_END_CMDDEF -GO4K_BEGIN_CMDDEF(Global) - db GO4K_ACC_ID - db GO4K_DLL_ID - db GO4K_FOP_ID - db GO4K_DLL_ID - db GO4K_FOP_ID - db GO4K_ACC_ID - db GO4K_FOP_ID - db GO4K_OUT_ID -GO4K_END_CMDDEF -go4k_synth_instructions_end -%ifdef USE_SECTIONS -section .g4kmuc4 data align=1 -%else -section .data -%endif -go4k_synth_parameter_values -GO4K_BEGIN_PARAMDEF(Instrument0) - GO4K_ENV ATTAC(22),DECAY(64),SUSTAIN(0),RELEASE(0),GAIN(38) - GO4K_VCO TRANSPOSE(77),DETUNE(64),PHASE(19),GATES(0),COLOR(128),SHAPE(66),GAIN(70),FLAGS(SINE) - GO4K_DST DRIVE(78), SNHFREQ(128), FLAGS(0) - GO4K_FOP OP(FOP_MULP) - GO4K_ENV ATTAC(0),DECAY(68),SUSTAIN(0),RELEASE(0),GAIN(70) - GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(0),COLOR(64),SHAPE(2),GAIN(128),FLAGS(NOISE) - GO4K_FOP OP(FOP_MULP) - GO4K_VCF FREQUENCY(97),RESONANCE(128),VCFTYPE(PEAK) - GO4K_VCF FREQUENCY(108),RESONANCE(128),VCFTYPE(PEAK) - GO4K_FOP OP(FOP_ADDP) - GO4K_VCF FREQUENCY(100),RESONANCE(128),VCFTYPE(PEAK) - GO4K_VCF FREQUENCY(19),RESONANCE(98),VCFTYPE(HIGHPASS) - GO4K_VCF FREQUENCY(15),RESONANCE(128),VCFTYPE(BANDSTOP) - GO4K_VCF FREQUENCY(40),RESONANCE(74),VCFTYPE(BANDSTOP) - GO4K_VCF FREQUENCY(23),RESONANCE(42),VCFTYPE(HIGHPASS) - GO4K_DST DRIVE(97), SNHFREQ(128), FLAGS(0) - GO4K_PAN PANNING(59) - GO4K_DLL PREGAIN(27),DRY(128),FEEDBACK(112),DAMP(0),FREQUENCY(7),DEPTH(70),DELAY(1),COUNT(8) - GO4K_FOP OP(FOP_XCH) - GO4K_DLL PREGAIN(27),DRY(128),FEEDBACK(112),DAMP(0),FREQUENCY(16),DEPTH(55),DELAY(9),COUNT(8) - GO4K_FOP OP(FOP_XCH) - GO4K_OUT GAIN(128), AUXSEND(0) - GO4K_ENV ATTAC(38),DECAY(49),SUSTAIN(0),RELEASE(0),GAIN(128) - GO4K_FST AMOUNT(70),DEST(1*MAX_UNIT_SLOTS+1+FST_SET+FST_POP) - GO4K_ENV ATTAC(47),DECAY(54),SUSTAIN(0),RELEASE(0),GAIN(128) - GO4K_FST AMOUNT(19),DEST(7*MAX_UNIT_SLOTS+4+FST_SET+FST_POP) - GO4K_ENV ATTAC(54),DECAY(68),SUSTAIN(70),RELEASE(0),GAIN(128) - GO4K_FST AMOUNT(26),DEST(10*MAX_UNIT_SLOTS+4+FST_SET+FST_POP) -GO4K_END_PARAMDEF -GO4K_BEGIN_PARAMDEF(Instrument1) - GO4K_ENV ATTAC(7),DECAY(73),SUSTAIN(0),RELEASE(0),GAIN(128) - GO4K_VCO TRANSPOSE(90),DETUNE(0),PHASE(10),GATES(0),COLOR(63),SHAPE(94),GAIN(128),FLAGS(TRISAW) - GO4K_DST DRIVE(58), SNHFREQ(100), FLAGS(0) - GO4K_FOP OP(FOP_ADDP) - GO4K_ENV ATTAC(28),DECAY(45),SUSTAIN(0),RELEASE(0),GAIN(128) - GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(0),COLOR(64),SHAPE(99),GAIN(128),FLAGS(NOISE) - GO4K_VCF FREQUENCY(80),RESONANCE(128),VCFTYPE(PEAK) - GO4K_VCF FREQUENCY(75),RESONANCE(87),VCFTYPE(LOWPASS) - GO4K_FOP OP(FOP_MULP) - GO4K_FOP OP(FOP_ADDP) - GO4K_VCF FREQUENCY(11),RESONANCE(128),VCFTYPE(PEAK) - GO4K_VCF FREQUENCY(53),RESONANCE(128),VCFTYPE(BANDSTOP) - GO4K_VCF FREQUENCY(12),RESONANCE(76),VCFTYPE(HIGHPASS) - GO4K_VCF FREQUENCY(11),RESONANCE(70),VCFTYPE(HIGHPASS) - GO4K_PAN PANNING(64) - GO4K_DLL PREGAIN(21),DRY(128),FEEDBACK(20),DAMP(128),FREQUENCY(6),DEPTH(51),DELAY(9),COUNT(8) - GO4K_FOP OP(FOP_XCH) - GO4K_DLL PREGAIN(20),DRY(128),FEEDBACK(20),DAMP(128),FREQUENCY(2),DEPTH(63),DELAY(1),COUNT(8) - GO4K_OUT GAIN(128), AUXSEND(0) - GO4K_ENV ATTAC(75),DECAY(128),SUSTAIN(0),RELEASE(0),GAIN(128) - GO4K_ENV ATTAC(49),DECAY(71),SUSTAIN(121),RELEASE(0),GAIN(128) - GO4K_FOP OP(FOP_MULP) - GO4K_FST AMOUNT(0),DEST(1*MAX_UNIT_SLOTS+1+FST_SET+FST_POP) -GO4K_END_PARAMDEF -GO4K_BEGIN_PARAMDEF(Instrument2) - GO4K_ENV ATTAC(4),DECAY(53),SUSTAIN(0),RELEASE(0),GAIN(128) - GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(0),COLOR(64),SHAPE(102),GAIN(128),FLAGS(NOISE) - GO4K_FOP OP(FOP_MULP) - GO4K_VCF FREQUENCY(87),RESONANCE(128),VCFTYPE(HIGHPASS) - GO4K_VCF FREQUENCY(95),RESONANCE(128),VCFTYPE(BANDSTOP) - GO4K_VCF FREQUENCY(89),RESONANCE(71),VCFTYPE(BANDPASS) - GO4K_PAN PANNING(61) - GO4K_DLL PREGAIN(28),DRY(128),FEEDBACK(50),DAMP(29),FREQUENCY(0),DEPTH(0),DELAY(1),COUNT(8) - GO4K_FOP OP(FOP_XCH) - GO4K_DLL PREGAIN(32),DRY(128),FEEDBACK(40),DAMP(0),FREQUENCY(10),DEPTH(34),DELAY(9),COUNT(8) - GO4K_OUT GAIN(45), AUXSEND(0) -GO4K_END_PARAMDEF -GO4K_BEGIN_PARAMDEF(Instrument3) - GO4K_ENV ATTAC(23),DECAY(85),SUSTAIN(113),RELEASE(38),GAIN(119) - GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(128),SHAPE(64),GAIN(128),FLAGS(TRISAW|LFO) - GO4K_FST AMOUNT(52),DEST(11*MAX_UNIT_SLOTS+2+FST_SET) - GO4K_FOP OP(FOP_POP) - GO4K_VCO TRANSPOSE(41),DETUNE(42),PHASE(55),GATES(85),COLOR(54),SHAPE(63),GAIN(128),FLAGS(SINE|LFO) - GO4K_FST AMOUNT(75),DEST(12*MAX_UNIT_SLOTS+2+FST_SET) - GO4K_FST AMOUNT(59),DEST(12*MAX_UNIT_SLOTS+5+FST_SET) - GO4K_FOP OP(FOP_POP) - GO4K_VCO TRANSPOSE(74),DETUNE(64),PHASE(0),GATES(85),COLOR(128),SHAPE(64),GAIN(128),FLAGS(SINE|LFO) - GO4K_FST AMOUNT(69),DEST(13*MAX_UNIT_SLOTS+2+FST_SET) - GO4K_FOP OP(FOP_POP) - GO4K_VCO TRANSPOSE(64),DETUNE(76),PHASE(22),GATES(85),COLOR(0),SHAPE(64),GAIN(128),FLAGS(TRISAW) - GO4K_VCO TRANSPOSE(64),DETUNE(60),PHASE(87),GATES(85),COLOR(128),SHAPE(64),GAIN(128),FLAGS(TRISAW) - GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(128),SHAPE(106),GAIN(128),FLAGS(SINE) - GO4K_FOP OP(FOP_ADDP) - GO4K_FOP OP(FOP_ADDP) - GO4K_VCF FREQUENCY(54),RESONANCE(120),VCFTYPE(LOWPASS|STEREO) - GO4K_VCF FREQUENCY(35),RESONANCE(70),VCFTYPE(BANDSTOP) - GO4K_FOP OP(FOP_MULP) - GO4K_PAN PANNING(63) - GO4K_OUT GAIN(71), AUXSEND(45) -GO4K_END_PARAMDEF -GO4K_BEGIN_PARAMDEF(Instrument4) - GO4K_ENV ATTAC(39),DECAY(62),SUSTAIN(0),RELEASE(30),GAIN(71) - GO4K_VCO TRANSPOSE(88),DETUNE(62),PHASE(14),GATES(0),COLOR(53),SHAPE(27),GAIN(72),FLAGS(SINE) - GO4K_VCO TRANSPOSE(88),DETUNE(66),PHASE(64),GATES(0),COLOR(113),SHAPE(64),GAIN(128),FLAGS(SINE) - GO4K_FOP OP(FOP_ADDP) - GO4K_FOP OP(FOP_MULP) - GO4K_PAN PANNING(64) - GO4K_DLL PREGAIN(95),DRY(128),FEEDBACK(83),DAMP(0),FREQUENCY(8),DEPTH(55),DELAY(17),COUNT(1) - GO4K_FOP OP(FOP_XCH) - GO4K_DLL PREGAIN(81),DRY(128),FEEDBACK(90),DAMP(0),FREQUENCY(11),DEPTH(48),DELAY(18),COUNT(1) - GO4K_OUT GAIN(65), AUXSEND(52) -GO4K_END_PARAMDEF -GO4K_BEGIN_PARAMDEF(Instrument5) - GO4K_ENV ATTAC(11),DECAY(99),SUSTAIN(0),RELEASE(0),GAIN(120) - GO4K_FST AMOUNT(83),DEST(8*MAX_UNIT_SLOTS+4+FST_SET) - GO4K_FST AMOUNT(93),DEST(6*MAX_UNIT_SLOTS+5+FST_SET+FST_POP) - GO4K_ENV ATTAC(42),DECAY(0),SUSTAIN(128),RELEASE(39),GAIN(128) - GO4K_VCO TRANSPOSE(76),DETUNE(64),PHASE(8),GATES(0),COLOR(128),SHAPE(64),GAIN(128),FLAGS(SINE) - GO4K_FOP OP(FOP_ADDP) - GO4K_VCO TRANSPOSE(64),DETUNE(71),PHASE(69),GATES(0),COLOR(73),SHAPE(75),GAIN(64),FLAGS(TRISAW) - GO4K_FOP OP(FOP_MULP) - GO4K_VCF FREQUENCY(39),RESONANCE(82),VCFTYPE(LOWPASS) - GO4K_VCF FREQUENCY(15),RESONANCE(128),VCFTYPE(PEAK) - GO4K_PAN PANNING(64) - GO4K_OUT GAIN(128), AUXSEND(40) -GO4K_END_PARAMDEF -GO4K_BEGIN_PARAMDEF(Global) - GO4K_ACC ACCTYPE(AUX) - GO4K_DLL PREGAIN(45),DRY(128),FEEDBACK(126),DAMP(79),FREQUENCY(7),DEPTH(68),DELAY(1),COUNT(8) - GO4K_FOP OP(FOP_XCH) - GO4K_DLL PREGAIN(45),DRY(128),FEEDBACK(124),DAMP(76),FREQUENCY(8),DEPTH(45),DELAY(9),COUNT(8) - GO4K_FOP OP(FOP_XCH) - GO4K_ACC ACCTYPE(OUTPUT) - GO4K_FOP OP(FOP_ADDP2) - GO4K_OUT GAIN(64), AUXSEND(0) -GO4K_END_PARAMDEF -go4k_synth_parameter_values_end -%ifdef USE_SECTIONS -section .g4kmuc5 data align=1 -%else -section .data -%endif -%ifdef GO4K_USE_DLL -global _go4k_delay_times -_go4k_delay_times - dw 0 - dw 1116 - dw 1188 - dw 1276 - dw 1356 - dw 1422 - dw 1492 - dw 1556 - dw 1618 - dw 1140 - dw 1212 - dw 1300 - dw 1380 - dw 1446 - dw 1516 - dw 1580 - dw 1642 - dw 21381 - dw 12027 -%endif +; auto-generated by Sointu, editing not recommended +%ifndef PLAYER_INC +%define PLAYER_INC + +%define SU_CHANNEL_COUNT 2 +%define SU_LENGTH_IN_SAMPLES 3335488 +%define SU_BUFFER_LENGTH (SU_LENGTH_IN_SAMPLES*SU_CHANNEL_COUNT) + +%define SU_SAMPLE_RATE 44100 +%define SU_BPM 165 +%define SU_ROWS_PER_BEAT 4 +%define SU_ROWS_PER_PATTERN 16 +%define SU_LENGTH_IN_PATTERNS 52 +%define SU_LENGTH_IN_ROWS (SU_LENGTH_IN_PATTERNS*SU_PATTERN_SIZE) +%define SU_SAMPLES_PER_ROW (SU_SAMPLE_RATE*60/(SU_BPM*SU_ROWS_PER_BEAT)) +%define SU_SAMPLE_SIZE 2 +%define SU_SAMPLE_RANGE 32767.0 +%define SU_SAMPLE_PCM16 + +_su_symbols: +%ifdef MANGLED + extern _su_render_song@4 +%else ; MANGLED + extern su_render_song +%endif ; MANGLED + +%endif ; PLAYER_INC diff --git a/4klang.yml b/4klang.yml new file mode 100644 index 0000000..26e270f --- /dev/null +++ b/4klang.yml @@ -0,0 +1,390 @@ +bpm: 165 +rowsperbeat: 4 +score: + tracks: + - numvoices: 1 + order: [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 2, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 4, 1, 1, 1, 2, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 4, -1, -1, 5, -1] + patterns: [[1, 1, 1, 1, 1, 1, 1, 1, 57, 1, 1, 1, 57, 1, 1, 1], [1, 1, 1, 1, 57, 1, 1, 1, 1, 1, 1, 1, 57, 1, 1, 1], [1, 1, 1, 1, 57, 1, 1, 1, 1, 1, 1, 57, 57, 1, 1, 1], [1, 1, 1, 1, 57, 57, 57, 57, 1, 1, 1, 1, 57, 1, 1, 1], [1, 1, 1, 1, 57, 1, 1, 1, 57, 57, 1, 1, 57, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1]] + - numvoices: 1 + order: [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 2, 1, 1, 1, 3, 1, 1, 1, 2, 1, 1, 1, 4, 1, 1, 1, 2, 1, 1, 1, 3, 1, 1, 1, 2, 1, 1, 1, 4, -1, -1, 5, -1] + patterns: [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 46, 1, 1, 1, 46, 1], [46, 1, 1, 1, 1, 1, 1, 1, 1, 1, 46, 1, 1, 1, 1, 1], [46, 1, 1, 1, 1, 1, 1, 1, 46, 1, 46, 1, 1, 1, 1, 1], [46, 1, 1, 1, 1, 1, 1, 1, 46, 46, 46, 46, 1, 1, 1, 1], [46, 1, 1, 1, 1, 1, 1, 1, 1, 1, 46, 46, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1]] + - numvoices: 1 + order: [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, 1, -1] + patterns: [[48, 1, 48, 1, 1, 1, 48, 1, 48, 1, 48, 1, 1, 1, 48, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1]] + - numvoices: 1 + order: [0, -1, 1, -1, 2, 3, 4, 5, 2, 6, 7, 8, 0, -1, -1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1] + patterns: [[50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [48, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [46, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 48, 1, 50, 1, 53, 1], [43, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 43, 1, 45, 1, 46, 1, 48, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 48, 1, 50, 1], [52, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [45, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1]] + - numvoices: 1 + order: [0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, -1, -1, -1, -1] + patterns: [[62, 0, 64, 0, 65, 0, 69, 0, 65, 0, 64, 0, 62, 0, 64, 0], [65, 0, 72, 0, 65, 0, 64, 0, 62, 0, 64, 0, 65, 0, 71, 0], [65, 0, 69, 0, 65, 0, 64, 0, 62, 0, 64, 0, 65, 0, 72, 0]] + - numvoices: 1 + order: [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 1, 1, 2, 2, 3, 3, 0, 0, 1, 1, 2, 2, 3, 3, 0, 0, 1, 1, 2, 2, 3, 3, 0, 0, 1, 1, 2, 2, 3, 4, -1, -1, -1, -1] + patterns: [[50, 0, 50, 1, 50, 0, 50, 1, 50, 0, 50, 1, 50, 0, 50, 1], [48, 0, 48, 1, 48, 0, 48, 1, 48, 0, 48, 1, 48, 0, 48, 1], [46, 0, 46, 1, 46, 0, 46, 1, 46, 0, 46, 1, 46, 0, 46, 1], [43, 0, 43, 1, 43, 0, 43, 1, 43, 0, 43, 1, 43, 0, 43, 1], [43, 0, 43, 1, 43, 0, 43, 1, 43, 0, 43, 1, 43, 0, 43, 0]] + rowsperpattern: 16 + length: 52 +patch: + - name: snare + numvoices: 1 + units: + - type: envelope + id: 1 + parameters: {attack: 22, decay: 64, gain: 38, release: 0, stereo: 0, sustain: 0} + - type: oscillator + id: 2 + parameters: {color: 128, detune: 64, gain: 70, lfo: 0, phase: 19, shape: 66, stereo: 0, transpose: 77, type: 0} + - type: distort + id: 3 + parameters: {drive: 78, stereo: 0} + - type: hold + id: 4 + parameters: {damp: 0, dry: 128, feedback: 96, holdfreq: 128, notetracking: 2, pregain: 40, stereo: 0} + - type: mulp + id: 5 + parameters: {panning: 64, stereo: 0} + - type: envelope + id: 6 + parameters: {attack: 0, auxgain: 64, decay: 68, gain: 70, outgain: 64, release: 0, stereo: 0, sustain: 0} + - type: noise + id: 81 + parameters: {gain: 128, shape: 2, stereo: 0} + - type: mulp + id: 82 + parameters: {stereo: 0} + - type: filter + id: 83 + parameters: {bandpass: 0, frequency: 97, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 128, stereo: 0} + - type: filter + id: 84 + parameters: {bandpass: 0, frequency: 108, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 128, stereo: 0} + - type: addp + id: 85 + parameters: {stereo: 0} + - type: filter + id: 86 + parameters: {bandpass: 0, frequency: 100, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 128, stereo: 0} + - type: filter + id: 87 + parameters: {bandpass: 0, frequency: 19, highpass: 1, lowpass: 0, negbandpass: 0, neghighpass: 0, resonance: 98, stereo: 0} + - type: filter + id: 88 + parameters: {bandpass: 0, frequency: 15, highpass: 1, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 128, stereo: 0} + - type: filter + id: 89 + parameters: {bandpass: 0, frequency: 40, highpass: 1, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 74, stereo: 0} + - type: filter + id: 90 + parameters: {bandpass: 0, frequency: 23, highpass: 1, lowpass: 0, negbandpass: 0, neghighpass: 0, resonance: 42, stereo: 0} + - type: distort + id: 91 + parameters: {drive: 97, stereo: 0} + - type: hold + id: 92 + parameters: {holdfreq: 128, stereo: 0} + - type: pan + id: 93 + parameters: {panning: 59, stereo: 0} + - type: delay + id: 94 + parameters: {damp: 0, dry: 128, feedback: 112, notetracking: 0, pregain: 27, stereo: 0} + varargs: [1116, 1188, 1276, 1356, 1422, 1492, 1556, 1618] + - type: xch + id: 95 + parameters: {stereo: 0} + - type: delay + id: 96 + parameters: {damp: 0, dry: 128, feedback: 112, notetracking: 0, pregain: 27, stereo: 0} + varargs: [1140, 1212, 1300, 1380, 1446, 1516, 1580, 1642] + - type: xch + id: 97 + parameters: {stereo: 0} + - type: outaux + id: 98 + parameters: {auxgain: 0, outgain: 128, stereo: 1} + - type: envelope + id: 99 + parameters: {attack: 38, decay: 49, gain: 128, release: 0, stereo: 0, sustain: 0} + - type: send + id: 100 + parameters: {amount: 70, port: 0, sendpop: 1, target: 2} + - type: envelope + id: 101 + parameters: {attack: 47, decay: 54, gain: 128, release: 0, stereo: 0, sustain: 0} + - type: send + id: 102 + parameters: {amount: 19, port: 0, sendpop: 1, target: 83} + - type: envelope + id: 103 + parameters: {attack: 54, decay: 68, gain: 128, release: 0, stereo: 0, sustain: 70} + - type: send + id: 104 + parameters: {amount: 26, port: 0, sendpop: 1, target: 86} + - name: kick + numvoices: 1 + units: + - type: envelope + id: 105 + parameters: {attack: 39, channel: 2, decay: 71, gain: 128, release: 0, stereo: 0, sustain: 0} + - type: send + id: 205 + parameters: {amount: 7, damp: 64, dry: 128, feedback: 125, notetracking: 0, port: 0, pregain: 40, sendpop: 0, stereo: 0, target: 184, unit: 0, voice: 0} + - type: oscillator + id: 106 + parameters: {color: 115, detune: 64, gain: 54, lfo: 0, phase: 42, shape: 114, stereo: 0, transpose: 88, type: 1, unison: 1} + - type: distort + id: 107 + parameters: {drive: 58, stereo: 0} + - type: hold + id: 108 + parameters: {holdfreq: 100, stereo: 0} + - type: mulp + id: 126 + parameters: {stereo: 0} + - type: envelope + id: 110 + parameters: {attack: 28, decay: 45, gain: 128, release: 0, stereo: 0, sustain: 0} + - type: noise + id: 111 + parameters: {gain: 102, shape: 84, stereo: 0} + - type: filter + id: 112 + parameters: {bandpass: 0, frequency: 93, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 63, stereo: 0} + - type: filter + id: 113 + parameters: {bandpass: 0, frequency: 75, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 87, stereo: 0} + - type: mulp + id: 10 + parameters: {stereo: 0} + - type: addp + id: 127 + parameters: {stereo: 0} + - type: filter + id: 12 + parameters: {bandpass: 0, frequency: 11, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 66, stereo: 0} + - type: filter + id: 13 + parameters: {bandpass: 0, frequency: 53, highpass: 1, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 128, stereo: 0} + - type: filter + id: 14 + parameters: {bandpass: 0, frequency: 7, highpass: 1, lowpass: 0, negbandpass: 0, neghighpass: 0, resonance: 76, stereo: 0} + - type: filter + id: 15 + parameters: {bandpass: 0, frequency: 13, highpass: 1, lowpass: 0, negbandpass: 0, neghighpass: 0, resonance: 54, stereo: 0} + - type: pan + id: 114 + parameters: {panning: 64, stereo: 0} + - type: outaux + id: 118 + parameters: {auxgain: 0, outgain: 128, stereo: 1} + - type: envelope + id: 119 + parameters: {attack: 71, decay: 128, gain: 128, release: 0, stereo: 0, sustain: 74} + - type: envelope + id: 120 + parameters: {attack: 49, decay: 71, gain: 128, release: 0, stereo: 0, sustain: 121} + - type: mulp + id: 121 + parameters: {stereo: 0} + - type: send + id: 122 + parameters: {amount: 0, port: 0, sendpop: 1, target: 106} + - name: hihat + numvoices: 1 + units: + - type: envelope + id: 128 + parameters: {attack: 4, decay: 53, gain: 128, release: 0, stereo: 0, sustain: 0} + - type: noise + id: 129 + parameters: {gain: 128, shape: 102, stereo: 0} + - type: mulp + id: 130 + parameters: {stereo: 0} + - type: filter + id: 131 + parameters: {bandpass: 0, frequency: 87, highpass: 1, lowpass: 0, negbandpass: 0, neghighpass: 0, resonance: 128, stereo: 0} + - type: filter + id: 132 + parameters: {bandpass: 0, frequency: 95, highpass: 1, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 128, stereo: 0} + - type: filter + id: 133 + parameters: {bandpass: 1, frequency: 89, highpass: 0, lowpass: 0, negbandpass: 0, neghighpass: 0, resonance: 71, stereo: 0} + - type: pan + id: 134 + parameters: {panning: 61, stereo: 0} + - type: outaux + id: 11 + parameters: {auxgain: 0, outgain: 45, stereo: 1} + - name: ba_reese + numvoices: 1 + units: + - type: oscillator + id: 159 + parameters: {color: 128, detune: 87, gain: 128, lfo: 0, phase: 64, shape: 65, stereo: 1, transpose: 64, type: 1, unison: 2} + - type: oscillator + id: 160 + parameters: {color: 128, detune: 60, gain: 128, lfo: 0, phase: 87, shape: 64, stereo: 1, transpose: 64, type: 1, unison: 3} + - type: addp + id: 162 + parameters: {stereo: 1} + - type: oscillator + id: 161 + parameters: {color: 128, detune: 69, gain: 128, lfo: 0, phase: 0, shape: 106, stereo: 1, transpose: 64, type: 0, unison: 2} + - type: addp + id: 16 + parameters: {stereo: 1} + - type: filter + id: 17 + parameters: {bandpass: 0, frequency: 62, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 91, stereo: 1} + - type: filter + id: 190 + parameters: {bandpass: 0, frequency: 103, highpass: 1, lowpass: 1, negbandpass: 1, neghighpass: 0, resonance: 26, stereo: 0} + - type: xch + id: 195 + parameters: {stereo: 0} + - type: filter + id: 196 + parameters: {bandpass: 0, frequency: 85, highpass: 1, lowpass: 1, negbandpass: 1, neghighpass: 0, resonance: 31, stereo: 0} + - type: distort + id: 193 + parameters: {drive: 96, stereo: 1} + - type: filter + id: 194 + parameters: {bandpass: 0, frequency: 12, highpass: 1, lowpass: 1, negbandpass: 1, neghighpass: 0, resonance: 128, stereo: 1} + - type: envelope + id: 189 + parameters: {attack: 25, decay: 59, gain: 64, release: 60, stereo: 1, sustain: 106} + - type: mulp + id: 206 + parameters: {stereo: 1} + - type: outaux + id: 21 + parameters: {auxgain: 21, outgain: 29, stereo: 1} + - type: oscillator + id: 149 + parameters: {color: 128, detune: 64, gain: 128, lfo: 1, phase: 4, shape: 64, stereo: 0, transpose: 64, type: 1} + - type: send + id: 150 + parameters: {amount: 52, port: 1, sendpop: 1, stereo: 0, target: 159} + - type: oscillator + id: 152 + parameters: {color: 54, detune: 42, gain: 128, lfo: 1, phase: 55, shape: 63, stereo: 0, transpose: 41, type: 0} + - type: send + id: 153 + parameters: {amount: 75, port: 1, sendpop: 0, stereo: 0, target: 160} + - type: send + id: 154 + parameters: {amount: 59, port: 3, sendpop: 1, stereo: 0, target: 160} + - type: oscillator + id: 156 + parameters: {color: 128, detune: 64, gain: 128, lfo: 1, phase: 0, shape: 64, stereo: 0, transpose: 74, type: 0} + - type: send + id: 157 + parameters: {amount: 69, port: 1, sendpop: 1, stereo: 0, target: 161} + - type: oscillator + id: 191 + parameters: {color: 128, detune: 64, gain: 128, lfo: 1, phase: 0, shape: 64, stereo: 0, transpose: 40, type: 0, unison: 0} + - type: send + id: 192 + parameters: {amount: 71, port: 0, sendpop: 1, stereo: 0, target: 190, unit: 0, voice: 0} + - type: oscillator + id: 197 + parameters: {color: 128, detune: 64, gain: 128, lfo: 1, phase: 0, shape: 64, stereo: 0, transpose: 40, type: 0} + - type: send + id: 198 + parameters: {amount: 86, port: 0, sendpop: 1, stereo: 0, target: 196, unit: 0, voice: 0} + - name: ky_sine + numvoices: 1 + units: + - type: envelope + id: 163 + parameters: {attack: 39, decay: 62, gain: 71, release: 30, stereo: 0, sustain: 0} + - type: oscillator + id: 164 + parameters: {color: 53, detune: 62, gain: 72, lfo: 0, phase: 14, shape: 27, stereo: 0, transpose: 88, type: 0} + - type: oscillator + id: 165 + parameters: {color: 113, detune: 66, gain: 128, lfo: 0, phase: 64, shape: 64, stereo: 0, transpose: 88, type: 0} + - type: addp + id: 166 + parameters: {stereo: 0} + - type: mulp + id: 167 + parameters: {stereo: 0} + - type: pan + id: 168 + parameters: {panning: 64, stereo: 0} + - type: delay + id: 169 + parameters: {damp: 0, dry: 128, feedback: 86, notetracking: 2, pregain: 71, stereo: 0} + varargs: [64] + - type: xch + id: 170 + parameters: {stereo: 0} + - type: delay + id: 171 + parameters: {damp: 0, dry: 128, feedback: 90, notetracking: 2, pregain: 73, stereo: 0} + varargs: [36] + - type: outaux + id: 172 + parameters: {auxgain: 52, outgain: 84, stereo: 1} + - name: ba_trana + numvoices: 1 + units: + - type: envelope + id: 173 + parameters: {attack: 39, decay: 81, gain: 128, release: 0, stereo: 0, sustain: 0} + - type: send + id: 174 + parameters: {amount: 87, port: 0, sendpop: 0, target: 181} + - type: send + id: 175 + parameters: {amount: 90, port: 3, sendpop: 1, target: 179} + - type: envelope + id: 176 + parameters: {attack: 33, decay: 57, gain: 128, release: 57, stereo: 0, sustain: 67} + - type: oscillator + id: 177 + parameters: {color: 98, detune: 64, gain: 101, lfo: 0, phase: 0, shape: 64, stereo: 0, transpose: 76, type: 0, unison: 0} + - type: oscillator + id: 179 + parameters: {color: 78, detune: 48, gain: 64, lfo: 0, phase: 85, shape: 97, stereo: 0, transpose: 64, type: 1, unison: 3} + - type: addp + id: 178 + parameters: {stereo: 0} + - type: mulp + id: 185 + parameters: {stereo: 0} + - type: filter + id: 181 + parameters: {bandpass: 0, frequency: 39, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 82, stereo: 0} + - type: filter + id: 182 + parameters: {bandpass: 0, frequency: 15, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 128, stereo: 0} + - type: pan + id: 183 + parameters: {panning: 64, stereo: 0} + - type: distort + id: 200 + parameters: {drive: 80, stereo: 1} + - type: compressor + id: 186 + parameters: {attack: 48, invgain: 87, ratio: 54, release: 56, stereo: 1, threshold: 46} + - type: mulp + id: 187 + parameters: {stereo: 1} + - type: outaux + id: 184 + parameters: {auxgain: 35, outgain: 85, stereo: 1} + - name: Global + numvoices: 1 + units: + - type: in + id: 7 + parameters: {channel: 2, stereo: 1} + - type: delay + id: 8 + parameters: {damp: 77, dry: 128, feedback: 114, notetracking: 0, pregain: 50, stereo: 1} + varargs: [1116, 1188, 1276, 1356, 1422, 1492, 1556, 1618, 1140, 1212, 1300, 1380, 1446, 1516, 1580, 1642] + - type: out + id: 9 + parameters: {gain: 128, stereo: 1} diff --git a/8klang.merge b/8klang.merge deleted file mode 100644 index 86882fc..0000000 Binary files a/8klang.merge and /dev/null differ diff --git a/BuildDLLs.script b/BuildDLLs.script index 57eabff..3abc40d 100644 --- a/BuildDLLs.script +++ b/BuildDLLs.script @@ -32,9 +32,9 @@ include common.script # Audio debug/test DLL -debug_4klang.obj: debug_4klang_wrapper.asm 4klang.asm 4klang.inc - $(ASSEMBLER_EXE) $(ASSEMBLER_OPTS) $(FORCED_4KLANG_OPTS) -o $@ $< - +debug_4klang.obj: debug_4klang_wrapper.asm sointu.asm sointu.inc + $(ASSEMBLER_EXE) $(ASSEMBLER_OPTS) $(FORCED_4KLANG_OPTS) -o $@ $< + debug_audio.obj: debug_audio.asm errormsg.inc $(ASSEMBLER_EXE) $(ASSEMBLER_OPTS) -o $@ $< diff --git a/_4klang.asm b/_4klang.asm new file mode 100644 index 0000000..a43f2b9 --- /dev/null +++ b/_4klang.asm @@ -0,0 +1,1758 @@ +bits 32 + +%define WRK ebp ; // alias for unit workspace +%define VAL esi ; // alias for unit values (transformed/untransformed) +%define COM ebx ; // alias for instrument opcodes + +%include "4klang.inc" + +;// conditional defines + +%ifdef GO4K_USE_VCO_SHAPE + %define INCLUDE_WAVESHAPER +%endif +%ifdef GO4K_USE_DST + %define INCLUDE_WAVESHAPER +%endif + +%ifdef GO4K_USE_VCO_MOD_PM + %define PHASE_RENORMALIZE +%endif +%ifdef GO4K_USE_VCO_PHASE_OFFSET + %define PHASE_RENORMALIZE +%endif + +%ifdef GO4K_USE_ENVELOPE_RECORDINGS + %define GO4K_USE_BUFFER_RECORDINGS +%endif +%ifdef GO4K_USE_NOTE_RECORDINGS + %define GO4K_USE_BUFFER_RECORDINGS +%endif + +; //======================================================================================== +; // .bss section +; //======================================================================================== +%ifdef USE_SECTIONS +section .g4kbss1 bss align=1 +%else +section .bss +%endif + +; // the one and only synth object +%if MAX_VOICES > 1 +go4k_voiceindex resd 16 +%endif +go4k_transformed_values resd 16 +go4k_synth_wrk resb go4k_synth.size +global _go4k_delay_buffer_ofs +_go4k_delay_buffer_ofs resd 1 +global _go4k_delay_buffer +_go4k_delay_buffer resd 16*16*go4kDLL_wrk.size + +%ifdef AUTHORING +global __4klang_current_tick +__4klang_current_tick resd 0 +%endif + +%ifdef GO4K_USE_ENVELOPE_RECORDINGS +global __4klang_envelope_buffer +__4klang_envelope_buffer resd ((MAX_SAMPLES)/8) ; // samples every 256 samples and stores 16*2 = 32 values +%endif +%ifdef GO4K_USE_NOTE_RECORDINGS +global __4klang_note_buffer +__4klang_note_buffer resd ((MAX_SAMPLES)/8) ; // samples every 256 samples and stores 16*2 = 32 values +%endif + +; //======================================================================================== +; // .g4kdat section (initialized data for go4k) +; //======================================================================================== +%ifdef USE_SECTIONS +section .g4kdat1 data align=1 +%else +section .data +%endif + +; // some synth constants +go4k_synth_commands dd 0 + dd _go4kENV_func@0 + dd _go4kVCO_func@0 + dd _go4kVCF_func@0 + dd _go4kDST_func@0 + dd _go4kDLL_func@0 + dd _go4kFOP_func@0 + dd _go4kFST_func@0 + dd _go4kPAN_func@0 + dd _go4kOUT_func@0 + dd _go4kACC_func@0 + dd _go4kFLD_func@0 +%ifdef GO4K_USE_GLITCH + dd _go4kGLITCH_func@0 +%else + dd _go4kFLD_func@0 +%endif +%ifdef GO4K_USE_FSTG + dd _go4kFSTG_func@0 +%endif + +%ifdef USE_SECTIONS +section .g4kdat2 data align=1 +%else +section .data +%endif + +%ifdef GO4K_USE_16BIT_OUTPUT +c_32767 dd 32767.0 +%endif +c_i128 dd 0.0078125 +c_RandDiv dd 65536*32768 +c_0_5 dd 0.5 +%ifdef GO4K_USE_VCO_GATE +c_16 dd 16.0 +%endif +%ifdef GO4K_USE_DLL_CHORUS +DLL_DEPTH dd 1024.0 +%endif +%ifdef GO4K_USE_DLL_DC_FILTER +c_dc_const dd 0.99609375 ; R = 1 - (pi*2 * frequency /samplerate) +%else + %ifdef GO4K_USE_VCO_GATE +c_dc_const dd 0.99609375 ; R = 1 - (pi*2 * frequency /samplerate) + %endif +%endif +global _RandSeed +_RandSeed dd 1 +c_24 dd 24 +c_i12 dd 0x3DAAAAAA +FREQ_NORMALIZE dd 0.000092696138 ; // 220.0/(2^(69/12)) / 44100.0 +global _LFO_NORMALIZE +_LFO_NORMALIZE dd DEF_LFO_NORMALIZE +%ifdef GO4K_USE_GROOVE_PATTERN +go4k_groove_pattern dw 0011100111001110b +%endif + +; //======================================================================================== +; // .crtemui section (emulates crt functions) +; //======================================================================================== +%ifdef USE_SECTIONS +section .crtemui code align=1 +%else +section .text +%endif + +export_func FloatRandomNumber@0 + push eax + imul eax,dword [_RandSeed],16007 + mov dword [_RandSeed], eax + fild dword [_RandSeed] + fidiv dword [c_RandDiv] + pop eax + ret + +; //======================================================================================== +; // .g4kcod* sections (code for go4k) +; //======================================================================================== + +%ifdef INCLUDE_WAVESHAPER + +%ifdef USE_SECTIONS +section .g4kcod2 code align=1 +%else +section .text +%endif +; //---------------------------------------------------------------------------------------- +; // Waveshaper function +; //---------------------------------------------------------------------------------------- +; // Input : st0 : shaping coeff +; // st1 : input +; // Output: st0 : result +; //---------------------------------------------------------------------------------------- + +go4kWaveshaper: +%ifdef GO4K_USE_WAVESHAPER_CLIP + fxch + fld1 ; // 1 val + fucomi st1 ; // 1 val + jbe short go4kWaveshaper_clip + fchs ; // -1 val + fucomi st1 ; // -1 val + fcmovb st0, st1 ; // val -1 (if val > -1) +go4kWaveshaper_clip: + fstp st1 ; // newval + fxch +%endif + fsub dword [c_0_5] + fadd st0 + fst dword [esp-4] ; // amnt in + fadd st0 ; // 2*amnt in + fld1 ; // 1 2*amnt in + fsub dword [esp-4] ; // 1-amnt 2*amnt in + fdivp st1, st0 ; // k in + fld st1 ; // sin k in + fabs ; // a(in) k in + fmul st1 ; // k*a(in) k in + fld1 + faddp st1, st0 ; // 1+k*a(in) k in + fxch st1 ; // k 1+k*a(in) in + fld1 + faddp st1, st0 ; // 1+k 1+k*a(in) in + fmulp st2 ; // 1+k*a(in) (1+k)*in + fdivp st1, st0 ; // out + ret + +%endif + +%ifdef USE_SECTIONS +section .g4kcod3 code align=1 +%else +section .text +%endif +; //---------------------------------------------------------------------------------------- +; // unit values preparation/transform +; //---------------------------------------------------------------------------------------- +go4kTransformValues: + push ecx + xor ecx, ecx + xor eax, eax + mov edx, go4k_transformed_values +go4kTransformValues_loop: + lodsb + push eax + fild dword [esp] + fmul dword [c_i128] + fstp dword [edx+ecx*4] + pop eax + inc ecx + cmp ecx, dword [esp+8] + jl go4kTransformValues_loop + pop ecx + ret 4 + +%ifdef USE_SECTIONS +section .g4kcod4 code align=1 +%else +section .text +%endif +; //---------------------------------------------------------------------------------------- +; // Envelope param mapping +; //---------------------------------------------------------------------------------------- +go4kENVMap: + fld dword [edx+eax*4] +%ifdef GO4K_USE_ENV_MOD_ADR + fadd dword [WRK+go4kENV_wrk.am+eax*4] +%endif + fimul dword [c_24] + fchs +; //---------------------------------------------------------------------------------------- +; // Power function (2^x) +; //---------------------------------------------------------------------------------------- +; // Input : st0 : base +; // st1 : exponent +; // Output: st0 : result +; //---------------------------------------------------------------------------------------- +export_func Power@0 ; // base exp + fld1 + fadd st0 + fyl2x ; // log2_base + fld1 ; // 1 log2_base + fld st1 ; // log2_base 1 log2_base + fprem ; // (frac)log2_base 1 log2_base + f2xm1 ; // 2 ^ '' - 1 1 log2_base + faddp st1, st0 ; // 2 ^ '' (int)log2_base + fscale + fstp st1 + ret + +%ifdef USE_SECTIONS +section .g4kcoda code align=1 +%else +section .text +%endif +; //---------------------------------------------------------------------------------------- +; // ENV Tick +; //---------------------------------------------------------------------------------------- +; // IN : WRK = unit workspace +; // IN : VAL = unit values +; // IN : ecx = global workspace +; // OUT : +; // DIRTY : eax +; //---------------------------------------------------------------------------------------- +export_func go4kENV_func@0 + push 5 + call go4kTransformValues +%ifdef GO4K_USE_ENV_CHECK +; check if current note still active + mov eax, dword [ecx-4] + test eax, eax + jne go4kENV_func_do + fldz + ret +%endif +go4kENV_func_do: + mov eax, dword [ecx-8] ; // is the instrument in release mode (note off)? + test eax, eax + je go4kENV_func_process + mov dword [WRK+go4kENV_wrk.state], ENV_STATE_RELEASE +go4kENV_func_process: + mov eax, dword [WRK+go4kENV_wrk.state] + fld dword [WRK+go4kENV_wrk.level] ; // val - +; // check for sustain state + cmp al, ENV_STATE_SUSTAIN + je short go4kENV_func_leave2 +go4kENV_func_attac: + cmp al, ENV_STATE_ATTAC + jne short go4kENV_func_decay + call go4kENVMap ; // newval + faddp st1, st0 +; // check for end of attac + fld1 ; // 1 newval + fucomi st1 ; // 1 newval + fcmovnb st0, st1 ; // newval 1 (if newval < 1) + jbe short go4kENV_func_statechange +go4kENV_func_decay: + cmp al, ENV_STATE_DECAY + jne short go4kENV_func_release + call go4kENVMap ; // newval + fsubp st1, st0 +; // check for end of decay + fld dword [edx+go4kENV_val.sustain] ; // sustain newval + fucomi st1 ; // sustain newval + fcmovb st0, st1 ; // newval sustain (if newval > sustain) + jnc short go4kENV_func_statechange +go4kENV_func_release: +; // release state? + cmp al, ENV_STATE_RELEASE + jne short go4kENV_func_leave + call go4kENVMap ; // newval + fsubp st1, st0 +; // check for end of release + fldz ; // 0 newval + fucomi st1 ; // 0 newval + fcmovb st0, st1 ; // newval 0 (if newval > 0) + jc short go4kENV_func_leave +go4kENV_func_statechange: ; // newval + inc dword [WRK+go4kENV_wrk.state] +go4kENV_func_leave: ; // newval bla + fstp st1 +; // store new env value + fst dword [WRK+go4kENV_wrk.level] +go4kENV_func_leave2: +; // mul by gain +%ifdef GO4K_USE_ENV_MOD_GM + fld dword [edx+go4kENV_val.gain] + fadd dword [WRK+go4kENV_wrk.gm] + fmulp st1, st0 +%else + fmul dword [edx+go4kENV_val.gain] +%endif + ret + + +; //---------------------------------------------------------------------------------------- +; // VCO Tick +; //---------------------------------------------------------------------------------------- +; // IN : WRK = unit workspace +; // IN : VAL = unit values +; // IN : ecx = global workspace +; // OUT : +; // DIRTY : eax +; //---------------------------------------------------------------------------------------- +%ifdef USE_SECTIONS +section .g4kcodp code align=1 +%else +section .text +%endif +go4kVCO_pulse: + fucomi st1 ; // c p + fld1 + jnc short go4kVCO_func_pulse_up ; // +1 c p + fchs ; // -1 c p +go4kVCO_func_pulse_up: + fstp st1 ; // +-1 p + fstp st1 ; // +-1 + ret + +%ifdef USE_SECTIONS +section .g4kcodt code align=1 +%else +section .text +%endif +go4kVCO_trisaw: + fucomi st1 ; // c p + jnc short go4kVCO_func_trisaw_up + fld1 ; // 1 c p + fsubr st2, st0 ; // 1 c 1-p + fsubrp st1, st0 ; // 1-c 1-p +go4kVCO_func_trisaw_up: + fdivp st1, st0 ; // tp'/tc + fadd st0 ; // 2*'' + fld1 ; // 1 2*'' + fsubp st1, st0 ; // 2*''-1 + ret + +%ifdef USE_SECTIONS +section .g4kcods code align=1 +%else +section .text +%endif +go4kVCO_sine: + fucomi st1 ; // c p + jnc short go4kVCO_func_sine_do + fstp st1 + fsub st0, st0 ; // 0 + ret +go4kVCO_func_sine_do + fdivp st1, st0 ; // p/c + fldpi ; // pi p + fadd st0 ; // 2*pi p + fmulp st1, st0 ; // 2*pi*p + fsin ; // sin(2*pi*p) + ret + +%ifdef GO4K_USE_VCO_GATE +%ifdef USE_SECTIONS +section .g4kcodq code align=1 +%else +section .text +%endif +go4kVCO_gate: + fxch ; // p c + fstp st1 ; // p + fmul dword [c_16] ; // p' + push eax + push eax + fistp dword [esp] ; // - + fld1 ; // 1 + pop eax + and al, 0xf + bt word [VAL-5],ax + jc go4kVCO_gate_bit + fsub st0, st0 ; // 0 +go4kVCO_gate_bit: + fld dword [WRK+go4kVCO_wrk.cm] ; // f x + fsub st1 ; // f-x x + fmul dword [c_dc_const] ; // c(f-x) x + faddp st1, st0 ; // x' + fst dword [WRK+go4kVCO_wrk.cm] + pop eax + ret +%endif + +%ifdef USE_SECTIONS +section .g4kcodb code align=1 +%else +section .text +%endif +export_func go4kVCO_func@0 +%ifdef GO4K_USE_VCO_PHASE_OFFSET + %ifdef GO4K_USE_VCO_SHAPE + %ifdef GO4K_USE_VCO_GATE + push 8 + %else + push 7 + %endif + %else + %ifdef GO4K_USE_VCO_GATE + push 7 + %else + push 6 + %endif + %endif +%else + %ifdef GO4K_USE_VCO_SHAPE + %ifdef GO4K_USE_VCO_GATE + push 7 + %else + push 6 + %endif + %else + %ifdef GO4K_USE_VCO_GATE + push 6 + %else + push 5 + %endif + %endif +%endif + call go4kTransformValues +%ifdef GO4K_USE_VCO_CHECK +; check if current note still active + mov eax, dword [ecx-4] + test eax, eax + jne go4kVCO_func_do +%ifdef GO4K_USE_VCO_STEREO + movzx eax, byte [VAL-1] ; // get flags and check for stereo + test al, byte VCO_STEREO + jz short go4kVCO_func_nostereoout + fldz +go4kVCO_func_nostereoout: +%endif + fldz + ret +go4kVCO_func_do: +%endif + movzx eax, byte [VAL-1] ; // get flags +%ifdef GO4K_USE_VCO_STEREO + test al, byte VCO_STEREO + jz short go4kVCO_func_nopswap + fld dword [WRK+go4kVCO_wrk.phase] ;// swap left/right phase values for first stereo run + fld dword [WRK+go4kVCO_wrk.phase2] + fstp dword [WRK+go4kVCO_wrk.phase] + fstp dword [WRK+go4kVCO_wrk.phase2] +go4kVCO_func_nopswap: +%endif +go4kVCO_func_process: + fld dword [edx+go4kVCO_val.transpose] + fsub dword [c_0_5] +%ifdef GO4K_USE_VCO_MOD_TM + fadd dword [WRK+go4kVCO_wrk.tm] +%endif + fdiv dword [c_i128] + fld dword [edx+go4kVCO_val.detune] + fsub dword [c_0_5] + fadd st0 +%ifdef GO4K_USE_VCO_STEREO + test al, byte VCO_STEREO + jz short go4kVCO_func_nodswap + fchs ;// negate detune for stereo +go4kVCO_func_nodswap: +%endif + faddp st1 +%ifdef GO4K_USE_VCO_MOD_DM + fadd dword [WRK+go4kVCO_wrk.dm] +%endif + ; // st0 now contains the transpose+detune offset + test al, byte LFO + jnz go4kVCO_func_skipnote + fiadd dword [ecx-4] ; // st0 is note, st1 is t+d offset +go4kVCO_func_skipnote: + fmul dword [c_i12] + call _Power@0 + test al, byte LFO + jz short go4kVCO_func_normalize_note + fmul dword [_LFO_NORMALIZE] ; // st0 is now frequency for lfo + jmp short go4kVCO_func_normalized +go4kVCO_func_normalize_note: + fmul dword [FREQ_NORMALIZE] ; // st0 is now frequency +go4kVCO_func_normalized: + fadd dword [WRK+go4kVCO_wrk.phase] +%ifdef GO4K_USE_VCO_MOD_FM + fadd dword [WRK+go4kVCO_wrk.fm] +%endif + fld1 + fadd st1, st0 + fxch + fprem + fstp st1 + fst dword [WRK+go4kVCO_wrk.phase] +%ifdef GO4K_USE_VCO_MOD_PM + fadd dword [WRK+go4kVCO_wrk.pm] +%endif +%ifdef GO4K_USE_VCO_PHASE_OFFSET + fadd dword [edx+go4kVCO_val.phaseofs] +%endif +%ifdef PHASE_RENORMALIZE + fld1 + fadd st1, st0 + fxch + fprem + fstp st1 ; // p +%endif + fld dword [edx+go4kVCO_val.color] ; // c p +%ifdef GO4K_USE_VCO_MOD_CM + fadd dword [WRK+go4kVCO_wrk.cm] ; // c p +%endif +go4kVCO_func_sine: + test al, byte SINE + jz short go4kVCO_func_trisaw + call go4kVCO_sine +go4kVCO_func_trisaw: + test al, byte TRISAW + jz short go4kVCO_func_pulse + call go4kVCO_trisaw +go4kVCO_func_pulse: + test al, byte PULSE +%ifdef GO4K_USE_VCO_GATE + jz short go4kVCO_func_gate +%else + jz short go4kVCO_func_noise +%endif + call go4kVCO_pulse +%ifdef GO4K_USE_VCO_GATE +go4kVCO_func_gate: + test al, byte GATE + jz short go4kVCO_func_noise + call go4kVCO_gate +%endif +go4kVCO_func_noise: + test al, byte NOISE + jz short go4kVCO_func_end + call _FloatRandomNumber@0 + fstp st1 + fstp st1 +go4kVCO_func_end: +%ifdef GO4K_USE_VCO_SHAPE + fld dword [edx+go4kVCO_val.shape] +%ifdef GO4K_USE_VCO_MOD_SM + fadd dword [WRK+go4kVCO_wrk.sm] +%endif + call go4kWaveshaper +%endif + fld dword [edx+go4kVCO_val.gain] +%ifdef GO4K_USE_VCO_MOD_GM + fadd dword [WRK+go4kVCO_wrk.gm] +%endif + fmulp st1, st0 + +%ifdef GO4K_USE_VCO_STEREO + test al, byte VCO_STEREO + jz short go4kVCO_func_stereodone + sub al, byte VCO_STEREO + fld dword [WRK+go4kVCO_wrk.phase] ;// swap left/right phase values again for second stereo run + fld dword [WRK+go4kVCO_wrk.phase2] + fstp dword [WRK+go4kVCO_wrk.phase] + fstp dword [WRK+go4kVCO_wrk.phase2] + jmp go4kVCO_func_process +go4kVCO_func_stereodone: +%endif + ret + +%ifdef USE_SECTIONS +section .g4kcodc code align=1 +%else +section .text +%endif +; //---------------------------------------------------------------------------------------- +; // VCF Tick +; //---------------------------------------------------------------------------------------- +; // IN : WRK = unit workspace +; // IN : VAL = unit values +; // IN : ecx = global workspace +; // OUT : +; // DIRTY : eax +; //---------------------------------------------------------------------------------------- +export_func go4kVCF_func@0 + push 3 + call go4kTransformValues +%ifdef GO4K_USE_VCF_CHECK +; check if current note still active + mov eax, dword [ecx-4] + test eax, eax + jne go4kVCF_func_do + ret +go4kVCF_func_do: +%endif + movzx eax, byte [VAL-1] ; // get type flag + + fld dword [edx+go4kVCF_val.res] ; // r in +%ifdef GO4K_USE_VCF_MOD_RM + fadd dword [WRK+go4kVCF_wrk.rm] +%endif + fstp dword [esp-8] + + fld dword [edx+go4kVCF_val.freq] ; // f in +%ifdef GO4K_USE_VCF_MOD_FM + fadd dword [WRK+go4kVCF_wrk.fm] +%endif + fmul st0, st0 ; // square the input so we never get negative and also have a smoother behaviour in the lower frequencies + fstp dword [esp-4] ; // in + +%ifdef GO4K_USE_VCF_STEREO + test al, byte STEREO + jz short go4kVCF_func_process + add WRK, go4kVCF_wrk.low2 +go4kVCF_func_stereoloop: ; // switch channels + fxch st1 ; // inr inl +%endif + +go4kVCF_func_process: + fld dword [esp-8] + fld dword [esp-4] + fmul dword [WRK+go4kVCF_wrk.band] ; // f*b r in + fadd dword [WRK+go4kVCF_wrk.low] ; // l' r in + fst dword [WRK+go4kVCF_wrk.low] ; // l' r in + fsubp st2, st0 ; // r in-l' + fmul dword [WRK+go4kVCF_wrk.band] ; // r*b in-l' + fsubp st1, st0 ; // h' + fst dword [WRK+go4kVCF_wrk.high] ; // h' + fmul dword [esp-4] ; // h'*f + fadd dword [WRK+go4kVCF_wrk.band] ; // b' + fstp dword [WRK+go4kVCF_wrk.band] + fldz +go4kVCF_func_low: + test al, byte LOWPASS + jz short go4kVCF_func_high + fadd dword [WRK+go4kVCF_wrk.low] +go4kVCF_func_high: +%ifdef GO4K_USE_VCF_HIGH + test al, byte HIGHPASS + jz short go4kVCF_func_band + fadd dword [WRK+go4kVCF_wrk.high] +%endif +go4kVCF_func_band: +%ifdef GO4K_USE_VCF_BAND + test al, byte BANDPASS + jz short go4kVCF_func_peak + fadd dword [WRK+go4kVCF_wrk.band] +%endif +go4kVCF_func_peak: +%ifdef GO4K_USE_VCF_PEAK + test al, byte PEAK + jz short go4kVCF_func_processdone + fadd dword [WRK+go4kVCF_wrk.low] + fsub dword [WRK+go4kVCF_wrk.high] +%endif +go4kVCF_func_processdone: + +%ifdef GO4K_USE_VCF_STEREO + test al, byte STEREO ; // outr inl + jz short go4kVCF_func_end + sub al, byte STEREO + sub WRK, go4kVCF_wrk.low2 + jmp go4kVCF_func_stereoloop +%endif + +go4kVCF_func_end: ; // value - - - - + ret + +%ifdef USE_SECTIONS +section .g4kcodd code align=1 +%else +section .text +%endif +; //---------------------------------------------------------------------------------------- +; // DST Tick +; //---------------------------------------------------------------------------------------- +; // IN : WRK = unit workspace +; // IN : VAL = unit values +; // IN : ecx = global workspace +; // OUT : +; // DIRTY : eax +; //---------------------------------------------------------------------------------------- +export_func go4kDST_func@0 +%ifdef GO4K_USE_DST +%ifdef GO4K_USE_DST_SH + %ifdef GO4K_USE_DST_STEREO + push 3 + %else + push 2 + %endif +%else + %ifdef GO4K_USE_DST_STEREO + push 2 + %else + push 1 + %endif +%endif + call go4kTransformValues +%ifdef GO4K_USE_DST_CHECK +; check if current note still active + mov eax, dword [ecx-4] + test eax, eax + jne go4kDST_func_do + ret +go4kDST_func_do: +%endif + movzx eax, byte [VAL-1] ; // get type flag +%ifdef GO4K_USE_DST_SH + fld dword [edx+go4kDST_val.snhfreq] ; // snh in (inr) +%ifdef GO4K_USE_DST_MOD_SH + fadd dword [WRK+go4kDST_wrk.sm] ; // snh' in (inr) +%endif + fmul st0, st0 ; // square the input so we never get negative and also have a smoother behaviour in the lower frequencies + fchs + fadd dword [WRK+go4kDST_wrk.snhphase]; // snh' in (inr) + fst dword [WRK+go4kDST_wrk.snhphase] + fldz ; // 0 snh' in (inr) + fucomip st1 ; // snh' in (inr) + fstp dword [esp-4] ; // in (inr) + jc short go4kDST_func_hold + fld1 ; // 1 in (inr) + fadd dword [esp-4] ; // 1+snh' in (inr) + fstp dword [WRK+go4kDST_wrk.snhphase]; // in (inr) +%endif +; // calc pregain and postgain +%ifdef GO4K_USE_DST_STEREO + test al, byte STEREO + jz short go4kDST_func_mono + fxch st1 ; // inr inl + fld dword [edx+go4kDST_val.drive] ; // drive inr inl +%ifdef GO4K_USE_DST_MOD_DM + fadd dword [WRK+go4kDST_wrk.dm] +%endif + call go4kWaveshaper ; // outr inl +%ifdef GO4K_USE_DST_SH + fst dword [WRK+go4kDST_wrk.out2] ; // outr inl +%endif + fxch st1 ; // inl outr +go4kDST_func_mono: +%endif + fld dword [edx+go4kDST_val.drive] ; // drive in (outr) +%ifdef GO4K_USE_DST_MOD_DM + fadd dword [WRK+go4kDST_wrk.dm] +%endif + call go4kWaveshaper ; // out (outr) +%ifdef GO4K_USE_DST_SH + fst dword [WRK+go4kDST_wrk.out] ; // out' (outr) +%endif + ret ; // out' (outr) +%ifdef GO4K_USE_DST_SH +go4kDST_func_hold: ; // in (inr) + fstp st0 ; // (inr) +%ifdef GO4K_USE_DST_STEREO + test al, byte STEREO + jz short go4kDST_func_monohold ; // (inr) + fstp st0 ; // + fld dword [WRK+go4kDST_wrk.out2] ; // outr +go4kDST_func_monohold: +%endif + fld dword [WRK+go4kDST_wrk.out] ; // out (outr) + ret +%endif + +%endif + +%ifdef USE_SECTIONS +section .g4kcodf code align=1 +%else +section .text +%endif +; //---------------------------------------------------------------------------------------- +; // DLL Tick +; //---------------------------------------------------------------------------------------- +; // IN : WRK = unit workspace +; // IN : VAL = unit values +; // IN : ecx = global workspace +; // OUT : +; // DIRTY : eax +; //---------------------------------------------------------------------------------------- +export_func go4kDLL_func@0 +%ifdef GO4K_USE_DLL +%ifdef GO4K_USE_DLL_CHORUS + %ifdef GO4K_USE_DLL_DAMP + push 8 + %else + push 7 + %endif +%else + %ifdef GO4K_USE_DLL_DAMP + push 6 + %else + push 5 + %endif +%endif + call go4kTransformValues + pushad + movzx ebx, byte [VAL-(go4kDLL_val.size-go4kDLL_val.delay)/4] ;// delay length index +%ifdef GO4K_USE_DLL_NOTE_SYNC + test ebx, ebx + jne go4kDLL_func_process + fld1 + fild dword [ecx-4] ; // load note freq + fmul dword [c_i12] + call _Power@0 + fmul dword [FREQ_NORMALIZE] ; // normalize + fdivp st1, st0 ; // invert to get numer of samples + fistp word [_go4k_delay_times+ebx*2] ; store current comb size +%endif +go4kDLL_func_process: + mov ecx, eax ;// ecx is delay counter +%ifdef GO4K_USE_DLL_MOD + mov edi, WRK ;// edi is modulation workspace +%endif + mov WRK, dword [_go4k_delay_buffer_ofs] ;// ebp is current delay + fld st0 ;// in in +%ifdef GO4K_USE_DLL_MOD_IM + fld dword [edx+go4kDLL_val.dry] ;// dry in in + fadd dword [edi+go4kDLL_wrk2.im] ;// dry' in in + fmulp st1, st0 ;// out in +%else + fmul dword [edx+go4kDLL_val.dry] ;// out in +%endif + fxch ;// in out +%ifdef GO4K_USE_DLL_MOD_PM + fld dword [edx+go4kDLL_val.pregain] ;// pg in out + fadd dword [edi+go4kDLL_wrk2.pm] ;// pg' in out + fmul st0, st0 ;// pg'' in out + fmulp st1, st0 ;// in' out +%else + fmul dword [edx+go4kDLL_val.pregain] ;// in' out + fmul dword [edx+go4kDLL_val.pregain] ;// in' out +%endif + +%ifdef GO4K_USE_DLL_CHORUS +;// update saw lfo for chorus/flanger + fld dword [edx+go4kDLL_val.freq] ;// f in' out +%ifdef GO4K_USE_DLL_MOD_SM + fadd dword [edi+go4kDLL_wrk2.sm] ;// f' in' out +%endif + fmul st0, st0 + fmul st0, st0 + fdiv dword [DLL_DEPTH] + fadd dword [WRK+go4kDLL_wrk.phase] ;// p' in' out +;// clamp phase to 0,1 (only in editor, since delay can be active quite long) +%ifdef GO4K_USE_DLL_CHORUS_CLAMP + fld1 ;// 1 p' in' out + fadd st1, st0 ;// 1 1+p' in' out + fxch ;// 1+p' 1 in' out + fprem ;// p'' 1 in' out + fstp st1 ;// p'' in' out +%endif + fst dword [WRK+go4kDLL_wrk.phase] +;// get current sine value + fldpi ; // pi p in' out + fadd st0 ; // 2*pi p in' out + fmulp st1, st0 ; // 2*pi*p in' out + fsin ; // sin in' out + fld1 ; // 1 sin in' out + faddp st1, st0 ; // 1+sin in' out +;// mul with depth and convert to samples + fld dword [edx+go4kDLL_val.depth] ; // d 1+sin in' out +%ifdef GO4K_USE_DLL_MOD_AM + fadd dword [edi+go4kDLL_wrk2.am] ; // d' 1+sin in' out +%endif + fmul st0, st0 + fmul st0, st0 + fmul dword [DLL_DEPTH] + fmulp st1, st0 + fistp dword [esp-4] ; // in' out +%endif + +go4kDLL_func_loop: + movzx esi, word [_go4k_delay_times+ebx*2] ; fetch comb size + mov eax, dword [WRK+go4kDLL_wrk.index] ;// eax is current comb index + +%ifdef GO4K_USE_DLL_CHORUS + ;// add lfo offset and wrap buffer + add eax, dword [esp-4] + cmp eax, esi + jl short go4kDLL_func_buffer_nowrap1 + sub eax, esi +go4kDLL_func_buffer_nowrap1: +%endif + + fld dword [WRK+eax*4+go4kDLL_wrk.buffer];// cout in' out + +%ifdef GO4K_USE_DLL_CHORUS + mov eax, dword [WRK+go4kDLL_wrk.index] +%endif + + ;// add comb output to current output + fadd st2, st0 ;// cout in' out' +%ifdef GO4K_USE_DLL_DAMP + fld1 ;// 1 cout in' out' + fsub dword [edx+go4kDLL_val.damp] ;// 1-damp cout in' out' +%ifdef GO4K_USE_DLL_MOD_DM + fsub dword [edi+go4kDLL_wrk2.dm] ;// 1-damp' cout in' out' +%endif + fmulp st1, st0 ;// cout*d2 in' out' + fld dword [edx+go4kDLL_val.damp] ;// d1 cout*d2 in' out' +%ifdef GO4K_USE_DLL_MOD_DM + fadd dword [edi+go4kDLL_wrk2.dm] ;// d1' cout*d2 in' out' +%endif + fmul dword [WRK+go4kDLL_wrk.store] ;// store*d1 cout*d2 in' out' + faddp st1, st0 ;// store' in' out' + fst dword [WRK+go4kDLL_wrk.store] ;// store' in' out' +%endif +%ifdef GO4K_USE_DLL_MOD_FM + fld dword [edx+go4kDLL_val.feedback] ;// fb cout in' out' + fadd dword [edi+go4kDLL_wrk2.fm] ;// fb' cout in' out' + fmulp st1, st0 ;// cout*fb' in' out' +%else + fmul dword [edx+go4kDLL_val.feedback] ;// cout*fb in' out' +%endif +%ifdef GO4K_USE_DLL_DC_FILTER + fadd st0, st1 ;// store in' out' + fstp dword [WRK+eax*4+go4kDLL_wrk.buffer];// in' out' +%else + fsub st0, st1 ;// store in' out' + fstp dword [WRK+eax*4+go4kDLL_wrk.buffer];// in' out' + fneg +%endif + ;// wrap comb buffer pos + inc eax + cmp eax, esi + jl short go4kDLL_func_buffer_nowrap2 +%ifdef GO4K_USE_DLL_CHORUS + sub eax, esi +%else + xor eax, eax +%endif +go4kDLL_func_buffer_nowrap2: + mov dword [WRK+go4kDLL_wrk.index], eax + ;// increment buffer pointer to next buffer + inc ebx ;// go to next delay length index + add WRK, go4kDLL_wrk.size ;// go to next delay + mov dword [_go4k_delay_buffer_ofs], WRK ;// store next delay offset + loopne go4kDLL_func_loop + fstp st0 ;// out' + ;// process a dc filter to prevent heavy offsets in reverb +%ifdef GO4K_USE_DLL_DC_FILTER +; y(n) = x(n) - x(n-1) + R * y(n-1) + sub WRK, go4kDLL_wrk.size + fld dword [WRK+go4kDLL_wrk.dcout] ;// dco out' + fmul dword [c_dc_const] ;// dcc*dco out' + fsub dword [WRK+go4kDLL_wrk.dcin] ;// dcc*dco-dci out' + fxch ;// out' dcc*dco-dci + fst dword [WRK+go4kDLL_wrk.dcin] ;// out' dcc*dco-dci + faddp st1 ;// out' +%ifdef GO4K_USE_UNDENORMALIZE + fadd dword [c_0_5] ;// add and sub small offset to prevent denormalization + fsub dword [c_0_5] +%endif + fst dword [WRK+go4kDLL_wrk.dcout] +%endif + popad + ret +%endif + + +%ifdef USE_SECTIONS +section .g4kcodu code align=1 +%else +section .text +%endif +; //---------------------------------------------------------------------------------------- +; // GLITCH Tick +; //---------------------------------------------------------------------------------------- +; // IN : WRK = unit workspace +; // IN : VAL = unit values +; // IN : ecx = global workspace +; // OUT : +; // DIRTY : eax +; //---------------------------------------------------------------------------------------- +export_func go4kGLITCH_func@0 +%ifdef GO4K_USE_GLITCH + push 5 + call go4kTransformValues + pushad + + mov edi, WRK + mov WRK, dword [_go4k_delay_buffer_ofs] ;// ebp is current delay + +; mov eax, dword [edx+go4kGLITCH_val.active] +; or eax, dword [edi+go4kGLITCH_wrk2.am] +; test eax, eax +; je go4kGLITCH_func_notactive ;// out + + fld dword [edx+go4kGLITCH_val.active] ;// a in + fadd dword [edi+go4kGLITCH_wrk2.am] ;// a' in + ; // check for activity + fldz ;// 0 a' in + fucomip st1 ;// a' in + fstp st0 ;// in + jnc go4kGLITCH_func_notactive ;// out + + ;// check for first call and init if so init (using slizesize slot) + mov eax, dword [WRK+go4kGLITCH_wrk.slizesize] + and eax, eax + jnz go4kGLITCH_func_process + mov dword [WRK+go4kGLITCH_wrk.index], eax + mov dword [WRK+go4kGLITCH_wrk.store], eax + movzx ebx, byte [VAL-(go4kGLITCH_val.size-go4kGLITCH_val.slicesize)/4] ;// slicesize index + movzx eax, word [_go4k_delay_times+ebx*2] ;// fetch slicesize + push eax + fld1 + fild dword [esp] + fstp dword [WRK+go4kGLITCH_wrk.slizesize] + fstp dword [WRK+go4kGLITCH_wrk.slicepitch] + pop eax +go4kGLITCH_func_process: + ;// fill buffer until full + mov eax, dword [WRK+go4kGLITCH_wrk.store] + cmp eax, MAX_DELAY + jae go4kGLITCH_func_filldone + fst dword [WRK+eax*4+go4kDLL_wrk.buffer] ;// in + inc dword [WRK+go4kGLITCH_wrk.store] +go4kGLITCH_func_filldone: + ;// save input + push eax + fstp dword [esp] ;// - + + ;// read from buffer + push eax + fld dword [WRK+go4kGLITCH_wrk.index] ;// idx + fist dword [esp] + pop eax + fld dword [WRK+eax*4+go4kDLL_wrk.buffer] ;// out idx + fxch ;// idx out + ;// progress readindex with current play speed + fadd dword [WRK+go4kGLITCH_wrk.slicepitch] ;// idx' out + fst dword [WRK+go4kGLITCH_wrk.index] + + ;// check for slice done + fld dword [WRK+go4kGLITCH_wrk.slizesize] ;// size idx' out + fxch ;// idx' size out + fucomip st1 ;// idx' out + fstp st0 ;// out + jc go4kGLITCH_func_process_done + ;// reinit for next loop + xor eax, eax + mov dword [WRK+go4kGLITCH_wrk.index], eax + + fld dword [edx+go4kGLITCH_val.dsize] + fadd dword [edi+go4kGLITCH_wrk2.sm] + fsub dword [c_0_5] + fmul dword [c_0_5] + call _Power@0 + fmul dword [WRK+go4kGLITCH_wrk.slizesize] + fstp dword [WRK+go4kGLITCH_wrk.slizesize] + + fld dword [edx+go4kGLITCH_val.dpitch] + fadd dword [edi+go4kGLITCH_wrk2.pm] + fsub dword [c_0_5] + fmul dword [c_0_5] + call _Power@0 + fmul dword [WRK+go4kGLITCH_wrk.slicepitch] + fstp dword [WRK+go4kGLITCH_wrk.slicepitch] +go4kGLITCH_func_process_done: + + ;// dry wet mix + fld dword [edx+go4kGLITCH_val.dry] ;// dry out + fadd dword [edi+go4kGLITCH_wrk2.dm] ;// dry' out + fld1 ;// 1 dry' out + fsub st1 ;// 1-dry' dry' out + fmulp st2 ;// dry' out' + fmul dword [esp] ;// in' out' + faddp st1, st0 ;// out' + + pop eax + jmp go4kGLITCH_func_leave +go4kGLITCH_func_notactive: + ;// mark as uninitialized again (using slizesize slot) + xor eax,eax + mov dword [WRK+go4kGLITCH_wrk.slizesize], eax +go4kGLITCH_func_leave: + add WRK, go4kDLL_wrk.size ;// go to next delay + mov dword [_go4k_delay_buffer_ofs], WRK ;// store next delay offset + popad + ret +%endif + +%ifdef USE_SECTIONS +section .g4kcodg code align=1 +%else +section .text +%endif +; //---------------------------------------------------------------------------------------- +; // FOP Tick (various fp stack based operations) +; //---------------------------------------------------------------------------------------- +; // IN : WRK = unit workspace +; // IN : VAL = unit values +; // IN : ecx = global workspace +; // OUT : +; // DIRTY : +; //---------------------------------------------------------------------------------------- +export_func go4kFOP_func@0 + push 1 + call go4kTransformValues +go4kFOP_func_pop: + dec eax + jnz go4kFOP_func_addp + fstp st0 + ret +go4kFOP_func_addp: + dec eax + jnz go4kFOP_func_mulp + faddp st1, st0 + ret +go4kFOP_func_mulp: + dec eax + jnz go4kFOP_func_push + fmulp st1, st0 + ret +go4kFOP_func_push: + dec eax + jnz go4kFOP_func_xch + fld st0 + ret +go4kFOP_func_xch: + dec eax + jnz go4kFOP_func_add + fxch + ret +go4kFOP_func_add: + dec eax + jnz go4kFOP_func_mul + fadd st1 + ret +go4kFOP_func_mul: + dec eax + jnz go4kFOP_func_addp2 + fmul st1 + ret +go4kFOP_func_addp2: + dec eax + jnz go4kFOP_func_loadnote + faddp st2, st0 + faddp st2, st0 + ret +go4kFOP_func_loadnote: + dec eax + jnz go4kFOP_func_mulp2 + fild dword [ecx-4] + fmul dword [c_i128] + ret +go4kFOP_func_mulp2: + fmulp st2, st0 + fmulp st2, st0 + ret + +%ifdef USE_SECTIONS +section .g4kcodh code align=1 +%else +section .text +%endif +; //---------------------------------------------------------------------------------------- +; // FST Tick (stores a value somewhere in the local workspace) +; //---------------------------------------------------------------------------------------- +; // IN : WRK = unit workspace +; // IN : VAL = unit values +; // IN : ecx = global workspace +; // OUT : +; // DIRTY : +; //---------------------------------------------------------------------------------------- +export_func go4kFST_func@0 + push 1 + call go4kTransformValues + fld dword [edx+go4kFST_val.amount] + fsub dword [c_0_5] + fadd st0 + fmul st1 + lodsw + and eax, 0x00003fff ; // eax is destination slot + test word [VAL-2], FST_ADD + jz go4kFST_func_set + fadd dword [ecx+eax*4] +go4kFST_func_set: + fstp dword [ecx+eax*4] + test word [VAL-2], FST_POP + jz go4kFST_func_done + fstp st0 +go4kFST_func_done: + ret + +%ifdef USE_SECTIONS +section .g4kcodm code align=1 +%else +section .text +%endif +; //---------------------------------------------------------------------------------------- +; // FLD Tick (load a value on stack, optionally add a modulation signal beforehand) +; //---------------------------------------------------------------------------------------- +; // IN : WRK = unit workspace +; // IN : VAL = unit values +; // IN : ecx = global workspace +; // OUT : signal-signal*pan , signal*pan +; // DIRTY : +; //---------------------------------------------------------------------------------------- +export_func go4kFLD_func@0 ;// in main env +%ifdef GO4K_USE_FLD + push 1 + call go4kTransformValues + fld dword [edx+go4kFLD_val.value] ;// value in + fsub dword [c_0_5] + fadd st0 +%ifdef GO4K_USE_FLD_MOD_VM + fadd dword [WRK+go4kFLD_wrk.vm] ;// value' in +%endif +%endif + ret + +%ifdef GO4K_USE_FSTG +%ifdef USE_SECTIONS +section .g4kcodi code align=1 +%else +section .text +%endif +; //---------------------------------------------------------------------------------------- +; // FSTG Tick (stores a value anywhere in the synth (and in each voice)) +; //---------------------------------------------------------------------------------------- +; // IN : WRK = unit workspace +; // IN : VAL = unit values +; // IN : ecx = global workspace +; // OUT : +; // DIRTY : +; //---------------------------------------------------------------------------------------- +export_func go4kFSTG_func@0 + push 1 + call go4kTransformValues +%ifdef GO4K_USE_FSTG_CHECK +; check if current note still active + mov eax, dword [ecx-4] + test eax, eax + jne go4kFSTG_func_do + lodsw + jmp go4kFSTG_func_testpop +go4kFSTG_func_do: +%endif + fld dword [edx+go4kFST_val.amount] + fsub dword [c_0_5] + fadd st0 + fmul st1 + lodsw + and eax, 0x00003fff ; // eax is destination slot + test word [VAL-2], FST_ADD + jz go4kFSTG_func_set + fadd dword [go4k_synth_wrk+eax*4] +go4kFSTG_func_set: +%if MAX_VOICES > 1 + fst dword [go4k_synth_wrk+eax*4] + fstp dword [go4k_synth_wrk+eax*4+go4k_instrument.size] +%else + fstp dword [go4k_synth_wrk+eax*4] +%endif +go4kFSTG_func_testpop: + test word [VAL-2], FST_POP + jz go4kFSTG_func_done + fstp st0 +go4kFSTG_func_done: + ret +%endif + +%ifdef USE_SECTIONS +section .g4kcodj code align=1 +%else +section .text +%endif +; //---------------------------------------------------------------------------------------- +; // PAN Tick (multiplies signal with main envelope and converts to stereo) +; //---------------------------------------------------------------------------------------- +; // IN : WRK = unit workspace +; // IN : VAL = unit values +; // IN : ecx = global workspace +; // OUT : signal-signal*pan , signal*pan +; // DIRTY : +; //---------------------------------------------------------------------------------------- +export_func go4kPAN_func@0 ;// in main env +%ifdef GO4K_USE_PAN + push 1 + call go4kTransformValues + fld dword [edx+go4kPAN_val.panning] ;// pan in +%ifdef GO4K_USE_PAN_MOD + fadd dword [WRK+go4kPAN_wrk.pm] ;// pan in +%endif + fmul st1 ;// r in + fsub st1, st0 ;// r l + fxch ;// l r +%else + fmul dword [c_0_5] + fld st0 +%endif + ret + +%ifdef USE_SECTIONS +section .g4kcodk code align=1 +%else +section .text +%endif +; //---------------------------------------------------------------------------------------- +; // OUT Tick ( stores stereo signal pair in temp buffers of the instrument) +; //---------------------------------------------------------------------------------------- +; // IN : WRK = unit workspace +; // IN : VAL = unit values +; // IN : ecx = global workspace +; // OUT : +; // DIRTY : +; //---------------------------------------------------------------------------------------- +export_func go4kOUT_func@0 ;// l r +%ifdef GO4K_USE_GLOBAL_DLL + push 2 + call go4kTransformValues + pushad + lea edi, [ecx+MAX_UNITS*MAX_UNIT_SLOTS*4] + fld st1 ;// r l r + fld st1 ;// l r l r + fld dword [edx+go4kOUT_val.auxsend] ;// as l r l r +%ifdef GO4K_USE_OUT_MOD_AM + fadd dword [WRK+go4kOUT_wrk.am] ;// am l r l r +%endif + fmulp st1, st0 ;// l' r l r + fstp dword [edi] ;// r l r + scasd + fld dword [edx+go4kOUT_val.auxsend] ;// as r l r +%ifdef GO4K_USE_OUT_MOD_AM + fadd dword [WRK+go4kOUT_wrk.am] ;// am r l r +%endif + fmulp st1, st0 ;// r' l r + fstp dword [edi] ;// l r + scasd + fld dword [edx+go4kOUT_val.gain] ;// g l r +%ifdef GO4K_USE_OUT_MOD_GM + fadd dword [WRK+go4kOUT_wrk.gm] ;// gm l r +%endif + fmulp st1, st0 ;// l' r + fstp dword [edi] ;// r + scasd + fld dword [edx+go4kOUT_val.gain] ;// g r +%ifdef GO4K_USE_OUT_MOD_GM + fadd dword [WRK+go4kOUT_wrk.gm] ;// gm r +%endif + fmulp st1, st0 ;// r' + fstp dword [edi] ;// - + scasd + popad +%else + push 1 + call go4kTransformValues + + fld dword [edx+go4kOUT_val.gain] ;// g l r +%ifdef GO4K_USE_OUT_MOD_GM + fadd dword [WRK+go4kOUT_wrk.gm] ;// gm l r +%endif + fmulp st1, st0 ;// l' r + fstp dword [ecx+MAX_UNITS*MAX_UNIT_SLOTS*4+0] ;// r + fld dword [edx+go4kOUT_val.gain] ;// g r +%ifdef GO4K_USE_OUT_MOD_GM + fadd dword [WRK+go4kOUT_wrk.gm] ;// gm r +%endif + fmulp st1, st0 ;// r' + fstp dword [ecx+MAX_UNITS*MAX_UNIT_SLOTS*4+4] ;// - + +%endif + ret + +%ifdef USE_SECTIONS +section .g4kcodl code align=1 +%else +section .text +%endif +; //---------------------------------------------------------------------------------------- +; // ACC Tick (stereo signal accumulation for synth commands only -> dont use in instrument commands) +; //---------------------------------------------------------------------------------------- +; // IN : WRK = unit workspace +; // IN : VAL = unit values +; // IN : ecx = global workspace +; // OUT : +; // DIRTY : eax +; //---------------------------------------------------------------------------------------- +export_func go4kACC_func@0 + push 1 + call go4kTransformValues + pushad + mov edi, go4k_synth_wrk + add edi, go4k_instrument.size + sub edi, eax ; // eax already contains the accumulation offset from the go4kTransformValues call + mov cl, MAX_INSTRUMENTS*MAX_VOICES + fldz ;// 0 + fldz ;// 0 0 +go4kACC_func_loop: + fadd dword [edi-8] ;// l 0 + fxch ;// 0 l + fadd dword [edi-4] ;// r l + fxch ;// l r + add edi, go4k_instrument.size + dec cl + jnz go4kACC_func_loop + popad + ret + +%ifdef USE_SECTIONS +section .g4kcodw code align=1 +%else +section .text +%endif +; //---------------------------------------------------------------------------------------- +; // Update Instrument (allocate voices, set voice to release) +; //---------------------------------------------------------------------------------------- +; // IN : +; // IN : +; // OUT : +; // DIRTY : +; //---------------------------------------------------------------------------------------- +go4kUpdateInstrument: +; // get new note + mov eax, dword [esp+4+4] ; // eax = current tick + shr eax, PATTERN_SIZE_SHIFT ; // eax = current pattern + imul edx, ecx, dword MAX_PATTERNS ; // edx = instrument pattern list index + movzx edx, byte [edx+eax+go4k_pattern_lists] ; // edx = pattern index + mov eax, dword [esp+4+4] ; // eax = current tick + shl edx, PATTERN_SIZE_SHIFT + and eax, PATTERN_SIZE-1 + movzx edx, byte [edx+eax+go4k_patterns] ; // edx = requested note in new patterntick +; // apply note changes + cmp dl, HLD ; // anything but hold causes action + je short go4kUpdateInstrument_done + inc dword [edi] ; // set release flag if needed +%if MAX_VOICES > 1 + inc dword [edi+go4k_instrument.size] ; // set release flag if needed +%endif + cmp dl, HLD ; // check for new note + jl short go4kUpdateInstrument_done +%if MAX_VOICES > 1 + pushad + xchg eax, dword [go4k_voiceindex + ecx*4] + test eax, eax + je go4kUpdateInstrument_newNote + add edi, go4k_instrument.size +go4kUpdateInstrument_newNote: + xor al,1 + xchg dword [go4k_voiceindex + ecx*4], eax +%endif + pushad + xor eax, eax + mov ecx, (8+MAX_UNITS*MAX_UNIT_SLOTS*4)/4 ; // clear only relase, note and workspace + rep stosd + popad + mov dword [edi+4], edx ; // set requested note as current note +%if MAX_VOICES > 1 + popad +%endif + jmp short go4kUpdateInstrument_done +go4kUpdateInstrument_done: + ret + +%ifdef USE_SECTIONS +section .g4kcodx code align=1 +%else +section .text +%endif +; //---------------------------------------------------------------------------------------- +; // Render Voices +; //---------------------------------------------------------------------------------------- +; // IN : +; // IN : +; // OUT : +; // DIRTY : +; //---------------------------------------------------------------------------------------- +go4kRenderVoices: + push ecx ; // save current instrument counter +%if MAX_VOICES > 1 + push COM ; // save current instrument command index + push VAL ; // save current instrument values index +%endif + call go4k_VM_process ; // call synth vm for instrument voices + mov eax, dword [ecx+go4kENV_wrk.state] + cmp al, byte ENV_STATE_OFF + jne go4kRenderVoices_next + xor eax, eax + mov dword [ecx-4], eax ; // kill note if voice is done +go4kRenderVoices_next: +%if MAX_VOICES > 1 + pop VAL ; // restore instrument value index + pop COM ; // restore instrument command index +%endif + +%ifdef GO4K_USE_BUFFER_RECORDINGS + mov eax, dword [esp+16] ; // get current tick + shr eax, 8 ; // every 256th sample = ~ 172 hz + shl eax, 5 ; // for 16 instruments a 2 voices + add eax, dword [esp] + add eax, dword [esp] ; // + 2*currentinstrument+0 +%ifdef GO4K_USE_ENVELOPE_RECORDINGS + mov edx, dword [ecx+go4kENV_wrk.level] + mov dword [__4klang_envelope_buffer+eax*4], edx +%endif +%ifdef GO4K_USE_NOTE_RECORDINGS + mov edx, dword [ecx-4] + mov dword [__4klang_note_buffer+eax*4], edx +%endif +%endif + +%if MAX_VOICES > 1 + call go4k_VM_process ; // call synth vm for instrument voices + mov eax, dword [ecx+go4kENV_wrk.state] + cmp al, byte ENV_STATE_OFF + jne go4k_render_instrument_next2 + xor eax, eax + mov dword [ecx-4], eax ; // kill note if voice is done +go4k_render_instrument_next2: + +%ifdef GO4K_USE_BUFFER_RECORDINGS + mov eax, dword [esp+16] ; // get current tick + shr eax, 8 ; // every 256th sample = ~ 172 hz + shl eax, 5 ; // for 16 instruments a 2 voices + add eax, dword [esp] + add eax, dword [esp] ; // + 2*currentinstrument+0 +%ifdef GO4K_USE_ENVELOPE_RECORDINGS + mov edx, dword [ecx+go4kENV_wrk.level] + mov dword [__4klang_envelope_buffer+eax*4+4], edx +%endif +%ifdef GO4K_USE_NOTE_RECORDINGS + mov edx, dword [ecx-4] + mov dword [__4klang_note_buffer+eax*4+4], edx +%endif +%endif + +%endif + pop ecx ; // restore instrument counter + ret + +%ifdef USE_SECTIONS +section .g4kcody code align=1 +%else +section .text +%endif +; //---------------------------------------------------------------------------------------- +; // the entry point for the synth +; //---------------------------------------------------------------------------------------- +%ifdef USE_SECTIONS +export_func _su_render_song@4 +%else +export_func _su_render_song +%endif + pushad + xor ecx, ecx +%ifdef GO4K_USE_BUFFER_RECORDINGS + push ecx +%endif +; loop all ticks +go4k_render_tickloop: + push ecx + xor ecx, ecx +; loop all samples per tick +go4k_render_sampleloop: + push ecx + xor ecx, ecx + mov ebx, go4k_synth_instructions ; // ebx = instrument command index + mov VAL, go4k_synth_parameter_values; // VAL = instrument values index + mov edi, _go4k_delay_buffer ; // get offset of first delay buffer + mov dword [_go4k_delay_buffer_ofs], edi ; // store offset in delaybuffer offset variable + mov edi, go4k_synth_wrk ; // edi = first instrument +; loop all instruments +go4k_render_instrumentloop: + mov eax, dword [esp] ; // eax = current tick sample + and eax, eax + jnz go4k_render_instrument_process ; // tick change? (first sample in current tick) + call go4kUpdateInstrument ; // update instrument state +; process instrument +go4k_render_instrument_process: + call go4kRenderVoices + inc ecx + cmp cl, byte MAX_INSTRUMENTS + jl go4k_render_instrumentloop + mov dword [edi+4], ecx ; // move a value != 0 into note slot, so processing will be done + call go4k_VM_process ; // call synth vm for synth +go4k_render_output_sample: +%ifdef GO4K_USE_BUFFER_RECORDINGS + inc dword [esp+8] + xchg esi, dword [esp+48] ; // edx = destbuffer +%else + xchg esi, dword [esp+44] ; // edx = destbuffer +%endif +%ifdef GO4K_CLIP_OUTPUT + fld dword [edi-8] + fld1 ; // 1 val + fucomi st1 ; // 1 val + jbe short go4k_render_clip1 + fchs ; // -1 val + fucomi st1 ; // -1 val + fcmovb st0, st1 ; // val -1 (if val > -1) +go4k_render_clip1: + fstp st1 ; // newval +%ifdef GO4K_USE_16BIT_OUTPUT + push eax + fmul dword [c_32767] + fistp dword [esp] + pop eax + mov word [esi],ax ; // store integer converted left sample + lodsw +%else + fstp dword [esi] ; // store left sample + lodsd +%endif + fld dword [edi-4] + fld1 ; // 1 val + fucomi st1 ; // 1 val + jbe short go4k_render_clip2 + fchs ; // -1 val + fucomi st1 ; // -1 val + fcmovb st0, st1 ; // val -1 (if val > -1) +go4k_render_clip2: + fstp st1 ; // newval +%ifdef GO4K_USE_16BIT_OUTPUT + push eax + fmul dword [c_32767] + fistp dword [esp] + pop eax + mov word [esi],ax ; // store integer converted right sample + lodsw +%else + fstp dword [esi] ; // store right sample + lodsd +%endif +%else + fld dword [edi-8] +%ifdef GO4K_USE_16BIT_OUTPUT + push eax + fmul dword [c_32767] + fistp dword [esp] + pop eax + mov word [esi],ax ; // store integer converted left sample + lodsw +%else + fstp dword [esi] ; // store left sample + lodsd +%endif + fld dword [edi-4] +%ifdef GO4K_USE_16BIT_OUTPUT + push eax + fmul dword [c_32767] + fistp dword [esp] + pop eax + mov word [esi],ax ; // store integer converted right sample + lodsw +%else + fstp dword [esi] ; // store right sample + lodsd +%endif +%endif +%ifdef GO4K_USE_BUFFER_RECORDINGS + xchg esi, dword [esp+48] +%else + xchg esi, dword [esp+44] +%endif + pop ecx + inc ecx +%ifdef GO4K_USE_GROOVE_PATTERN + mov ebx, dword SAMPLES_PER_TICK + mov eax, dword [esp] + and eax, 0x0f + bt dword [go4k_groove_pattern],eax + jnc go4k_render_nogroove + sub ebx, dword 3000 +go4k_render_nogroove: + cmp ecx, ebx +%else + cmp ecx, dword SAMPLES_PER_TICK +%endif + jl go4k_render_sampleloop + pop ecx + inc ecx +%ifdef AUTHORING + mov dword[__4klang_current_tick], ecx +%endif + cmp ecx, dword MAX_TICKS + jl go4k_render_tickloop +%ifdef GO4K_USE_BUFFER_RECORDINGS + pop ecx +%endif + popad + ret 4 + +%ifdef USE_SECTIONS +section .g4kcodz code align=1 +%else +section .text +%endif +; //---------------------------------------------------------------------------------------- +; // the magic behind it :) +; //---------------------------------------------------------------------------------------- +; // IN : edi = instrument pointer +; // IN : esi = instrumet values +; // IN : ebx = instrument instructions +; // OUT : +; // DIRTY : +; //---------------------------------------------------------------------------------------- +go4k_VM_process: + lea WRK, [edi+8] ; // get current workspace pointer + mov ecx, WRK ; // ecx = workspace start +go4k_VM_process_loop: + movzx eax, byte [ebx] ; // get command byte + inc ebx + test eax, eax + je go4k_VM_process_done ; // command byte = 0? so done + call dword [eax*4+go4k_synth_commands] + add WRK, MAX_UNIT_SLOTS*4 ; // go to next workspace slot + jmp short go4k_VM_process_loop +go4k_VM_process_done: + add edi, go4k_instrument.size ; // go to next instrument voice + ret \ No newline at end of file diff --git a/4klang_old.h b/_4klang.h similarity index 72% rename from 4klang_old.h rename to _4klang.h index 668b979..8aa0545 100644 --- a/4klang_old.h +++ b/_4klang.h @@ -1,21 +1,21 @@ // some useful song defines for 4klang #define SAMPLE_RATE 44100 -#define BPM 133.000000 -#define MAX_INSTRUMENTS 5 -#define MAX_PATTERNS 52 -#define PATTERN_SIZE_SHIFT 4 +#define BPM 165.000000 +#define MAX_INSTRUMENTS 6 +#define MAX_PATTERNS 25 +#define PATTERN_SIZE_SHIFT 5 #define PATTERN_SIZE (1 << PATTERN_SIZE_SHIFT) #define MAX_TICKS (MAX_PATTERNS*PATTERN_SIZE) -#define SAMPLES_PER_TICK 4973 +#define SAMPLES_PER_TICK 4009 #define MAX_SAMPLES (SAMPLES_PER_TICK*MAX_TICKS) #define POLYPHONY 1 -#define INTEGER_16BIT -#define SAMPLE_TYPE short +#define FLOAT_32BIT +#define SAMPLE_TYPE float #define WINDOWS_OBJECT // declaration of the external synth render function, you'll always need that -extern "C" void __stdcall _4klang_render(void*); +extern "C" void __stdcall _su_render_song(void*); // declaration of the external envelope buffer. access only if you're song was exported with that option extern "C" float _4klang_envelope_buffer; // declaration of the external note buffer. access only if you're song was exported with that option diff --git a/4klang_old.inc b/_4klang.inc similarity index 54% rename from 4klang_old.inc rename to _4klang.inc index 955645b..eae70c1 100644 --- a/4klang_old.inc +++ b/_4klang.inc @@ -4,28 +4,27 @@ %endmacro %define USE_SECTIONS %define SAMPLE_RATE 44100 -%define MAX_INSTRUMENTS 5 +%define MAX_INSTRUMENTS 6 %define MAX_VOICES 1 %define HLD 1 -%define BPM 133.000000 -%define MAX_PATTERNS 52 -%define PATTERN_SIZE_SHIFT 4 +%define BPM 165.000000 +%define MAX_PATTERNS 25 +%define PATTERN_SIZE_SHIFT 5 %define PATTERN_SIZE (1 << PATTERN_SIZE_SHIFT) %define MAX_TICKS (MAX_PATTERNS*PATTERN_SIZE) -%define SAMPLES_PER_TICK 4973 -%define DEF_LFO_NORMALIZE 0.0000502645 +%define SAMPLES_PER_TICK 4009 +%define DEF_LFO_NORMALIZE 0.0000623583 %define MAX_SAMPLES (SAMPLES_PER_TICK*MAX_TICKS) -%define GO4K_USE_16BIT_OUTPUT +;%define GO4K_USE_16BIT_OUTPUT ;%define GO4K_USE_GROOVE_PATTERN ;%define GO4K_USE_ENVELOPE_RECORDINGS -%define GO4K_USE_NOTE_RECORDINGS +;%define GO4K_USE_NOTE_RECORDINGS %define GO4K_CLIP_OUTPUT %define GO4K_USE_DST %define GO4K_USE_DLL %define GO4K_USE_PAN %define GO4K_USE_GLOBAL_DLL %define GO4K_USE_ENV_CHECK -%define GO4K_USE_ENV_MOD_GM %define GO4K_USE_VCO_CHECK %define GO4K_USE_VCO_PHASE_OFFSET %define GO4K_USE_VCO_SHAPE @@ -33,12 +32,14 @@ %define GO4K_USE_VCO_MOD_DM %define GO4K_USE_VCO_MOD_CM %define GO4K_USE_VCF_CHECK +%define GO4K_USE_VCF_MOD_FM %define GO4K_USE_VCF_HIGH %define GO4K_USE_VCF_BAND %define GO4K_USE_VCF_PEAK %define GO4K_USE_VCF_STEREO %define GO4K_USE_DST_CHECK %define GO4K_USE_DST_SH +%define GO4K_USE_DLL_CHORUS %define GO4K_USE_DLL_CHORUS_CLAMP %define GO4K_USE_DLL_DAMP %define GO4K_USE_DLL_DC_FILTER @@ -451,52 +452,33 @@ section .g4kmuc1 data align=1 section .data align=1 %endif go4k_patterns - db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - db 38, HLD, 0, 0, 38, HLD, HLD, HLD, 0, 0, 38, 0, 48, 50, HLD, HLD, - db 0, 0, 50, 0, 38, HLD, HLD, HLD, 50, 0, 50, 0, 38, HLD, HLD, HLD, - db 0, 0, 38, 0, 38, HLD, 38, 38, 48, HLD, 50, HLD, 53, HLD, 52, HLD, - db 38, 0, 38, 0, 38, HLD, HLD, HLD, HLD, HLD, 0, 0, 38, HLD, 50, HLD, - db 0, 0, 50, 0, 38, 0, 38, 38, 46, HLD, 48, HLD, 57, HLD, 48, HLD, - db 46, HLD, 0, 0, 46, HLD, HLD, HLD, 0, 0, 46, 0, 47, HLD, 48, HLD, - db 0, 0, 48, 0, 36, HLD, HLD, HLD, 36, 0, 48, 0, 48, HLD, HLD, HLD, - db 38, HLD, 0, 0, 38, HLD, HLD, HLD, 0, 0, 38, 0, 48, HLD, 50, HLD, - db HLD, 0, 50, 0, 38, HLD, HLD, HLD, 38, HLD, 50, HLD, 48, HLD, 36, HLD, - db 43, HLD, 0, 0, 43, HLD, HLD, HLD, 55, HLD, 43, HLD, HLD, HLD, 55, HLD, - db 41, 0, 41, 0, 41, HLD, HLD, HLD, 53, HLD, 41, HLD, 41, HLD, HLD, HLD, - db 40, HLD, 0, 0, 40, HLD, HLD, HLD, 52, HLD, 40, HLD, HLD, HLD, 52, HLD, - db 45, 0, 45, 0, 45, HLD, HLD, HLD, 57, HLD, 45, HLD, 45, HLD, HLD, HLD, - db 50, HLD, 0, 0, 50, HLD, HLD, HLD, 62, HLD, 50, HLD, HLD, HLD, 62, HLD, - db 43, 0, 43, 0, 43, HLD, HLD, HLD, 55, HLD, 43, HLD, 43, HLD, HLD, HLD, - db 48, HLD, 0, 0, 48, HLD, HLD, HLD, 60, HLD, 48, HLD, HLD, HLD, 60, HLD, - db 48, 0, 48, 0, 48, HLD, HLD, HLD, 60, HLD, 48, HLD, 48, HLD, HLD, HLD, - db 48, HLD, HLD, 48, HLD, HLD, 48, HLD, HLD, HLD, 48, HLD, HLD, HLD, 48, HLD, - db HLD, HLD, 48, HLD, HLD, HLD, 48, 48, HLD, HLD, 48, HLD, HLD, HLD, HLD, HLD, - db 48, HLD, HLD, 48, HLD, HLD, 48, HLD, HLD, HLD, 48, HLD, HLD, HLD, HLD, HLD, - db HLD, HLD, 48, HLD, HLD, HLD, 48, HLD, HLD, HLD, 48, HLD, HLD, HLD, HLD, HLD, - db 48, HLD, 48, HLD, HLD, HLD, 48, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, - db 48, HLD, 48, HLD, HLD, HLD, 48, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 48, HLD, - db 48, HLD, HLD, HLD, HLD, HLD, 48, HLD, HLD, HLD, 48, HLD, HLD, HLD, HLD, HLD, - db 0, 0, 0, 0, 0, 0, 0, 0, 60, HLD, HLD, HLD, HLD, HLD, HLD, HLD, - db HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 60, HLD, HLD, HLD, 60, HLD, HLD, HLD, - db HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 60, HLD, HLD, HLD, HLD, HLD, HLD, HLD, - db HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 48, HLD, HLD, HLD, HLD, HLD, HLD, HLD, - db 74, HLD, HLD, HLD, HLD, HLD, 69, HLD, HLD, HLD, HLD, HLD, 72, HLD, HLD, HLD, - db HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 74, HLD, HLD, HLD, 76, HLD, HLD, HLD, - db 77, HLD, HLD, HLD, 76, HLD, 74, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, - db HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0, - db 74, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, - db HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, - db 70, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, - db 72, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, - db 76, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, - db 77, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, - db 79, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, - db 81, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, - db HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 76, HLD, HLD, HLD, HLD, HLD, HLD, HLD, - db 67, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, - db 69, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, - db 73, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, - db 71, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, + db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, HLD, HLD, HLD, 57, HLD, HLD, HLD, + db HLD, HLD, HLD, HLD, 57, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 57, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 57, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 57, HLD, HLD, HLD, + db HLD, HLD, HLD, HLD, 57, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 57, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 57, HLD, HLD, HLD, HLD, HLD, HLD, 57, 57, HLD, HLD, HLD, + db HLD, HLD, HLD, HLD, 57, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 57, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 57, 57, 57, 57, HLD, HLD, HLD, HLD, 57, HLD, HLD, HLD, + db HLD, HLD, HLD, HLD, 57, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 57, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 57, HLD, HLD, HLD, 57, 57, HLD, HLD, 57, HLD, HLD, HLD, + db HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, + db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, HLD, HLD, HLD, 46, HLD, + db 46, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 46, HLD, HLD, HLD, HLD, HLD, 46, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 46, HLD, HLD, HLD, HLD, HLD, + db 46, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 46, HLD, HLD, HLD, HLD, HLD, 46, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 46, HLD, 46, HLD, HLD, HLD, HLD, HLD, + db 46, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 46, HLD, HLD, HLD, HLD, HLD, 46, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 46, 46, 46, 46, HLD, HLD, HLD, HLD, + db 46, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 46, HLD, HLD, HLD, HLD, HLD, 46, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 46, 46, HLD, HLD, HLD, HLD, + db 48, HLD, 48, HLD, HLD, HLD, 48, HLD, 48, HLD, 48, HLD, HLD, HLD, 48, HLD, 48, HLD, 48, HLD, HLD, HLD, 48, HLD, 48, HLD, 48, HLD, HLD, HLD, 48, HLD, + db 50, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, + db 48, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, + db 46, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 48, HLD, 50, HLD, 53, HLD, + db 43, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 43, HLD, 45, HLD, 46, HLD, 48, HLD, + db 46, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 48, HLD, 50, HLD, + db 52, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 45, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, + db HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, HLD, 0, 0, 0, 0, 0, 0, 0, 0, + db 62, 0, 64, 0, 65, 0, 69, 0, 65, 0, 64, 0, 62, 0, 64, 0, 65, 0, 72, 0, 65, 0, 64, 0, 62, 0, 64, 0, 65, 0, 71, 0, + db 62, 0, 64, 0, 65, 0, 69, 0, 65, 0, 64, 0, 62, 0, 64, 0, 65, 0, 69, 0, 65, 0, 64, 0, 62, 0, 64, 0, 65, 0, 72, 0, + db 50, 0, 50, HLD, 50, 0, 50, HLD, 50, 0, 50, HLD, 50, 0, 50, HLD, 50, 0, 50, HLD, 50, 0, 50, HLD, 50, 0, 50, HLD, 50, 0, 50, HLD, + db 48, 0, 48, HLD, 48, 0, 48, HLD, 48, 0, 48, HLD, 48, 0, 48, HLD, 48, 0, 48, HLD, 48, 0, 48, HLD, 48, 0, 48, HLD, 48, 0, 48, HLD, + db 46, 0, 46, HLD, 46, 0, 46, HLD, 46, 0, 46, HLD, 46, 0, 46, HLD, 46, 0, 46, HLD, 46, 0, 46, HLD, 46, 0, 46, HLD, 46, 0, 46, HLD, + db 43, 0, 43, HLD, 43, 0, 43, HLD, 43, 0, 43, HLD, 43, 0, 43, HLD, 43, 0, 43, HLD, 43, 0, 43, HLD, 43, 0, 43, HLD, 43, 0, 43, HLD, + db 43, 0, 43, HLD, 43, 0, 43, HLD, 43, 0, 43, HLD, 43, 0, 43, HLD, 43, 0, 43, HLD, 43, 0, 43, HLD, 43, 0, 43, HLD, 43, 0, 43, 0, go4k_patterns_end %ifdef USE_SECTIONS section .g4kmuc2 data align=1 @@ -504,11 +486,12 @@ section .g4kmuc2 data align=1 section .data %endif go4k_pattern_lists -Instrument0List db 1, 2, 1, 3, 1, 2, 1, 3, 1, 2, 4, 5, 1, 3, 1, 2, 6, 7, 8, 9, 6, 7, 1, 2, 10, 11, 12, 13, 14, 15, 16, 17, 10, 11, 12, 13, 14, 15, 16, 17, 1, 2, 1, 3, 1, 2, 1, 3, 1, 2, 1, 3, -Instrument2List db 18, 19, 18, 19, 18, 19, 18, 19, 20, 21, 20, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 22, 21, 23, 19, 24, 21, 18, 19, 24, 21, 18, 19, -Instrument3List db 25, 26, 27, 26, 28, 26, 28, 26, 28, 26, 28, 26, 28, 26, 28, 26, 28, 26, 28, 26, 28, 26, 28, 26, 28, 26, 28, 26, 28, 26, 28, 26, 28, 26, 28, 26, 28, 26, 28, 26, 28, 26, 28, 26, 28, 26, 28, 26, 28, 26, 28, 26, -Instrument4List db 0, 0, 0, 0, 0, 0, 0, 0, 29, 30, 31, 32, 29, 30, 33, 32, 29, 30, 31, 32, 29, 30, 33, 34, 35, 36, 33, 37, 38, 39, 38, 37, 35, 36, 33, 37, 38, 39, 40, 39, 38, 41, 33, 34, 0, 0, 0, 0, 0, 0, 0, 0, -Instrument5List db 0, 0, 0, 0, 0, 0, 0, 0, 29, 30, 31, 32, 29, 30, 33, 32, 29, 30, 31, 32, 29, 30, 33, 34, 42, 43, 35, 44, 36, 33, 36, 36, 42, 43, 35, 44, 36, 45, 43, 42, 38, 41, 33, 34, 0, 0, 0, 0, 0, 0, 0, 0, +Instrument0List db 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 4, 2, 2, 2, 5, 2, 3, 2, 4, 2, 2, 2, 5, 6, +Instrument1List db 0, 0, 0, 0, 0, 0, 0, 7, 8, 9, 8, 10, 8, 9, 8, 11, 8, 9, 8, 10, 8, 9, 8, 11, 6, +Instrument2List db 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 6, +Instrument3List db 13, 14, 15, 16, 17, 18, 13, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +Instrument4List db 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 0, +Instrument5List db 0, 0, 0, 0, 0, 0, 0, 0, 22, 23, 24, 25, 22, 23, 24, 25, 22, 23, 24, 25, 22, 23, 24, 26, 0, go4k_pattern_lists_end %ifdef USE_SECTIONS section .g4kmuc3 data align=1 @@ -519,98 +502,118 @@ go4k_synth_instructions GO4K_BEGIN_CMDDEF(Instrument0) db GO4K_ENV_ID db GO4K_VCO_ID - db GO4K_FST_ID + db GO4K_DST_ID db GO4K_FOP_ID + db GO4K_ENV_ID db GO4K_VCO_ID - db GO4K_FST_ID - db GO4K_FST_ID db GO4K_FOP_ID - db GO4K_VCO_ID - db GO4K_FST_ID + db GO4K_VCF_ID + db GO4K_VCF_ID db GO4K_FOP_ID + db GO4K_VCF_ID + db GO4K_VCF_ID + db GO4K_VCF_ID + db GO4K_VCF_ID + db GO4K_VCF_ID + db GO4K_DST_ID + db GO4K_PAN_ID + db GO4K_DLL_ID + db GO4K_FOP_ID + db GO4K_DLL_ID + db GO4K_FOP_ID + db GO4K_OUT_ID + db GO4K_ENV_ID + db GO4K_FST_ID + db GO4K_ENV_ID + db GO4K_FST_ID + db GO4K_ENV_ID + db GO4K_FST_ID +GO4K_END_CMDDEF +GO4K_BEGIN_CMDDEF(Instrument1) + db GO4K_ENV_ID db GO4K_VCO_ID + db GO4K_DST_ID + db GO4K_FOP_ID + db GO4K_ENV_ID db GO4K_VCO_ID - db GO4K_VCO_ID + db GO4K_VCF_ID + db GO4K_VCF_ID db GO4K_FOP_ID db GO4K_FOP_ID db GO4K_VCF_ID - db GO4K_FOP_ID + db GO4K_VCF_ID + db GO4K_VCF_ID + db GO4K_VCF_ID db GO4K_PAN_ID + db GO4K_DLL_ID + db GO4K_FOP_ID + db GO4K_DLL_ID db GO4K_OUT_ID + db GO4K_ENV_ID + db GO4K_ENV_ID + db GO4K_FOP_ID + db GO4K_FST_ID GO4K_END_CMDDEF GO4K_BEGIN_CMDDEF(Instrument2) db GO4K_ENV_ID - db GO4K_FST_ID - db GO4K_ENV_ID - db GO4K_DST_ID - db GO4K_FST_ID - db GO4K_FOP_ID db GO4K_VCO_ID db GO4K_FOP_ID + db GO4K_VCF_ID + db GO4K_VCF_ID + db GO4K_VCF_ID db GO4K_PAN_ID + db GO4K_DLL_ID + db GO4K_FOP_ID + db GO4K_DLL_ID db GO4K_OUT_ID GO4K_END_CMDDEF GO4K_BEGIN_CMDDEF(Instrument3) db GO4K_ENV_ID + db GO4K_VCO_ID db GO4K_FST_ID - db GO4K_ENV_ID + db GO4K_FOP_ID + db GO4K_VCO_ID db GO4K_FST_ID db GO4K_FST_ID db GO4K_FOP_ID db GO4K_VCO_ID + db GO4K_FST_ID + db GO4K_FOP_ID + db GO4K_VCO_ID db GO4K_VCO_ID db GO4K_VCO_ID - db GO4K_VCF_ID - db GO4K_FOP_ID db GO4K_FOP_ID db GO4K_FOP_ID db GO4K_VCF_ID + db GO4K_VCF_ID + db GO4K_FOP_ID db GO4K_PAN_ID db GO4K_OUT_ID GO4K_END_CMDDEF GO4K_BEGIN_CMDDEF(Instrument4) db GO4K_ENV_ID - db GO4K_FST_ID db GO4K_VCO_ID db GO4K_VCO_ID - db GO4K_VCO_ID - db GO4K_FST_ID - db GO4K_FST_ID db GO4K_FOP_ID db GO4K_FOP_ID - db GO4K_FOP_ID - db GO4K_VCF_ID - db GO4K_VCF_ID - db GO4K_DST_ID db GO4K_PAN_ID db GO4K_DLL_ID db GO4K_FOP_ID db GO4K_DLL_ID - db GO4K_FOP_ID db GO4K_OUT_ID GO4K_END_CMDDEF GO4K_BEGIN_CMDDEF(Instrument5) db GO4K_ENV_ID - db GO4K_VCO_ID - db GO4K_FST_ID - db GO4K_FOP_ID - db GO4K_VCO_ID db GO4K_FST_ID db GO4K_FST_ID - db GO4K_FOP_ID - db GO4K_VCO_ID - db GO4K_FST_ID - db GO4K_FOP_ID - db GO4K_VCO_ID - db GO4K_VCO_ID + db GO4K_ENV_ID db GO4K_VCO_ID db GO4K_FOP_ID + db GO4K_VCO_ID db GO4K_FOP_ID db GO4K_VCF_ID - db GO4K_FOP_ID - db GO4K_DLL_ID + db GO4K_VCF_ID db GO4K_PAN_ID - db GO4K_DLL_ID db GO4K_OUT_ID GO4K_END_CMDDEF GO4K_BEGIN_CMDDEF(Global) @@ -631,7 +634,75 @@ section .data %endif go4k_synth_parameter_values GO4K_BEGIN_PARAMDEF(Instrument0) - GO4K_ENV ATTAC(9),DECAY(85),SUSTAIN(113),RELEASE(38),GAIN(119) + GO4K_ENV ATTAC(22),DECAY(64),SUSTAIN(0),RELEASE(0),GAIN(38) + GO4K_VCO TRANSPOSE(77),DETUNE(64),PHASE(19),GATES(0),COLOR(128),SHAPE(66),GAIN(70),FLAGS(SINE) + GO4K_DST DRIVE(78), SNHFREQ(128), FLAGS(0) + GO4K_FOP OP(FOP_MULP) + GO4K_ENV ATTAC(0),DECAY(68),SUSTAIN(0),RELEASE(0),GAIN(70) + GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(0),COLOR(64),SHAPE(2),GAIN(128),FLAGS(NOISE) + GO4K_FOP OP(FOP_MULP) + GO4K_VCF FREQUENCY(97),RESONANCE(128),VCFTYPE(PEAK) + GO4K_VCF FREQUENCY(108),RESONANCE(128),VCFTYPE(PEAK) + GO4K_FOP OP(FOP_ADDP) + GO4K_VCF FREQUENCY(100),RESONANCE(128),VCFTYPE(PEAK) + GO4K_VCF FREQUENCY(19),RESONANCE(98),VCFTYPE(HIGHPASS) + GO4K_VCF FREQUENCY(15),RESONANCE(128),VCFTYPE(BANDSTOP) + GO4K_VCF FREQUENCY(40),RESONANCE(74),VCFTYPE(BANDSTOP) + GO4K_VCF FREQUENCY(23),RESONANCE(42),VCFTYPE(HIGHPASS) + GO4K_DST DRIVE(97), SNHFREQ(128), FLAGS(0) + GO4K_PAN PANNING(59) + GO4K_DLL PREGAIN(27),DRY(128),FEEDBACK(112),DAMP(0),FREQUENCY(7),DEPTH(70),DELAY(1),COUNT(8) + GO4K_FOP OP(FOP_XCH) + GO4K_DLL PREGAIN(27),DRY(128),FEEDBACK(112),DAMP(0),FREQUENCY(16),DEPTH(55),DELAY(9),COUNT(8) + GO4K_FOP OP(FOP_XCH) + GO4K_OUT GAIN(128), AUXSEND(0) + GO4K_ENV ATTAC(38),DECAY(49),SUSTAIN(0),RELEASE(0),GAIN(128) + GO4K_FST AMOUNT(70),DEST(1*MAX_UNIT_SLOTS+1+FST_SET+FST_POP) + GO4K_ENV ATTAC(47),DECAY(54),SUSTAIN(0),RELEASE(0),GAIN(128) + GO4K_FST AMOUNT(19),DEST(7*MAX_UNIT_SLOTS+4+FST_SET+FST_POP) + GO4K_ENV ATTAC(54),DECAY(68),SUSTAIN(70),RELEASE(0),GAIN(128) + GO4K_FST AMOUNT(26),DEST(10*MAX_UNIT_SLOTS+4+FST_SET+FST_POP) +GO4K_END_PARAMDEF +GO4K_BEGIN_PARAMDEF(Instrument1) + GO4K_ENV ATTAC(7),DECAY(73),SUSTAIN(0),RELEASE(0),GAIN(128) + GO4K_VCO TRANSPOSE(90),DETUNE(0),PHASE(10),GATES(0),COLOR(63),SHAPE(94),GAIN(128),FLAGS(TRISAW) + GO4K_DST DRIVE(58), SNHFREQ(100), FLAGS(0) + GO4K_FOP OP(FOP_ADDP) + GO4K_ENV ATTAC(28),DECAY(45),SUSTAIN(0),RELEASE(0),GAIN(128) + GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(0),COLOR(64),SHAPE(99),GAIN(128),FLAGS(NOISE) + GO4K_VCF FREQUENCY(80),RESONANCE(128),VCFTYPE(PEAK) + GO4K_VCF FREQUENCY(75),RESONANCE(87),VCFTYPE(LOWPASS) + GO4K_FOP OP(FOP_MULP) + GO4K_FOP OP(FOP_ADDP) + GO4K_VCF FREQUENCY(11),RESONANCE(128),VCFTYPE(PEAK) + GO4K_VCF FREQUENCY(53),RESONANCE(128),VCFTYPE(BANDSTOP) + GO4K_VCF FREQUENCY(12),RESONANCE(76),VCFTYPE(HIGHPASS) + GO4K_VCF FREQUENCY(11),RESONANCE(70),VCFTYPE(HIGHPASS) + GO4K_PAN PANNING(64) + GO4K_DLL PREGAIN(21),DRY(128),FEEDBACK(20),DAMP(128),FREQUENCY(6),DEPTH(51),DELAY(9),COUNT(8) + GO4K_FOP OP(FOP_XCH) + GO4K_DLL PREGAIN(20),DRY(128),FEEDBACK(20),DAMP(128),FREQUENCY(2),DEPTH(63),DELAY(1),COUNT(8) + GO4K_OUT GAIN(128), AUXSEND(0) + GO4K_ENV ATTAC(75),DECAY(128),SUSTAIN(0),RELEASE(0),GAIN(128) + GO4K_ENV ATTAC(49),DECAY(71),SUSTAIN(121),RELEASE(0),GAIN(128) + GO4K_FOP OP(FOP_MULP) + GO4K_FST AMOUNT(0),DEST(1*MAX_UNIT_SLOTS+1+FST_SET+FST_POP) +GO4K_END_PARAMDEF +GO4K_BEGIN_PARAMDEF(Instrument2) + GO4K_ENV ATTAC(4),DECAY(53),SUSTAIN(0),RELEASE(0),GAIN(128) + GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(0),COLOR(64),SHAPE(102),GAIN(128),FLAGS(NOISE) + GO4K_FOP OP(FOP_MULP) + GO4K_VCF FREQUENCY(87),RESONANCE(128),VCFTYPE(HIGHPASS) + GO4K_VCF FREQUENCY(95),RESONANCE(128),VCFTYPE(BANDSTOP) + GO4K_VCF FREQUENCY(89),RESONANCE(71),VCFTYPE(BANDPASS) + GO4K_PAN PANNING(61) + GO4K_DLL PREGAIN(28),DRY(128),FEEDBACK(50),DAMP(29),FREQUENCY(0),DEPTH(0),DELAY(1),COUNT(8) + GO4K_FOP OP(FOP_XCH) + GO4K_DLL PREGAIN(32),DRY(128),FEEDBACK(40),DAMP(0),FREQUENCY(10),DEPTH(34),DELAY(9),COUNT(8) + GO4K_OUT GAIN(45), AUXSEND(0) +GO4K_END_PARAMDEF +GO4K_BEGIN_PARAMDEF(Instrument3) + GO4K_ENV ATTAC(23),DECAY(85),SUSTAIN(113),RELEASE(38),GAIN(119) GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(128),SHAPE(64),GAIN(128),FLAGS(TRISAW|LFO) GO4K_FST AMOUNT(52),DEST(11*MAX_UNIT_SLOTS+2+FST_SET) GO4K_FOP OP(FOP_POP) @@ -647,95 +718,47 @@ GO4K_BEGIN_PARAMDEF(Instrument0) GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(128),SHAPE(106),GAIN(128),FLAGS(SINE) GO4K_FOP OP(FOP_ADDP) GO4K_FOP OP(FOP_ADDP) - GO4K_VCF FREQUENCY(128),RESONANCE(89),VCFTYPE(LOWPASS|STEREO) + GO4K_VCF FREQUENCY(54),RESONANCE(120),VCFTYPE(LOWPASS|STEREO) + GO4K_VCF FREQUENCY(35),RESONANCE(70),VCFTYPE(BANDSTOP) GO4K_FOP OP(FOP_MULP) GO4K_PAN PANNING(63) - GO4K_OUT GAIN(128), AUXSEND(55) -GO4K_END_PARAMDEF -GO4K_BEGIN_PARAMDEF(Instrument2) - GO4K_ENV ATTAC(0),DECAY(83),SUSTAIN(0),RELEASE(0),GAIN(100) - GO4K_FST AMOUNT(128),DEST(0*MAX_UNIT_SLOTS+2+FST_SET) - GO4K_ENV ATTAC(0),DECAY(73),SUSTAIN(0),RELEASE(0),GAIN(128) - GO4K_DST DRIVE(27), SNHFREQ(128), FLAGS(0) - GO4K_FST AMOUNT(86),DEST(6*MAX_UNIT_SLOTS+1+FST_SET) - GO4K_FOP OP(FOP_MULP) - GO4K_VCO TRANSPOSE(38),DETUNE(128),PHASE(20),GATES(85),COLOR(64),SHAPE(88),GAIN(90),FLAGS(TRISAW) - GO4K_FOP OP(FOP_MULP) - GO4K_PAN PANNING(64) - GO4K_OUT GAIN(128), AUXSEND(0) -GO4K_END_PARAMDEF -GO4K_BEGIN_PARAMDEF(Instrument3) - GO4K_ENV ATTAC(0),DECAY(74),SUSTAIN(0),RELEASE(0),GAIN(93) - GO4K_FST AMOUNT(128),DEST(0*MAX_UNIT_SLOTS+2+FST_SET) - GO4K_ENV ATTAC(0),DECAY(57),SUSTAIN(64),RELEASE(0),GAIN(128) - GO4K_FST AMOUNT(66),DEST(6*MAX_UNIT_SLOTS+1+FST_SET) - GO4K_FST AMOUNT(58),DEST(7*MAX_UNIT_SLOTS+1+FST_SET) - GO4K_FOP OP(FOP_POP) - GO4K_VCO TRANSPOSE(63),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(127),GAIN(83),FLAGS(NOISE) - GO4K_VCO TRANSPOSE(62),DETUNE(59),PHASE(14),GATES(85),COLOR(64),SHAPE(89),GAIN(67),FLAGS(SINE) - GO4K_VCO TRANSPOSE(68),DETUNE(64),PHASE(0),GATES(85),COLOR(42),SHAPE(65),GAIN(14),FLAGS(TRISAW) - GO4K_VCF FREQUENCY(98),RESONANCE(128),VCFTYPE(LOWPASS) - GO4K_FOP OP(FOP_ADDP) - GO4K_FOP OP(FOP_ADDP) - GO4K_FOP OP(FOP_MULP) - GO4K_VCF FREQUENCY(22),RESONANCE(11),VCFTYPE(HIGHPASS) - GO4K_PAN PANNING(64) - GO4K_OUT GAIN(45), AUXSEND(28) + GO4K_OUT GAIN(71), AUXSEND(45) GO4K_END_PARAMDEF GO4K_BEGIN_PARAMDEF(Instrument4) - GO4K_ENV ATTAC(79),DECAY(96),SUSTAIN(95),RELEASE(82),GAIN(128) - GO4K_FST AMOUNT(68),DEST(0*MAX_UNIT_SLOTS+2+FST_SET) - GO4K_VCO TRANSPOSE(64),DETUNE(59),PHASE(0),GATES(85),COLOR(52),SHAPE(64),GAIN(128),FLAGS(PULSE) - GO4K_VCO TRANSPOSE(76),DETUNE(64),PHASE(64),GATES(85),COLOR(101),SHAPE(127),GAIN(111),FLAGS(TRISAW) - GO4K_VCO TRANSPOSE(32),DETUNE(64),PHASE(0),GATES(85),COLOR(128),SHAPE(64),GAIN(128),FLAGS(TRISAW|LFO) - GO4K_FST AMOUNT(61),DEST(2*MAX_UNIT_SLOTS+2+FST_SET) - GO4K_FST AMOUNT(63),DEST(3*MAX_UNIT_SLOTS+2+FST_SET) - GO4K_FOP OP(FOP_POP) + GO4K_ENV ATTAC(39),DECAY(62),SUSTAIN(0),RELEASE(30),GAIN(71) + GO4K_VCO TRANSPOSE(88),DETUNE(62),PHASE(14),GATES(0),COLOR(53),SHAPE(27),GAIN(72),FLAGS(SINE) + GO4K_VCO TRANSPOSE(88),DETUNE(66),PHASE(64),GATES(0),COLOR(113),SHAPE(64),GAIN(128),FLAGS(SINE) GO4K_FOP OP(FOP_ADDP) GO4K_FOP OP(FOP_MULP) - GO4K_VCF FREQUENCY(26),RESONANCE(36),VCFTYPE(PEAK) - GO4K_VCF FREQUENCY(80),RESONANCE(44),VCFTYPE(LOWPASS) - GO4K_DST DRIVE(109), SNHFREQ(85), FLAGS(0) GO4K_PAN PANNING(64) - GO4K_DLL PREGAIN(72),DRY(123),FEEDBACK(90),DAMP(49),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) + GO4K_DLL PREGAIN(95),DRY(128),FEEDBACK(83),DAMP(0),FREQUENCY(8),DEPTH(55),DELAY(17),COUNT(1) GO4K_FOP OP(FOP_XCH) - GO4K_DLL PREGAIN(98),DRY(112),FEEDBACK(62),DAMP(45),FREQUENCY(0),DEPTH(0),DELAY(18),COUNT(1) - GO4K_FOP OP(FOP_XCH) - GO4K_OUT GAIN(59), AUXSEND(59) + GO4K_DLL PREGAIN(81),DRY(128),FEEDBACK(90),DAMP(0),FREQUENCY(11),DEPTH(48),DELAY(18),COUNT(1) + GO4K_OUT GAIN(65), AUXSEND(52) GO4K_END_PARAMDEF GO4K_BEGIN_PARAMDEF(Instrument5) - GO4K_ENV ATTAC(64),DECAY(70),SUSTAIN(70),RELEASE(88),GAIN(128) - GO4K_VCO TRANSPOSE(69),DETUNE(64),PHASE(0),GATES(85),COLOR(128),SHAPE(64),GAIN(128),FLAGS(SINE|LFO) - GO4K_FST AMOUNT(72),DEST(11*MAX_UNIT_SLOTS+2+FST_SET) - GO4K_FOP OP(FOP_POP) - GO4K_VCO TRANSPOSE(73),DETUNE(64),PHASE(0),GATES(85),COLOR(128),SHAPE(64),GAIN(128),FLAGS(SINE|LFO) - GO4K_FST AMOUNT(56),DEST(12*MAX_UNIT_SLOTS+2+FST_SET) - GO4K_FST AMOUNT(56),DEST(12*MAX_UNIT_SLOTS+5+FST_SET) - GO4K_FOP OP(FOP_POP) - GO4K_VCO TRANSPOSE(77),DETUNE(64),PHASE(0),GATES(85),COLOR(128),SHAPE(64),GAIN(128),FLAGS(SINE|LFO) - GO4K_FST AMOUNT(72),DEST(13*MAX_UNIT_SLOTS+2+FST_SET) - GO4K_FOP OP(FOP_POP) - GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(0),SHAPE(64),GAIN(128),FLAGS(TRISAW) - GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(0),SHAPE(64),GAIN(128),FLAGS(TRISAW) - GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(0),SHAPE(64),GAIN(128),FLAGS(TRISAW) + GO4K_ENV ATTAC(11),DECAY(99),SUSTAIN(0),RELEASE(0),GAIN(120) + GO4K_FST AMOUNT(83),DEST(8*MAX_UNIT_SLOTS+4+FST_SET) + GO4K_FST AMOUNT(93),DEST(6*MAX_UNIT_SLOTS+5+FST_SET+FST_POP) + GO4K_ENV ATTAC(42),DECAY(0),SUSTAIN(128),RELEASE(39),GAIN(128) + GO4K_VCO TRANSPOSE(76),DETUNE(64),PHASE(8),GATES(0),COLOR(128),SHAPE(64),GAIN(128),FLAGS(SINE) GO4K_FOP OP(FOP_ADDP) - GO4K_FOP OP(FOP_ADDP) - GO4K_VCF FREQUENCY(104),RESONANCE(96),VCFTYPE(LOWPASS) + GO4K_VCO TRANSPOSE(64),DETUNE(71),PHASE(69),GATES(0),COLOR(73),SHAPE(75),GAIN(64),FLAGS(TRISAW) GO4K_FOP OP(FOP_MULP) - GO4K_DLL PREGAIN(64),DRY(64),FEEDBACK(64),DAMP(0),FREQUENCY(0),DEPTH(0),DELAY(19),COUNT(1) + GO4K_VCF FREQUENCY(39),RESONANCE(82),VCFTYPE(LOWPASS) + GO4K_VCF FREQUENCY(15),RESONANCE(128),VCFTYPE(PEAK) GO4K_PAN PANNING(64) - GO4K_DLL PREGAIN(128),DRY(0),FEEDBACK(0),DAMP(0),FREQUENCY(0),DEPTH(0),DELAY(20),COUNT(1) - GO4K_OUT GAIN(128), AUXSEND(47) + GO4K_OUT GAIN(128), AUXSEND(40) GO4K_END_PARAMDEF GO4K_BEGIN_PARAMDEF(Global) GO4K_ACC ACCTYPE(AUX) - GO4K_DLL PREGAIN(39),DRY(90),FEEDBACK(93),DAMP(62),FREQUENCY(0),DEPTH(0),DELAY(1),COUNT(8) + GO4K_DLL PREGAIN(45),DRY(128),FEEDBACK(126),DAMP(79),FREQUENCY(7),DEPTH(68),DELAY(1),COUNT(8) GO4K_FOP OP(FOP_XCH) - GO4K_DLL PREGAIN(41),DRY(128),FEEDBACK(93),DAMP(74),FREQUENCY(0),DEPTH(0),DELAY(9),COUNT(8) + GO4K_DLL PREGAIN(45),DRY(128),FEEDBACK(124),DAMP(76),FREQUENCY(8),DEPTH(45),DELAY(9),COUNT(8) GO4K_FOP OP(FOP_XCH) GO4K_ACC ACCTYPE(OUTPUT) GO4K_FOP OP(FOP_ADDP2) - GO4K_OUT GAIN(67), AUXSEND(0) + GO4K_OUT GAIN(64), AUXSEND(0) GO4K_END_PARAMDEF go4k_synth_parameter_values_end %ifdef USE_SECTIONS @@ -763,8 +786,6 @@ _go4k_delay_times dw 1516 dw 1580 dw 1642 - dw 19894 - dw 13263 - dw 29842 - dw 512 + dw 21381 + dw 12027 %endif diff --git a/shader_orig.glsl b/_shader.glsl similarity index 83% rename from shader_orig.glsl rename to _shader.glsl index f1b8e1f..54758b9 100644 --- a/shader_orig.glsl +++ b/_shader.glsl @@ -1,12 +1,20 @@ -6precision mediump float; +#version 460 + +precision mediump float; uniform vec2 u_resolution; uniform float u_time; +uniform vec2 u_mouse; +out vec4 my_fragColor; + uniform sampler2D texture_sampler; uniform sampler2D texts; struct Ray { - vec3 rd; - vec3 dir; + vec3 rayDir; + vec3 rayOrigin; + vec3 hitPos; + float distance; + bool isHit; }; mat2 scale(vec2 scale){ @@ -443,6 +451,23 @@ float pReflect(inout vec3 p, vec3 planeNormal, float offset) { //////////////////////////////////////////////////////////////// +float fOpUnion( float d1, float d2 ) +{ + return min(d1,d2); +} +float fOpSubtraction( float d1, float d2 ) +{ + return max(-d1,d2); +} +float opIntersection( float d1, float d2 ) +{ + return max(d1,d2); +} +float opXor(float d1, float d2 ) +{ + return max(min(d1,d2),-max(d1,d2)); +} + // The "Chamfer" flavour makes a 45-degree chamfered edge (the diagonal of a square of size ): float fOpUnionChamfer(float a, float b, float r) { return min(min(a, b), (a - r + b)*sqrt(0.5)); @@ -622,52 +647,40 @@ float sdCog(vec3 pos, float angle) { float sdHex(vec3 pos, float i, float angle) { - vec3 po = pos; - + vec3 po = pos; po.xz *= Rot(angle); po.yz *= Rot(angle); pR(po.yz, PI/2.); - float d1 = fHexagonCircumcircle(po, vec2(0.5+i, .1)); float d2 = fHexagonCircumcircle(po, vec2(0.2+i, .1)); return fOpDifferenceRound(d1,d2,0.1); - } -// Scene -vec2 mapScene(in vec3 p) { + +/* +* Scene geometry +* returns vec2(distance, material.id) +*/ +vec2 mapScene(in vec3 pos) { float mat = 0.; float d = 1e10; - //float dGround = p.y + 2.5; - //d = min(d, dGround); + float r = 10.; + float box1 = fBoxCheap(pos, vec3(r)); + float box2 = fBoxCheap(pos - vec3(0., 0., 15.), vec3(r - .5)); + float room = fOpSubtraction(box2, box1); + d = min(d,room); - vec3 po = p; - //po.y += sin(u_time); - // pMod3(po, vec3(3.)); - po.xy *= scale(vec2(1.3, 1.3)); + if ( d == room) mat = 1.; - const float num = 6.; - for (float i = 1.; i <= num; i++) { - // pos.z += i*.1; - float a = sdHex(po,i*0.35, u_time + abs( 2. + 0.4 * sin(u_time)) * i*3.1415/num); - d = min(d,a); - if (d == a) mat = 1. + mod(i,3.); - } - - /*float c2 = sdCog(p, u_time); + /* float c2 = sdCog(pos, 0.); d = min(d, c2); if ( d == c2) mat = 4.; +/* + float c3 = fBox(pos+vec3(0.0, .0, 0.), vec3(1., 1.,1.)); + d = min(d, c3); + if ( d == c3) mat = 4.; */ - // float c3 = fBox(p+vec3(0.5, .87, 0.), vec3(1., 1.,1.)); - // d = min(d, c3); - - - - // if ( d == c1) mat = 1.; - - //if ( d == c3) mat = 3.; - return vec2(d, mat); } @@ -675,18 +688,18 @@ vec3 castRay(vec3 ro, vec3 rd, inout vec3 pos) { float t = 0.0; float mat = 0.; float hit = 0.; - for(int i=0; i < 150; i++) { + for(int i=0; i < 200; i++) { pos = ro + rd * t; vec2 res = mapScene(pos); t += res.x; mat = res.y; - if (t > 80.) break; + if (t > 100.) break; if (res.x < abs(0.001*t) ) { hit = 1.; break; } } - if (t > 80.) t = -1.0; + if (t > 100.) t = -1.0; return vec3(t, mat, hit); } @@ -728,19 +741,6 @@ float softshadow( in vec3 ro, in vec3 rd, float mint, float maxt, float w ) return 0.25*(1.0+res)*(1.0+res)*(2.0-res); } -float castShadow(vec3 ro, vec3 rd) { - float res = 1.0; - float t = 0.001; - for(int i = 0; i < 40; i++) { - float h = mapScene(ro + t* rd).x; - res = min(res, 10.0*h/t); - if (abs(h) < (0.001*t) ) break; - t += h; - if (t > 20.) break; - } - return clamp(res,0., 1.); -} - vec3 calcNormal(vec3 pos) { vec2 e = vec2(.001, 0.); vec3 n = vec3( mapScene(pos+e.xyy).x - mapScene(pos-e.xyy).x, @@ -759,46 +759,46 @@ vec3 eRot(vec3 p, vec3 ax, float ro) { return mix(dot(ax,p)*ax, p, cos(ro)) + sin(ro)*cross(ax,p); } - - vec3 addPointLight(vec3 light_pos, vec3 light_color, float shininess, vec3 v, vec3 dir, vec3 n, float occ) { vec3 Ks = vec3( .5454 ); - vec3 Kd = vec3( 1. ); - vec3 ref = reflect( dir, n ); + vec3 Kd = vec3( .9 ); + vec3 ref = reflect( dir, n ); vec3 vl = normalize( v ); vec3 diffuse = Kd * vec3( max( 0.0, dot( vl, n ) ) ); vec3 specular = vec3( max( 0.0, dot( vl, ref ) ) ); - vec3 F = fresnel( Ks, normalize( vl - dir ), vl )*occ; + vec3 F = fresnel( Ks, normalize( vl - dir ), vl );//*occ; float shadow = softshadow(v+n*0.01,light_pos, .01, 30., 18.); - //float shadow = castShadow(v + n*0.02, light_pos); - specular = pow( specular, vec3( shininess ) )*occ; - return light_color * mix( diffuse, specular, F ) * shadow; - + specular = pow( specular, vec3( shininess ) );//*occ; + return light_color * shadow * occ; //* mix( diffuse, specular, F );// * shadow; } + + + + float getAmbientOcc(vec3 p, vec3 n) { float occ = 0.; - float weight = 1.; + float weight = 0.5; for (int i = 0; i < 8; i++) { float len = 0.01 + 0.02 * float(i*i); float dist = mapScene(p+n*len).x; occ += (len - dist) * weight; - weight *=0.85; + weight *=0.8; } - return 1.0 - clamp(0.6 * occ, 0., 1.); + return 1.0 - clamp(0.5 * occ, 0., 1.); } -vec3 shading(vec3 v, vec3 n, vec3 dir, float material) { +vec3 shading(vec3 p, vec3 nor, vec3 dir, float material) { float shininess = 1.; - float occ = getAmbientOcc(v,n); + float occ = getAmbientOcc(p,nor); vec3 outMaterial = vec3(0.1529, 0.1529, 0.1529); if (material == 0.) { - outMaterial = vec3(0.2863, 0.1059, 0.2431); + outMaterial = vec3(0.1216, 0.098, 0.1137); shininess = 1.5; } else if (material == 1.) { - outMaterial = vec3(0.3294, 0.0941, 0.6); - shininess = 0.6; + outMaterial = vec3(0.549, 0.549, 0.5529); + shininess = 1.0; } else if (material == 2.) { outMaterial = vec3(0.5804, 0.9647, 1.0); shininess = 1.; @@ -811,22 +811,22 @@ vec3 shading(vec3 v, vec3 n, vec3 dir, float material) { } vec3 lights = vec3(0.); - lights += addPointLight(vec3( 0., -40., -1. ),vec3(0.56, 0.44, 0.18)*2., shininess, v, dir, n, occ); - lights += addPointLight(vec3( -20.,4., 10. ),vec3(0.04, 0.2, 0.71)*2., shininess, v, dir, n, occ); + lights += addPointLight(vec3( 1., 0., 10. ),vec3(0.9882, 0.9882, 0.9882)*3., shininess, p, dir, nor, occ); + //lights += addPointLight(vec3( -2.,4., 1. ),vec3(0.04, 0.2, 0.71)*1., shininess, v, dir, n, occ); // lights += addPointLight(vec3( 2., -1.0, -1.0 ),vec3(0.12, 0.51, 0.63)*2., shininess, v,dir,n,occ ); - vec3 lightDir = vec3(0. , 4., 2.); - float sun_dif = clamp(dot(n, lightDir), 0., 1.); - float shadow = softshadow(v+n*0.01,lightDir, .01, 30., 18.); - lights += vec3(0.6627, 0.7098, 0.8863) * sun_dif*shadow*occ; //* shadow; //* mix( vec3(sun_dif), specular, F ) + vec3 lightDir = vec3(0. , 5., 10.); + float sun_dif = clamp(dot(nor, lightDir), 0., 1.); + float shadow = softshadow(p+nor*0.01,lightDir, .01, 30., 18.); + // lights += vec3(0.6627, 0.7098, 0.8863) * sun_dif*shadow*occ; //* shadow; //* mix( vec3(sun_dif), specular, F ) // final += texture( iChannel0, ref ).rgb * fresnel( Ks, n, -dir ); // vec3 col = vec3(0.4)* ref.x; - float ind = clamp( dot( n, normalize(lightDir*vec3(-1.0,.0,-1.0)) ), 0.0, 1.0 ); - lights += vec3(0.1333, 0.1333, 0.1216) * ind *occ; + float ind = clamp( dot( nor, normalize(lightDir*vec3(-1.0,.0,-1.0)) ), 0.0, 1.0 ); + // lights += vec3(0.1333, 0.1333, 0.1216) * ind *occ; return outMaterial * max(vec3(0.), lights); } @@ -848,55 +848,21 @@ vec3 postProcess(vec3 col) { float constrast = .5; col = mix(col, smoothstep(0.0, 1.0, col), constrast); - // Colour mapping col *= vec3(1.0, 1.0, 1.0); + + // gamma + col = pow( col, vec3(1.0/2.2) ); - col = pow( col, vec3(1.0/2.2) ); // gamma - - // fade in at the beginning - //col*=vec3(clamp((u_time-1.8)*0.5,0., 1.)); - - // fade out at the end - // col*=vec3(clamp((120.-u_time)*.35, 0., 1.)); - return col; } -vec3 getCameraRayDir(vec2 uv, vec3 camPos, vec3 camTarget) -{ - // Calculate camera's "orthonormal basis", i.e. its transform matrix components - vec3 camForward = normalize(camTarget-camPos ); - vec3 camRight = normalize(cross(vec3(.0, 1.0, 0.0), camForward)); - vec3 camUp = normalize(cross(camForward, camRight)); - float fov = 0.7; - vec3 vDir = normalize(uv.x * camRight + uv.y * camUp + camForward * fov); - return vDir; -} - -vec3 getCameraRayDir2(vec2 uv, vec3 camPos, vec3 lookAt, float zoom){ - vec3 f = normalize(lookAt - camPos); - vec3 r = cross(vec3(0.0,1.0,0.0),f); - vec3 u = cross(f,r); - vec3 c=camPos+f*zoom; - vec3 i=c+uv.x*r+uv.y*u; - return normalize(i-camPos); -} - -vec3 getCameraFov(vec2 uv, vec3 camPos, vec3 camTarget) { - vec3 camForward = normalize(camTarget-camPos); - vec3 camRight = normalize(cross(vec3(0.0, 1.0, 0.0), camForward)); - vec3 camUp = normalize(cross(camForward,camRight)); - float fov = 1.7; - // Depth of field - float dof = .25; - vec2 h = vec2( noise(gl_FragCoord.xy, .13), noise(gl_FragCoord.xy, .4)); - //vec3 h= rnd23(gl_FragCoord.xy); - vec3 voff = sqrt(h.x)*(camRight*sin(h.y*6.283)+camUp*cos(h.y*6.283))*dof; - // camTarget -=voff; - float focusdistance = 150.2; - return normalize(uv.x * camRight + uv.y * camUp + fov * camForward + voff * fov/focusdistance); +mat3 getCamera(vec3 camPosition, vec3 lookAt) { + vec3 camF = normalize(lookAt - camPosition); + vec3 camR = normalize(cross(vec3(0., 1., 0.), camF)); + vec3 camU = cross(camF, camR); + return mat3(camR, camU, camF); } vec3 applyFog(vec3 col, float t, vec3 rd, vec3 lightDir, float b ) { @@ -908,38 +874,32 @@ vec3 applyFog(vec3 col, float t, vec3 rd, vec3 lightDir, float b ) { return mix( col, fogColor, fogAmount ); } +/** + * renders the scene + * @param uv uv coordinate from gl_fragCoord + */ vec3 render(vec2 uv) { - - bool useDof = !true; - //vec2 uv = (2.0 * gl_FragCoord.xy - u_resolution.xy) / u_resolution.y; - - float angle = -2.4 +u_time*0.4; - //angle = 0.; - - // camera - vec3 camPos = vec3(0., 5., -3.); - vec3 camTarget = vec3(0., 0., 0.); - vec3 rayDir; - if (useDof) { - rayDir = getCameraFov(uv, camPos, camTarget); - } else { - rayDir = getCameraRayDir2(uv, camPos, camTarget, 1.0); - } - vec3 col = vec3(0.051, 0.0667, 0.1529); - //vec3 col = vec3(0.0314, 0.0118, 0.1255) + rayDir.y * 0.4; + // camera + vec3 camPos = vec3( sin(u_time)*2., 10., 30.); + vec3 lookAt = vec3(0., 0., 0.); + vec3 rayDir = getCamera(camPos, lookAt) * normalize(vec3(uv, 1.2)); + + vec3 hitPos = vec3(0.); vec3 t = castRay(camPos, rayDir, hitPos); vec3 rd = rayDir; - - if (t.z > 0.) { + + vec3 col = vec3(0.051, 0.0667, 0.1529); + if (t.x > 0.) { vec3 nor = calcNormal(hitPos); col = shading(hitPos, nor, rayDir , t.y); - float fogAmount = 0.04; - col = col*exp(-t.x*fogAmount) + applyFog(col, t.x, rd, vec3(0., .3, -1.), fogAmount) * (1.0-exp(-t.x*fogAmount)); + + // float fogAmount = 0.04; + // col = col*exp(-t.x*fogAmount) + applyFog(col, t.x, rd, vec3(0., .3, -1.), fogAmount) * (1.0-exp(-t.x*fogAmount)); - rayDir = normalize(reflect(rayDir, nor)); + /* rayDir = normalize(reflect(rayDir, nor)); vec3 rayOrigin = hitPos + (rayDir * 0.01); vec3 t2 = castReflectedRay(rayOrigin, rayDir, hitPos); @@ -948,16 +908,16 @@ vec3 render(vec2 uv) { nor = calcNormal(hitPos); col += 0.1 * shading(hitPos, nor, rayDir , t2.y); - /* rayDir = normalize(reflect(rayDir, nor)); + /* rayDir = normalize(reflect(rayDir, nor)); rayOrigin = hitPos + (rayDir * 0.01); vec3 t3 = castReflectedRay(rayOrigin, rayDir, hitPos); if (t3.z > 0.) { hitPos = rayOrigin + rayDir * t3.x; nor = calcNormal(hitPos); - col += 0.025 * shading(hitPos, nor, rayDir , t3.y); - } */ - } + col += 0.05 * shading(hitPos, nor, rayDir , t3.y); + } */ + // } } // pixelColor*exp(-distance*b) + fogColor*(1.0-exp(-distance*b)); @@ -973,9 +933,10 @@ void main() { vec3 finalColor = vec3(0.); + + /* const float AA_SIZE = 1.; float count = 0.0; -/* for (float aaY = 0.0; aaY < AA_SIZE; aaY++) { for (float aaX = 0.0; aaX < AA_SIZE; aaX++) { finalColor += render(getUV(vec2( aaX, aaY))); @@ -983,10 +944,17 @@ void main() } } finalColor /= count; */ - finalColor += render(getUV(vec2( 0.,0.))); - finalColor = postProcess(finalColor); + finalColor += render(getUV(vec2( 0., 0.))); - gl_FragColor = vec4(finalColor, 1.); + finalColor = postProcess(finalColor); + + // fade in at the beginning + // finalColor*=vec3(clamp((u_time-.5)*0.35,0., 1.)); + + // fade out at the end + // finalColor*=vec3(clamp((120.-u_time)*.35, 0., 1.)); + + my_fragColor = vec4(finalColor, 1.); } \ No newline at end of file diff --git a/base.config b/base.config index cdf26c4..afc39f3 100644 --- a/base.config +++ b/base.config @@ -47,7 +47,7 @@ TIME_DIVIDER=64145.453 TIME_BASE=bars AUDIO_DELAY=2048 -PROD_END_TIME=3209248 +PROD_END_TIME=3337536 ########### Final product ########### @@ -61,7 +61,7 @@ QUOTE=\" CRINKLER_ORDERTRIES=400 -AUDIO_BUFFER_ALLOCATED_LENGTH=3209248 +AUDIO_BUFFER_ALLOCATED_LENGTH=3337536 AUDIO_SAMPLING_RATE=44100 LOOP_END=5.0 LOOP_START=3.0 diff --git a/debug_4klang_wrapper.asm b/debug_4klang_wrapper.asm index d9e8e65..fcdaf42 100644 --- a/debug_4klang_wrapper.asm +++ b/debug_4klang_wrapper.asm @@ -1,17 +1,17 @@ ; Compofiller Studio by Yzi -; Wrapper for 4klang.asm, so we can get our debug/test DLL to export the constants. +; Wrapper 4klang.asm, so we can get our debug/test DLL to export the constants. %include "4klang.asm" music_sample_rate: - dd SAMPLE_RATE + dd SU_SAMPLE_RATE music_bpm: - dd __float32__(%[BPM]) + dd __float32__(%[SU_BPM]) music_length_samples: - dd MAX_SAMPLES + dd SU_LENGTH_IN_SAMPLES %macro EXPORT_FUNC 1 export %1 diff --git a/debug_audio.asm b/debug_audio.asm index d877e4e..5848f84 100644 --- a/debug_audio.asm +++ b/debug_audio.asm @@ -132,7 +132,7 @@ section .text ; the __imp__ versions refer to the corresponding import address table entries ; 4klang -extern __4klang_render@4 +extern _su_render_song@4 ; General Windows stuff extern _ExitProcess@4 @@ -183,7 +183,7 @@ StartAudioRendererThread: push 0 push 0 push dword [rendered_audio_buffer_ptr] - push __4klang_render@4 + push _su_render_song@4 push 0 push 0 ;call _CreateThread@24 @@ -380,8 +380,8 @@ StopAudio: ret -; // _4klang_render(myMuzik); -; CreateThread(0, 0, (LPTHREAD_START_ROUTINE)_4klang_render, myMuzik, 0, 0); +; // _su_render_song(myMuzik); +; CreateThread(0, 0, (LPTHREAD_START_ROUTINE)_su_render_song, myMuzik, 0, 0); ; Sleep(200); ; waveOutOpen( &hWaveOut, WAVE_MAPPER, &pcmFormat, NULL, 0, CALLBACK_NULL ); diff --git a/exemain.asm b/exemain.asm index 34e91e9..bcc17ad 100644 --- a/exemain.asm +++ b/exemain.asm @@ -266,7 +266,7 @@ extern __imp__CreateFontA@56 ; SelectObject@8 HGDIOBJ SelectObject(HDC hdc, HGDIOBJ h) extern _SelectObject@8 extern __imp__SelectObject@8 -; EHKÄ SetBkColor@8 COLORREF SetBkColor(HDC hdc, COLORREF color) +; EHK� SetBkColor@8 COLORREF SetBkColor(HDC hdc, COLORREF color) extern _SetBkColor@8 extern __imp__SetBkColor@8 ; SetTextColor@8 COLORREF SetTextColor(HDC hdc, COLORREF color) @@ -389,7 +389,7 @@ main: push 0 push 0 push rendered_audio_data + (AUDIO_DELAY * 4) - push __4klang_render@4 + push _su_render_song@4 push 0 push 0 call [__imp__CreateThread@24] @@ -536,7 +536,7 @@ section .cdennn section .text ; the __imp__ versions refer to the corresponding import address table entries -extern __4klang_render@4 +extern _su_render_song@4 extern _CreateThread@24 extern __imp__CreateThread@24 diff --git a/minified.config b/minified.config index 4be2eb8..10eeafa 100644 --- a/minified.config +++ b/minified.config @@ -7,16 +7,16 @@ include $(PARENT_CONFIG) # Visuals -EXE_LINKER_PROGRAM=Crinkler +EXE_LINKER_PROGRAM=GNU ld CRINKLER_ORDERTRIES=4000 SHADER_FILE=shader_minified.h -TIME_UNIFORM_NAME='m' -RESOLUTION_UNIFORM_NAME='v' -TEXTS_UNIFORM_NAME='f' +TIME_UNIFORM_NAME='n' +RESOLUTION_UNIFORM_NAME='r' +TEXTS_UNIFORM_NAME='k' USE_WIDECHAR_TEXTS=1 -TIME_DIVIDER=64145.453 -TIME_BASE=bars -TIME_DIVIDER_INT=64145 +TIME_DIVIDER=44100.000 +TIME_BASE=seconds +TIME_DIVIDER_INT=44100 TWO_PASS_RENDERING=1 SECOND_PASS_NEGATIVE_TIME=1 USE_TEXTS=0 diff --git a/music/4k_sointu.xrns b/music/4k_sointu.xrns new file mode 100644 index 0000000..7847c93 Binary files /dev/null and b/music/4k_sointu.xrns differ diff --git a/music/ba_reese.4ki b/music/ba_reese.4ki new file mode 100644 index 0000000..2e9bf8c Binary files /dev/null and b/music/ba_reese.4ki differ diff --git a/music/ba_reese.yml b/music/ba_reese.yml new file mode 100644 index 0000000..28bb3bb --- /dev/null +++ b/music/ba_reese.yml @@ -0,0 +1,78 @@ +name: ba_reese +numvoices: 1 +units: + - type: oscillator + id: 149 + parameters: {color: 128, detune: 64, gain: 128, lfo: 1, phase: 4, shape: 64, stereo: 1, transpose: 64, type: 1} + - type: send + id: 150 + parameters: {amount: 52, port: 1, sendpop: 1, stereo: 1, target: 159} + - type: oscillator + id: 152 + parameters: {color: 54, detune: 42, gain: 128, lfo: 1, phase: 55, shape: 63, stereo: 1, transpose: 41, type: 0} + - type: send + id: 153 + parameters: {amount: 75, port: 1, sendpop: 0, stereo: 1, target: 160} + - type: send + id: 154 + parameters: {amount: 59, port: 3, sendpop: 1, stereo: 1, target: 160} + - type: oscillator + id: 156 + parameters: {color: 128, detune: 64, gain: 128, lfo: 1, phase: 0, shape: 64, stereo: 1, transpose: 74, type: 0} + - type: send + id: 157 + parameters: {amount: 69, port: 1, sendpop: 1, stereo: 1, target: 161} + - type: envelope + id: 189 + parameters: {attack: 22, decay: 54, gain: 64, release: 64, stereo: 1, sustain: 64} + - type: oscillator + id: 159 + parameters: {color: 128, detune: 87, gain: 128, lfo: 0, phase: 64, shape: 65, stereo: 1, transpose: 64, type: 1, unison: 3} + - type: oscillator + id: 160 + parameters: {color: 128, detune: 60, gain: 128, lfo: 0, phase: 87, shape: 64, stereo: 1, transpose: 64, type: 1, unison: 2} + - type: oscillator + id: 161 + parameters: {color: 128, detune: 69, gain: 128, lfo: 0, phase: 0, shape: 106, stereo: 1, transpose: 64, type: 0, unison: 2} + - type: addp + id: 162 + parameters: {stereo: 1} + - type: addp + id: 16 + parameters: {stereo: 1} + - type: filter + id: 17 + parameters: {bandpass: 0, frequency: 62, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 91, stereo: 1} + - type: filter + id: 190 + parameters: {bandpass: 0, frequency: 93, highpass: 1, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 74, stereo: 1} + - type: xch + id: 195 + parameters: {stereo: 1} + - type: filter + id: 196 + parameters: {bandpass: 0, frequency: 94, highpass: 1, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 78, stereo: 1} + - type: mulp + id: 19 + parameters: {stereo: 1} + - type: distort + id: 193 + parameters: {drive: 96, stereo: 1} + - type: filter + id: 194 + parameters: {bandpass: 0, frequency: 12, highpass: 1, lowpass: 1, negbandpass: 1, neghighpass: 0, resonance: 128, stereo: 1} + - type: outaux + id: 21 + parameters: {auxgain: 21, outgain: 29, stereo: 1} + - type: oscillator + id: 191 + parameters: {color: 128, detune: 64, gain: 128, lfo: 1, phase: 0, shape: 64, stereo: 1, transpose: 40, type: 0, unison: 0} + - type: send + id: 192 + parameters: {amount: 71, port: 0, sendpop: 1, stereo: 1, target: 190, unit: 0, voice: 0} + - type: oscillator + id: 197 + parameters: {color: 128, detune: 64, gain: 128, lfo: 1, phase: 0, shape: 64, stereo: 1, transpose: 40, type: 0} + - type: send + id: 198 + parameters: {amount: 86, port: 0, sendpop: 1, stereo: 1, target: 196, unit: 0, voice: 0} diff --git a/music/ba_trana.4ki b/music/ba_trana.4ki new file mode 100644 index 0000000..fe4d7c3 Binary files /dev/null and b/music/ba_trana.4ki differ diff --git a/music/ba_trana.yml b/music/ba_trana.yml new file mode 100644 index 0000000..bc1b86f --- /dev/null +++ b/music/ba_trana.yml @@ -0,0 +1,48 @@ +name: ba_trana +numvoices: 1 +units: + - type: envelope + id: 173 + parameters: {attack: 39, decay: 81, gain: 128, release: 0, stereo: 0, sustain: 0} + - type: send + id: 174 + parameters: {amount: 85, port: 0, sendpop: 0, target: 181} + - type: send + id: 175 + parameters: {amount: 90, port: 3, sendpop: 1, target: 179} + - type: envelope + id: 176 + parameters: {attack: 33, decay: 57, gain: 128, release: 57, stereo: 0, sustain: 67} + - type: oscillator + id: 177 + parameters: {color: 98, detune: 64, gain: 101, lfo: 0, phase: 0, shape: 64, stereo: 0, transpose: 76, type: 0, unison: 0} + - type: oscillator + id: 179 + parameters: {color: 78, detune: 48, gain: 64, lfo: 0, phase: 85, shape: 97, stereo: 0, transpose: 64, type: 1, unison: 3} + - type: addp + id: 178 + parameters: {stereo: 0} + - type: mulp + id: 185 + parameters: {stereo: 0} + - type: filter + id: 181 + parameters: {bandpass: 0, frequency: 39, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 82, stereo: 0} + - type: filter + id: 182 + parameters: {bandpass: 0, frequency: 15, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 128, stereo: 0} + - type: pan + id: 183 + parameters: {panning: 64, stereo: 0} + - type: distort + id: 200 + parameters: {drive: 80, stereo: 1} + - type: compressor + id: 186 + parameters: {attack: 54, invgain: 61, ratio: 70, release: 59, stereo: 1, threshold: 29} + - type: mulp + id: 187 + parameters: {stereo: 1} + - type: outaux + id: 184 + parameters: {auxgain: 40, outgain: 58, stereo: 1} diff --git a/music/hihat.4ki b/music/hihat.4ki new file mode 100644 index 0000000..065cb06 Binary files /dev/null and b/music/hihat.4ki differ diff --git a/music/kick.4ki b/music/kick.4ki new file mode 100644 index 0000000..965fb99 Binary files /dev/null and b/music/kick.4ki differ diff --git a/music/ky_sine.4ki b/music/ky_sine.4ki new file mode 100644 index 0000000..fef8631 Binary files /dev/null and b/music/ky_sine.4ki differ diff --git a/music/snare.4ki b/music/snare.4ki new file mode 100644 index 0000000..7e099b4 Binary files /dev/null and b/music/snare.4ki differ diff --git a/music/song.yml b/music/song.yml new file mode 100644 index 0000000..5af582e --- /dev/null +++ b/music/song.yml @@ -0,0 +1,424 @@ +bpm: 165 +rowsperbeat: 4 +score: + tracks: + - numvoices: 1 + order: [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 2, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 4, 1, 1, 1, 2, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 4, -1, -1, 5, -1] + patterns: [[1, 1, 1, 1, 1, 1, 1, 1, 57, 1, 1, 1, 57, 1, 1, 1], [1, 1, 1, 1, 57, 1, 1, 1, 1, 1, 1, 1, 57, 1, 1, 1], [1, 1, 1, 1, 57, 1, 1, 1, 1, 1, 1, 57, 57, 1, 1, 1], [1, 1, 1, 1, 57, 57, 57, 57, 1, 1, 1, 1, 57, 1, 1, 1], [1, 1, 1, 1, 57, 1, 1, 1, 57, 57, 1, 1, 57, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1]] + - numvoices: 1 + order: [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 2, 1, 1, 1, 3, 1, 1, 1, 2, 1, 1, 1, 4, 1, 1, 1, 2, 1, 1, 1, 3, 1, 1, 1, 2, 1, 1, 1, 4, -1, -1, 5, -1] + patterns: [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 46, 1, 1, 1, 46, 1], [46, 1, 1, 1, 1, 1, 1, 1, 1, 1, 46, 1, 1, 1, 1, 1], [46, 1, 1, 1, 1, 1, 1, 1, 46, 1, 46, 1, 1, 1, 1, 1], [46, 1, 1, 1, 1, 1, 1, 1, 46, 46, 46, 46, 1, 1, 1, 1], [46, 1, 1, 1, 1, 1, 1, 1, 1, 1, 46, 46, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1]] + - numvoices: 1 + order: [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, 1, -1] + patterns: [[48, 1, 48, 1, 1, 1, 48, 1, 48, 1, 48, 1, 1, 1, 48, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1]] + - numvoices: 1 + order: [0, -1, 1, -1, 2, 3, 4, 5, 2, 6, 7, 8, 0, -1, -1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1] + patterns: [[50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [48, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [46, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 48, 1, 50, 1, 53, 1], [43, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 43, 1, 45, 1, 46, 1, 48, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 48, 1, 50, 1], [52, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [45, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1]] + - numvoices: 1 + order: [0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, -1, -1, -1, -1] + patterns: [[62, 0, 64, 0, 65, 0, 69, 0, 65, 0, 64, 0, 62, 0, 64, 0], [65, 0, 72, 0, 65, 0, 64, 0, 62, 0, 64, 0, 65, 0, 71, 0], [65, 0, 69, 0, 65, 0, 64, 0, 62, 0, 64, 0, 65, 0, 72, 0]] + - numvoices: 1 + order: [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 1, 1, 2, 2, 3, 3, 0, 0, 1, 1, 2, 2, 3, 3, 0, 0, 1, 1, 2, 2, 3, 3, 0, 0, 1, 1, 2, 2, 3, 4, -1, -1, -1, -1] + patterns: [[50, 0, 50, 1, 50, 0, 50, 1, 50, 0, 50, 1, 50, 0, 50, 1], [48, 0, 48, 1, 48, 0, 48, 1, 48, 0, 48, 1, 48, 0, 48, 1], [46, 0, 46, 1, 46, 0, 46, 1, 46, 0, 46, 1, 46, 0, 46, 1], [43, 0, 43, 1, 43, 0, 43, 1, 43, 0, 43, 1, 43, 0, 43, 1], [43, 0, 43, 1, 43, 0, 43, 1, 43, 0, 43, 1, 43, 0, 43, 0]] + rowsperpattern: 16 + length: 52 +patch: + - name: snare + numvoices: 1 + units: + - type: envelope + id: 1 + parameters: {attack: 22, decay: 64, gain: 38, release: 0, stereo: 0, sustain: 0} + - type: oscillator + id: 2 + parameters: {color: 128, detune: 64, gain: 70, lfo: 0, phase: 19, shape: 66, stereo: 0, transpose: 77, type: 0} + - type: distort + id: 3 + parameters: {drive: 78, stereo: 0} + - type: hold + id: 4 + parameters: {damp: 0, dry: 128, feedback: 96, holdfreq: 128, notetracking: 2, pregain: 40, stereo: 0} + - type: mulp + id: 5 + parameters: {panning: 64, stereo: 0} + - type: envelope + id: 6 + parameters: {attack: 0, auxgain: 64, decay: 68, gain: 70, outgain: 64, release: 0, stereo: 0, sustain: 0} + - type: noise + id: 81 + parameters: {gain: 128, shape: 2, stereo: 0} + - type: mulp + id: 82 + parameters: {stereo: 0} + - type: filter + id: 83 + parameters: {bandpass: 0, frequency: 97, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 128, stereo: 0} + - type: filter + id: 84 + parameters: {bandpass: 0, frequency: 108, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 128, stereo: 0} + - type: addp + id: 85 + parameters: {stereo: 0} + - type: filter + id: 86 + parameters: {bandpass: 0, frequency: 100, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 128, stereo: 0} + - type: filter + id: 87 + parameters: {bandpass: 0, frequency: 19, highpass: 1, lowpass: 0, negbandpass: 0, neghighpass: 0, resonance: 98, stereo: 0} + - type: filter + id: 88 + parameters: {bandpass: 0, frequency: 15, highpass: 1, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 128, stereo: 0} + - type: filter + id: 89 + parameters: {bandpass: 0, frequency: 40, highpass: 1, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 74, stereo: 0} + - type: filter + id: 90 + parameters: {bandpass: 0, frequency: 23, highpass: 1, lowpass: 0, negbandpass: 0, neghighpass: 0, resonance: 42, stereo: 0} + - type: distort + id: 91 + parameters: {drive: 97, stereo: 0} + - type: hold + id: 92 + parameters: {holdfreq: 128, stereo: 0} + - type: pan + id: 93 + parameters: {panning: 59, stereo: 0} + - type: delay + id: 94 + parameters: {damp: 0, dry: 128, feedback: 112, notetracking: 0, pregain: 27, stereo: 0} + varargs: [1116, 1188, 1276, 1356, 1422, 1492, 1556, 1618] + - type: xch + id: 95 + parameters: {stereo: 0} + - type: delay + id: 96 + parameters: {damp: 0, dry: 128, feedback: 112, notetracking: 0, pregain: 27, stereo: 0} + varargs: [1140, 1212, 1300, 1380, 1446, 1516, 1580, 1642] + - type: xch + id: 97 + parameters: {stereo: 0} + - type: outaux + id: 98 + parameters: {auxgain: 0, outgain: 128, stereo: 1} + - type: envelope + id: 99 + parameters: {attack: 38, decay: 49, gain: 128, release: 0, stereo: 0, sustain: 0} + - type: send + id: 100 + parameters: {amount: 70, port: 0, sendpop: 1, target: 2} + - type: envelope + id: 101 + parameters: {attack: 47, decay: 54, gain: 128, release: 0, stereo: 0, sustain: 0} + - type: send + id: 102 + parameters: {amount: 19, port: 0, sendpop: 1, target: 83} + - type: envelope + id: 103 + parameters: {attack: 54, decay: 68, gain: 128, release: 0, stereo: 0, sustain: 70} + - type: send + id: 104 + parameters: {amount: 26, port: 0, sendpop: 1, target: 86} + - name: kick + numvoices: 1 + units: + - type: envelope + id: 105 + parameters: {attack: 39, channel: 2, decay: 71, gain: 128, release: 0, stereo: 0, sustain: 0} + - type: send + id: 205 + parameters: {amount: 7, damp: 64, dry: 128, feedback: 125, notetracking: 0, port: 0, pregain: 40, sendpop: 0, stereo: 0, target: 184, unit: 0, voice: 0} + - type: oscillator + id: 106 + parameters: {color: 115, detune: 64, gain: 54, lfo: 0, phase: 42, shape: 114, stereo: 0, transpose: 88, type: 1, unison: 1} + - type: distort + id: 107 + parameters: {drive: 58, stereo: 0} + - type: hold + id: 108 + parameters: {holdfreq: 100, stereo: 0} + - type: mulp + id: 126 + parameters: {stereo: 0} + - type: envelope + id: 110 + parameters: {attack: 28, decay: 45, gain: 128, release: 0, stereo: 0, sustain: 0} + - type: noise + id: 111 + parameters: {gain: 102, shape: 84, stereo: 0} + - type: filter + id: 112 + parameters: {bandpass: 0, frequency: 93, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 63, stereo: 0} + - type: filter + id: 113 + parameters: {bandpass: 0, frequency: 75, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 87, stereo: 0} + - type: mulp + id: 10 + parameters: {stereo: 0} + - type: addp + id: 127 + parameters: {stereo: 0} + - type: filter + id: 12 + parameters: {bandpass: 0, frequency: 11, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 66, stereo: 0} + - type: filter + id: 13 + parameters: {bandpass: 0, frequency: 53, highpass: 1, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 128, stereo: 0} + - type: filter + id: 14 + parameters: {bandpass: 0, frequency: 7, highpass: 1, lowpass: 0, negbandpass: 0, neghighpass: 0, resonance: 76, stereo: 0} + - type: filter + id: 15 + parameters: {bandpass: 0, frequency: 13, highpass: 1, lowpass: 0, negbandpass: 0, neghighpass: 0, resonance: 54, stereo: 0} + - id: 203 + parameters: {} + - type: pan + id: 114 + parameters: {panning: 64, stereo: 0} + - type: delay + id: 115 + parameters: {damp: 128, dry: 128, feedback: 20, notetracking: 0, pregain: 21, stereo: 0} + varargs: [1140, 1212, 1300, 1380, 1446, 1516, 1580, 1642] + - type: xch + id: 116 + parameters: {stereo: 0} + - type: delay + id: 117 + parameters: {damp: 128, dry: 128, feedback: 20, notetracking: 0, pregain: 20, stereo: 0} + varargs: [1116, 1188, 1276, 1356, 1422, 1492, 1556, 1618] + - type: outaux + id: 118 + parameters: {auxgain: 0, outgain: 128, stereo: 1} + - type: envelope + id: 119 + parameters: {attack: 71, decay: 128, gain: 128, release: 0, stereo: 0, sustain: 74} + - type: envelope + id: 120 + parameters: {attack: 49, decay: 71, gain: 128, release: 0, stereo: 0, sustain: 121} + - type: mulp + id: 121 + parameters: {stereo: 0} + - type: send + id: 122 + parameters: {amount: 0, port: 0, sendpop: 1, target: 106} + - id: 204 + parameters: {} + - name: hihat + numvoices: 1 + units: + - type: envelope + id: 128 + parameters: {attack: 4, decay: 53, gain: 128, release: 0, stereo: 0, sustain: 0} + - type: noise + id: 129 + parameters: {gain: 128, shape: 102, stereo: 0} + - type: mulp + id: 130 + parameters: {stereo: 0} + - type: filter + id: 131 + parameters: {bandpass: 0, frequency: 87, highpass: 1, lowpass: 0, negbandpass: 0, neghighpass: 0, resonance: 128, stereo: 0} + - type: filter + id: 132 + parameters: {bandpass: 0, frequency: 95, highpass: 1, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 128, stereo: 0} + - type: filter + id: 133 + parameters: {bandpass: 1, frequency: 89, highpass: 0, lowpass: 0, negbandpass: 0, neghighpass: 0, resonance: 71, stereo: 0} + - type: pan + id: 134 + parameters: {panning: 61, stereo: 0} + - type: delay + id: 135 + parameters: {damp: 29, dry: 128, feedback: 50, notetracking: 0, pregain: 28, stereo: 0} + varargs: [1116, 1188, 1276, 1356, 1422, 1492, 1556, 1618] + - type: xch + id: 136 + parameters: {stereo: 0} + - type: delay + id: 137 + parameters: {damp: 0, dry: 128, feedback: 40, notetracking: 0, pregain: 32, stereo: 0} + varargs: [1140, 1212, 1300, 1380, 1446, 1516, 1580, 1642] + - type: outaux + id: 11 + parameters: {auxgain: 0, outgain: 45, stereo: 1} + - name: ba_reese + numvoices: 1 + units: + - type: envelope + id: 148 + parameters: {attack: 23, decay: 85, gain: 119, release: 38, stereo: 0, sustain: 113} + - type: pop + id: 188 + parameters: {stereo: 0} + - type: oscillator + id: 149 + parameters: {color: 128, detune: 64, gain: 128, lfo: 1, phase: 4, shape: 64, stereo: 0, transpose: 64, type: 1} + - type: send + id: 150 + parameters: {amount: 52, port: 1, sendpop: 1, stereo: 0, target: 159} + - type: oscillator + id: 152 + parameters: {color: 54, detune: 42, gain: 128, lfo: 1, phase: 55, shape: 63, stereo: 0, transpose: 41, type: 0} + - type: send + id: 153 + parameters: {amount: 75, port: 1, sendpop: 0, stereo: 0, target: 160} + - type: send + id: 154 + parameters: {amount: 59, port: 3, sendpop: 1, stereo: 0, target: 160} + - type: oscillator + id: 156 + parameters: {color: 128, detune: 64, gain: 128, lfo: 1, phase: 0, shape: 64, stereo: 0, transpose: 74, type: 0} + - type: send + id: 157 + parameters: {amount: 69, port: 1, sendpop: 1, stereo: 0, target: 161} + - type: envelope + id: 189 + parameters: {attack: 22, decay: 54, gain: 64, release: 64, stereo: 1, sustain: 64} + - type: oscillator + id: 159 + parameters: {color: 128, detune: 87, gain: 128, lfo: 0, phase: 64, shape: 65, stereo: 1, transpose: 64, type: 1, unison: 3} + - type: oscillator + id: 160 + parameters: {color: 128, detune: 60, gain: 128, lfo: 0, phase: 87, shape: 64, stereo: 1, transpose: 64, type: 1, unison: 2} + - type: oscillator + id: 161 + parameters: {color: 128, detune: 69, gain: 128, lfo: 0, phase: 0, shape: 106, stereo: 1, transpose: 64, type: 0, unison: 2} + - type: addp + id: 162 + parameters: {stereo: 1} + - type: addp + id: 16 + parameters: {stereo: 1} + - type: filter + id: 17 + parameters: {bandpass: 0, frequency: 62, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 91, stereo: 1} + - type: filter + id: 190 + parameters: {bandpass: 0, frequency: 93, highpass: 1, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 74, stereo: 0} + - type: xch + id: 195 + parameters: {stereo: 0} + - type: filter + id: 196 + parameters: {bandpass: 0, frequency: 94, highpass: 1, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 78, stereo: 0} + - type: mulp + id: 19 + parameters: {stereo: 1} + - type: distort + id: 193 + parameters: {drive: 96, stereo: 1} + - type: filter + id: 194 + parameters: {bandpass: 0, frequency: 10, highpass: 1, lowpass: 1, negbandpass: 1, neghighpass: 0, resonance: 128, stereo: 1} + - type: outaux + id: 21 + parameters: {auxgain: 21, outgain: 29, stereo: 1} + - type: oscillator + id: 191 + parameters: {color: 128, detune: 64, gain: 128, lfo: 1, phase: 0, shape: 64, stereo: 0, transpose: 40, type: 0, unison: 0} + - type: send + id: 192 + parameters: {amount: 71, port: 0, sendpop: 1, stereo: 0, target: 190, unit: 0, voice: 0} + - type: oscillator + id: 197 + parameters: {color: 128, detune: 64, gain: 128, lfo: 1, phase: 0, shape: 64, stereo: 0, transpose: 40, type: 0} + - type: send + id: 198 + parameters: {amount: 86, port: 0, sendpop: 1, stereo: 0, target: 196, unit: 0, voice: 0} + - name: ky_sine + numvoices: 1 + units: + - type: envelope + id: 163 + parameters: {attack: 39, decay: 62, gain: 71, release: 30, stereo: 0, sustain: 0} + - type: oscillator + id: 164 + parameters: {color: 53, detune: 62, gain: 72, lfo: 0, phase: 14, shape: 27, stereo: 0, transpose: 88, type: 0} + - type: oscillator + id: 165 + parameters: {color: 113, detune: 66, gain: 128, lfo: 0, phase: 64, shape: 64, stereo: 0, transpose: 88, type: 0} + - type: addp + id: 166 + parameters: {stereo: 0} + - type: mulp + id: 167 + parameters: {stereo: 0} + - type: pan + id: 168 + parameters: {panning: 64, stereo: 0} + - type: delay + id: 169 + parameters: {damp: 0, dry: 128, feedback: 86, notetracking: 2, pregain: 71, stereo: 0} + varargs: [64] + - type: xch + id: 170 + parameters: {stereo: 0} + - type: delay + id: 171 + parameters: {damp: 0, dry: 128, feedback: 90, notetracking: 2, pregain: 73, stereo: 0} + varargs: [36] + - type: outaux + id: 172 + parameters: {auxgain: 52, outgain: 84, stereo: 1} + - name: ba_trana + numvoices: 1 + units: + - type: envelope + id: 173 + parameters: {attack: 39, decay: 81, gain: 128, release: 0, stereo: 0, sustain: 0} + - type: send + id: 174 + parameters: {amount: 87, port: 0, sendpop: 0, target: 181} + - type: send + id: 175 + parameters: {amount: 90, port: 3, sendpop: 1, target: 179} + - type: envelope + id: 176 + parameters: {attack: 33, decay: 57, gain: 128, release: 57, stereo: 0, sustain: 67} + - type: oscillator + id: 177 + parameters: {color: 98, detune: 64, gain: 101, lfo: 0, phase: 0, shape: 64, stereo: 0, transpose: 76, type: 0, unison: 0} + - type: oscillator + id: 179 + parameters: {color: 78, detune: 48, gain: 64, lfo: 0, phase: 85, shape: 97, stereo: 0, transpose: 64, type: 1, unison: 3} + - type: addp + id: 178 + parameters: {stereo: 0} + - type: mulp + id: 185 + parameters: {stereo: 0} + - type: filter + id: 181 + parameters: {bandpass: 0, frequency: 39, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 82, stereo: 0} + - type: filter + id: 182 + parameters: {bandpass: 0, frequency: 15, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 128, stereo: 0} + - type: pan + id: 183 + parameters: {panning: 64, stereo: 0} + - type: distort + id: 200 + parameters: {drive: 80, stereo: 1} + - type: compressor + id: 186 + parameters: {attack: 48, invgain: 87, ratio: 54, release: 56, stereo: 1, threshold: 46} + - type: mulp + id: 187 + parameters: {stereo: 1} + - type: outaux + id: 184 + parameters: {auxgain: 35, outgain: 85, stereo: 1} + - id: 202 + parameters: {} + - name: Global + numvoices: 1 + units: + - type: in + id: 7 + parameters: {channel: 2, stereo: 1} + - type: delay + id: 8 + parameters: {damp: 77, dry: 128, feedback: 114, notetracking: 0, pregain: 50, stereo: 1} + varargs: [1116, 1188, 1276, 1356, 1422, 1492, 1556, 1618, 1140, 1212, 1300, 1380, 1446, 1516, 1580, 1642] + - type: out + id: 9 + parameters: {gain: 128, stereo: 1} diff --git a/music/song_optimized.yml b/music/song_optimized.yml new file mode 100644 index 0000000..eea44cb --- /dev/null +++ b/music/song_optimized.yml @@ -0,0 +1,396 @@ +bpm: 165 +rowsperbeat: 4 +score: + tracks: + - numvoices: 1 + order: [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 2, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 4, 1, 1, 1, 2, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 4, -1, -1, 5, -1] + patterns: [[1, 1, 1, 1, 1, 1, 1, 1, 57, 1, 1, 1, 57, 1, 1, 1], [1, 1, 1, 1, 57, 1, 1, 1, 1, 1, 1, 1, 57, 1, 1, 1], [1, 1, 1, 1, 57, 1, 1, 1, 1, 1, 1, 57, 57, 1, 1, 1], [1, 1, 1, 1, 57, 57, 57, 57, 1, 1, 1, 1, 57, 1, 1, 1], [1, 1, 1, 1, 57, 1, 1, 1, 57, 57, 1, 1, 57, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1]] + - numvoices: 1 + order: [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 2, 1, 1, 1, 3, 1, 1, 1, 2, 1, 1, 1, 4, 1, 1, 1, 2, 1, 1, 1, 3, 1, 1, 1, 2, 1, 1, 1, 4, -1, -1, 5, -1] + patterns: [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 46, 1, 1, 1, 46, 1], [46, 1, 1, 1, 1, 1, 1, 1, 1, 1, 46, 1, 1, 1, 1, 1], [46, 1, 1, 1, 1, 1, 1, 1, 46, 1, 46, 1, 1, 1, 1, 1], [46, 1, 1, 1, 1, 1, 1, 1, 46, 46, 46, 46, 1, 1, 1, 1], [46, 1, 1, 1, 1, 1, 1, 1, 1, 1, 46, 46, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1]] + - numvoices: 1 + order: [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, 1, -1] + patterns: [[48, 1, 48, 1, 1, 1, 48, 1, 48, 1, 48, 1, 1, 1, 48, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1]] + - numvoices: 1 + order: [0, -1, 1, -1, 2, 3, 4, 5, 2, 6, 7, 8, 0, -1, -1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1] + patterns: [[50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [48, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [46, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 48, 1, 50, 1, 53, 1], [43, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 43, 1, 45, 1, 46, 1, 48, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 48, 1, 50, 1], [52, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [45, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1]] + - numvoices: 1 + order: [0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, -1, -1, -1, -1] + patterns: [[62, 0, 64, 0, 65, 0, 69, 0, 65, 0, 64, 0, 62, 0, 64, 0], [65, 0, 72, 0, 65, 0, 64, 0, 62, 0, 64, 0, 65, 0, 71, 0], [65, 0, 69, 0, 65, 0, 64, 0, 62, 0, 64, 0, 65, 0, 72, 0]] + - numvoices: 1 + order: [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 1, 1, 2, 2, 3, 3, 0, 0, 1, 1, 2, 2, 3, 3, 0, 0, 1, 1, 2, 2, 3, 3, 0, 0, 1, 1, 2, 2, 3, 4, -1, -1, -1, -1] + patterns: [[50, 0, 50, 1, 50, 0, 50, 1, 50, 0, 50, 1, 50, 0, 50, 1], [48, 0, 48, 1, 48, 0, 48, 1, 48, 0, 48, 1, 48, 0, 48, 1], [46, 0, 46, 1, 46, 0, 46, 1, 46, 0, 46, 1, 46, 0, 46, 1], [43, 0, 43, 1, 43, 0, 43, 1, 43, 0, 43, 1, 43, 0, 43, 1], [43, 0, 43, 1, 43, 0, 43, 1, 43, 0, 43, 1, 43, 0, 43, 0]] + rowsperpattern: 16 + length: 52 +patch: + - name: snare + numvoices: 1 + units: + - type: envelope + id: 1 + parameters: {attack: 22, decay: 64, gain: 38, release: 0, stereo: 0, sustain: 0} + - type: oscillator + id: 2 + parameters: {color: 128, detune: 64, gain: 70, lfo: 0, phase: 19, shape: 66, stereo: 0, transpose: 77, type: 0} + - type: distort + id: 3 + parameters: {drive: 78, stereo: 0} + - type: hold + id: 4 + parameters: {damp: 0, dry: 128, feedback: 96, holdfreq: 128, notetracking: 2, pregain: 40, stereo: 0} + - type: mulp + id: 5 + parameters: {panning: 64, stereo: 0} + - type: envelope + id: 6 + parameters: {attack: 0, auxgain: 64, decay: 68, gain: 70, outgain: 64, release: 0, stereo: 0, sustain: 0} + - type: noise + id: 81 + parameters: {gain: 128, shape: 2, stereo: 0} + - type: mulp + id: 82 + parameters: {stereo: 0} + - type: filter + id: 83 + parameters: {bandpass: 0, frequency: 97, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 128, stereo: 0} + - type: filter + id: 84 + parameters: {bandpass: 0, frequency: 108, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 128, stereo: 0} + - type: addp + id: 85 + parameters: {stereo: 0} + - type: filter + id: 86 + parameters: {bandpass: 0, frequency: 100, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 128, stereo: 0} + - type: filter + id: 87 + parameters: {bandpass: 0, frequency: 19, highpass: 1, lowpass: 0, negbandpass: 0, neghighpass: 0, resonance: 98, stereo: 0} + - type: filter + id: 88 + parameters: {bandpass: 0, frequency: 15, highpass: 1, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 128, stereo: 0} + - type: filter + id: 89 + parameters: {bandpass: 0, frequency: 40, highpass: 1, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 74, stereo: 0} + - type: filter + id: 90 + parameters: {bandpass: 0, frequency: 23, highpass: 1, lowpass: 0, negbandpass: 0, neghighpass: 0, resonance: 42, stereo: 0} + - type: distort + id: 91 + parameters: {drive: 97, stereo: 0} + - type: hold + id: 92 + parameters: {holdfreq: 128, stereo: 0} + - type: pan + id: 93 + parameters: {panning: 59, stereo: 0} + - type: delay + id: 94 + parameters: {damp: 0, dry: 128, feedback: 112, notetracking: 0, pregain: 27, stereo: 0} + varargs: [1116, 1188, 1276, 1356, 1422, 1492, 1556, 1618] + - type: xch + id: 95 + parameters: {stereo: 0} + - type: delay + id: 96 + parameters: {damp: 0, dry: 128, feedback: 112, notetracking: 0, pregain: 27, stereo: 0} + varargs: [1140, 1212, 1300, 1380, 1446, 1516, 1580, 1642] + - type: xch + id: 97 + parameters: {stereo: 0} + - type: outaux + id: 98 + parameters: {auxgain: 0, outgain: 128, stereo: 1} + - type: envelope + id: 99 + parameters: {attack: 38, decay: 49, gain: 128, release: 0, stereo: 0, sustain: 0} + - type: send + id: 100 + parameters: {amount: 70, port: 0, sendpop: 1, target: 2} + - type: envelope + id: 101 + parameters: {attack: 47, decay: 54, gain: 128, release: 0, stereo: 0, sustain: 0} + - type: send + id: 102 + parameters: {amount: 19, port: 0, sendpop: 1, target: 83} + - type: envelope + id: 103 + parameters: {attack: 54, decay: 68, gain: 128, release: 0, stereo: 0, sustain: 70} + - type: send + id: 104 + parameters: {amount: 26, port: 0, sendpop: 1, target: 86} + - name: kick + numvoices: 1 + units: + - type: envelope + id: 105 + parameters: {attack: 39, channel: 2, decay: 71, gain: 128, release: 0, stereo: 0, sustain: 0} + - type: send + id: 205 + parameters: {amount: 7, damp: 64, dry: 128, feedback: 125, notetracking: 0, port: 0, pregain: 40, sendpop: 0, stereo: 0, target: 184, unit: 0, voice: 0} + - type: oscillator + id: 106 + parameters: {color: 115, detune: 64, gain: 54, lfo: 0, phase: 42, shape: 114, stereo: 0, transpose: 88, type: 1, unison: 1} + - type: distort + id: 107 + parameters: {drive: 58, stereo: 0} + - type: hold + id: 108 + parameters: {holdfreq: 100, stereo: 0} + - type: mulp + id: 126 + parameters: {stereo: 0} + - type: envelope + id: 110 + parameters: {attack: 28, decay: 45, gain: 128, release: 0, stereo: 0, sustain: 0} + - type: noise + id: 111 + parameters: {gain: 102, shape: 84, stereo: 0} + - type: filter + id: 112 + parameters: {bandpass: 0, frequency: 93, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 63, stereo: 0} + - type: filter + id: 113 + parameters: {bandpass: 0, frequency: 75, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 87, stereo: 0} + - type: mulp + id: 10 + parameters: {stereo: 0} + - type: addp + id: 127 + parameters: {stereo: 0} + - type: filter + id: 12 + parameters: {bandpass: 0, frequency: 11, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 66, stereo: 0} + - type: filter + id: 13 + parameters: {bandpass: 0, frequency: 53, highpass: 1, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 128, stereo: 0} + - type: filter + id: 14 + parameters: {bandpass: 0, frequency: 7, highpass: 1, lowpass: 0, negbandpass: 0, neghighpass: 0, resonance: 76, stereo: 0} + - type: filter + id: 15 + parameters: {bandpass: 0, frequency: 13, highpass: 1, lowpass: 0, negbandpass: 0, neghighpass: 0, resonance: 54, stereo: 0} + - type: pan + id: 114 + parameters: {panning: 64, stereo: 0} + - type: outaux + id: 118 + parameters: {auxgain: 0, outgain: 128, stereo: 1} + - type: envelope + id: 119 + parameters: {attack: 71, decay: 128, gain: 128, release: 0, stereo: 0, sustain: 74} + - type: envelope + id: 120 + parameters: {attack: 49, decay: 71, gain: 128, release: 0, stereo: 0, sustain: 121} + - type: mulp + id: 121 + parameters: {stereo: 0} + - type: send + id: 122 + parameters: {amount: 0, port: 0, sendpop: 1, target: 106} + - name: hihat + numvoices: 1 + units: + - type: envelope + id: 128 + parameters: {attack: 4, decay: 53, gain: 128, release: 0, stereo: 0, sustain: 0} + - type: noise + id: 129 + parameters: {gain: 128, shape: 102, stereo: 0} + - type: mulp + id: 130 + parameters: {stereo: 0} + - type: filter + id: 131 + parameters: {bandpass: 0, frequency: 87, highpass: 1, lowpass: 0, negbandpass: 0, neghighpass: 0, resonance: 128, stereo: 0} + - type: filter + id: 132 + parameters: {bandpass: 0, frequency: 95, highpass: 1, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 128, stereo: 0} + - type: filter + id: 133 + parameters: {bandpass: 1, frequency: 89, highpass: 0, lowpass: 0, negbandpass: 0, neghighpass: 0, resonance: 71, stereo: 0} + - type: pan + id: 134 + parameters: {panning: 61, stereo: 0} + - type: outaux + id: 11 + parameters: {auxgain: 0, outgain: 45, stereo: 1} + - name: ba_reese + numvoices: 1 + units: + - type: envelope + id: 148 + parameters: {attack: 23, decay: 85, gain: 119, release: 38, stereo: 0, sustain: 113} + - type: pop + id: 188 + parameters: {stereo: 0} + - type: oscillator + id: 149 + parameters: {color: 128, detune: 64, gain: 128, lfo: 1, phase: 4, shape: 64, stereo: 0, transpose: 64, type: 1} + - type: send + id: 150 + parameters: {amount: 52, port: 1, sendpop: 1, stereo: 0, target: 159} + - type: oscillator + id: 152 + parameters: {color: 54, detune: 42, gain: 128, lfo: 1, phase: 55, shape: 63, stereo: 0, transpose: 41, type: 0} + - type: send + id: 153 + parameters: {amount: 75, port: 1, sendpop: 0, stereo: 0, target: 160} + - type: send + id: 154 + parameters: {amount: 59, port: 3, sendpop: 1, stereo: 0, target: 160} + - type: oscillator + id: 156 + parameters: {color: 128, detune: 64, gain: 128, lfo: 1, phase: 0, shape: 64, stereo: 0, transpose: 74, type: 0} + - type: send + id: 157 + parameters: {amount: 69, port: 1, sendpop: 1, stereo: 0, target: 161} + - type: envelope + id: 189 + parameters: {attack: 22, decay: 54, gain: 64, release: 64, stereo: 1, sustain: 64} + - type: oscillator + id: 159 + parameters: {color: 128, detune: 87, gain: 128, lfo: 0, phase: 64, shape: 65, stereo: 1, transpose: 64, type: 1, unison: 3} + - type: oscillator + id: 160 + parameters: {color: 128, detune: 60, gain: 128, lfo: 0, phase: 87, shape: 64, stereo: 1, transpose: 64, type: 1, unison: 2} + - type: addp + id: 162 + parameters: {stereo: 1} + - type: oscillator + id: 161 + parameters: {color: 128, detune: 69, gain: 128, lfo: 0, phase: 0, shape: 106, stereo: 1, transpose: 64, type: 0, unison: 2} + - type: addp + id: 16 + parameters: {stereo: 1} + - type: filter + id: 17 + parameters: {bandpass: 0, frequency: 62, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 91, stereo: 1} + - type: filter + id: 190 + parameters: {bandpass: 0, frequency: 93, highpass: 1, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 74, stereo: 0} + - type: xch + id: 195 + parameters: {stereo: 0} + - type: filter + id: 196 + parameters: {bandpass: 0, frequency: 94, highpass: 1, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 78, stereo: 0} + - type: mulp + id: 19 + parameters: {stereo: 1} + - type: distort + id: 193 + parameters: {drive: 96, stereo: 1} + - type: filter + id: 194 + parameters: {bandpass: 0, frequency: 10, highpass: 1, lowpass: 1, negbandpass: 1, neghighpass: 0, resonance: 128, stereo: 1} + - type: outaux + id: 21 + parameters: {auxgain: 21, outgain: 29, stereo: 1} + - type: oscillator + id: 191 + parameters: {color: 128, detune: 64, gain: 128, lfo: 1, phase: 0, shape: 64, stereo: 0, transpose: 40, type: 0, unison: 0} + - type: send + id: 192 + parameters: {amount: 71, port: 0, sendpop: 1, stereo: 0, target: 190, unit: 0, voice: 0} + - type: oscillator + id: 197 + parameters: {color: 128, detune: 64, gain: 128, lfo: 1, phase: 0, shape: 64, stereo: 0, transpose: 40, type: 0} + - type: send + id: 198 + parameters: {amount: 86, port: 0, sendpop: 1, stereo: 0, target: 196, unit: 0, voice: 0} + - name: ky_sine + numvoices: 1 + units: + - type: envelope + id: 163 + parameters: {attack: 39, decay: 62, gain: 71, release: 30, stereo: 0, sustain: 0} + - type: oscillator + id: 164 + parameters: {color: 53, detune: 62, gain: 72, lfo: 0, phase: 14, shape: 27, stereo: 0, transpose: 88, type: 0} + - type: oscillator + id: 165 + parameters: {color: 113, detune: 66, gain: 128, lfo: 0, phase: 64, shape: 64, stereo: 0, transpose: 88, type: 0} + - type: addp + id: 166 + parameters: {stereo: 0} + - type: mulp + id: 167 + parameters: {stereo: 0} + - type: pan + id: 168 + parameters: {panning: 64, stereo: 0} + - type: delay + id: 169 + parameters: {damp: 0, dry: 128, feedback: 86, notetracking: 2, pregain: 71, stereo: 0} + varargs: [64] + - type: xch + id: 170 + parameters: {stereo: 0} + - type: delay + id: 171 + parameters: {damp: 0, dry: 128, feedback: 90, notetracking: 2, pregain: 73, stereo: 0} + varargs: [36] + - type: outaux + id: 172 + parameters: {auxgain: 52, outgain: 84, stereo: 1} + - name: ba_trana + numvoices: 1 + units: + - type: envelope + id: 173 + parameters: {attack: 39, decay: 81, gain: 128, release: 0, stereo: 0, sustain: 0} + - type: send + id: 174 + parameters: {amount: 87, port: 0, sendpop: 0, target: 181} + - type: send + id: 175 + parameters: {amount: 90, port: 3, sendpop: 1, target: 179} + - type: envelope + id: 176 + parameters: {attack: 33, decay: 57, gain: 128, release: 57, stereo: 0, sustain: 67} + - type: oscillator + id: 177 + parameters: {color: 98, detune: 64, gain: 101, lfo: 0, phase: 0, shape: 64, stereo: 0, transpose: 76, type: 0, unison: 0} + - type: oscillator + id: 179 + parameters: {color: 78, detune: 48, gain: 64, lfo: 0, phase: 85, shape: 97, stereo: 0, transpose: 64, type: 1, unison: 3} + - type: addp + id: 178 + parameters: {stereo: 0} + - type: mulp + id: 185 + parameters: {stereo: 0} + - type: filter + id: 181 + parameters: {bandpass: 0, frequency: 39, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 82, stereo: 0} + - type: filter + id: 182 + parameters: {bandpass: 0, frequency: 15, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 128, stereo: 0} + - type: pan + id: 183 + parameters: {panning: 64, stereo: 0} + - type: distort + id: 200 + parameters: {drive: 80, stereo: 1} + - type: compressor + id: 186 + parameters: {attack: 48, invgain: 87, ratio: 54, release: 56, stereo: 1, threshold: 46} + - type: mulp + id: 187 + parameters: {stereo: 1} + - type: outaux + id: 184 + parameters: {auxgain: 35, outgain: 85, stereo: 1} + - name: Global + numvoices: 1 + units: + - type: in + id: 7 + parameters: {channel: 2, stereo: 1} + - type: delay + id: 8 + parameters: {damp: 77, dry: 128, feedback: 114, notetracking: 0, pregain: 50, stereo: 1} + varargs: [1116, 1188, 1276, 1356, 1422, 1492, 1556, 1618, 1140, 1212, 1300, 1380, 1446, 1516, 1580, 1642] + - type: out + id: 9 + parameters: {gain: 128, stereo: 1} diff --git a/shader.glsl b/shader.glsl index 31e6028..05fc5cc 100644 --- a/shader.glsl +++ b/shader.glsl @@ -1,17 +1,12 @@ precision mediump float; uniform vec2 u_resolution; uniform float u_time; -uniform vec2 u_mouse; - uniform sampler2D texture_sampler; uniform sampler2D texts; struct Ray { - vec3 rayDir; - vec3 rayOrigin; - vec3 hitPos; - float distance; - bool isHit; + vec3 rd; + vec3 dir; }; mat2 scale(vec2 scale){ @@ -627,47 +622,52 @@ float sdCog(vec3 pos, float angle) { float sdHex(vec3 pos, float i, float angle) { - vec3 po = pos; + vec3 po = pos; + po.xz *= Rot(angle); po.yz *= Rot(angle); pR(po.yz, PI/2.); + float d1 = fHexagonCircumcircle(po, vec2(0.5+i, .1)); float d2 = fHexagonCircumcircle(po, vec2(0.2+i, .1)); return fOpDifferenceRound(d1,d2,0.1); + } - -/* -* Scene geometry -* returns vec2(distance, material.id) -*/ -vec2 mapScene(in vec3 pos) { +// Scene +vec2 mapScene(in vec3 p) { float mat = 0.; float d = 1e10; //float dGround = p.y + 2.5; //d = min(d, dGround); - vec3 po = pos; + vec3 po = p; //po.y += sin(u_time); // pMod3(po, vec3(3.)); - po.xz *= Rot(.745); + po.xy *= scale(vec2(1.3, 1.3)); - const float num = 6.; + const float num = 6.; for (float i = 1.; i <= num; i++) { // pos.z += i*.1; float a = sdHex(po,i*0.35, u_time + abs( 2. + 0.4 * sin(u_time)) * i*3.1415/num); d = min(d,a); if (d == a) mat = 1. + mod(i,3.); - } - /* float c2 = sdCog(pos, 0.); + } + + /*float c2 = sdCog(p, u_time); d = min(d, c2); if ( d == c2) mat = 4.; -/* - float c3 = fBox(pos+vec3(0.0, .0, 0.), vec3(1., 1.,1.)); - d = min(d, c3); - if ( d == c3) mat = 4.; */ + // float c3 = fBox(p+vec3(0.5, .87, 0.), vec3(1., 1.,1.)); + // d = min(d, c3); + + + + // if ( d == c1) mat = 1.; + + //if ( d == c3) mat = 3.; + return vec2(d, mat); } @@ -728,6 +728,19 @@ float softshadow( in vec3 ro, in vec3 rd, float mint, float maxt, float w ) return 0.25*(1.0+res)*(1.0+res)*(2.0-res); } +float castShadow(vec3 ro, vec3 rd) { + float res = 1.0; + float t = 0.001; + for(int i = 0; i < 40; i++) { + float h = mapScene(ro + t* rd).x; + res = min(res, 10.0*h/t); + if (abs(h) < (0.001*t) ) break; + t += h; + if (t > 20.) break; + } + return clamp(res,0., 1.); +} + vec3 calcNormal(vec3 pos) { vec2 e = vec2(.001, 0.); vec3 n = vec3( mapScene(pos+e.xyy).x - mapScene(pos-e.xyy).x, @@ -746,6 +759,8 @@ vec3 eRot(vec3 p, vec3 ax, float ro) { return mix(dot(ax,p)*ax, p, cos(ro)) + sin(ro)*cross(ax,p); } + + vec3 addPointLight(vec3 light_pos, vec3 light_color, float shininess, vec3 v, vec3 dir, vec3 n, float occ) { vec3 Ks = vec3( .5454 ); vec3 Kd = vec3( 1. ); @@ -755,7 +770,8 @@ vec3 addPointLight(vec3 light_pos, vec3 light_color, float shininess, vec3 v, ve vec3 specular = vec3( max( 0.0, dot( vl, ref ) ) ); vec3 F = fresnel( Ks, normalize( vl - dir ), vl )*occ; float shadow = softshadow(v+n*0.01,light_pos, .01, 30., 18.); - specular = pow( specular, vec3( shininess ) )*occ; + //float shadow = castShadow(v + n*0.02, light_pos); + specular = pow( specular, vec3( shininess ) )*occ; return light_color * mix( diffuse, specular, F ) * shadow; } @@ -832,21 +848,55 @@ vec3 postProcess(vec3 col) { float constrast = .5; col = mix(col, smoothstep(0.0, 1.0, col), constrast); + // Colour mapping col *= vec3(1.0, 1.0, 1.0); - - // gamma - col = pow( col, vec3(1.0/2.2) ); + col = pow( col, vec3(1.0/2.2) ); // gamma + + // fade in at the beginning + //col*=vec3(clamp((u_time-1.8)*0.5,0., 1.)); + + // fade out at the end + // col*=vec3(clamp((120.-u_time)*.35, 0., 1.)); + return col; } +vec3 getCameraRayDir(vec2 uv, vec3 camPos, vec3 camTarget) +{ + // Calculate camera's "orthonormal basis", i.e. its transform matrix components + vec3 camForward = normalize(camTarget-camPos ); + vec3 camRight = normalize(cross(vec3(.0, 1.0, 0.0), camForward)); + vec3 camUp = normalize(cross(camForward, camRight)); + float fov = 0.7; + vec3 vDir = normalize(uv.x * camRight + uv.y * camUp + camForward * fov); + return vDir; +} -mat3 getCamera(vec3 camPosition, vec3 lookAt) { - vec3 camF = normalize(lookAt - camPosition); - vec3 camR = normalize(cross(vec3(0., 1., 0.), camF)); - vec3 camU = cross(camF, camR); - return mat3(camR, camU, camF); + +vec3 getCameraRayDir2(vec2 uv, vec3 camPos, vec3 lookAt, float zoom){ + vec3 f = normalize(lookAt - camPos); + vec3 r = cross(vec3(0.0,1.0,0.0),f); + vec3 u = cross(f,r); + vec3 c=camPos+f*zoom; + vec3 i=c+uv.x*r+uv.y*u; + return normalize(i-camPos); +} + +vec3 getCameraFov(vec2 uv, vec3 camPos, vec3 camTarget) { + vec3 camForward = normalize(camTarget-camPos); + vec3 camRight = normalize(cross(vec3(0.0, 1.0, 0.0), camForward)); + vec3 camUp = normalize(cross(camForward,camRight)); + float fov = 1.7; + // Depth of field + float dof = .25; + vec2 h = vec2( noise(gl_FragCoord.xy, .13), noise(gl_FragCoord.xy, .4)); + //vec3 h= rnd23(gl_FragCoord.xy); + vec3 voff = sqrt(h.x)*(camRight*sin(h.y*6.283)+camUp*cos(h.y*6.283))*dof; + // camTarget -=voff; + float focusdistance = 150.2; + return normalize(uv.x * camRight + uv.y * camUp + fov * camForward + voff * fov/focusdistance); } vec3 applyFog(vec3 col, float t, vec3 rd, vec3 lightDir, float b ) { @@ -858,24 +908,31 @@ vec3 applyFog(vec3 col, float t, vec3 rd, vec3 lightDir, float b ) { return mix( col, fogColor, fogAmount ); } -/** - * renders the scene - * @param uv uv coordinate from gl_fragCoord - */ vec3 render(vec2 uv) { - - // camera - vec3 camPos = vec3( sin(u_time)*2., 0., 4.); - vec3 lookAt = vec3(0., 0., 0.); - vec3 rayDir = getCamera(camPos, lookAt) * normalize(vec3(uv, 1.2)); + bool useDof = !true; + //vec2 uv = (2.0 * gl_FragCoord.xy - u_resolution.xy) / u_resolution.y; + + float angle = -2.4 +u_time*0.4; + //angle = 0.; + + // camera + vec3 camPos = vec3(0., 5., -3.); + vec3 camTarget = vec3(0., 0., 0.); + vec3 rayDir; + if (useDof) { + rayDir = getCameraFov(uv, camPos, camTarget); + } else { + rayDir = getCameraRayDir2(uv, camPos, camTarget, 1.0); + } + vec3 col = vec3(0.051, 0.0667, 0.1529); + //vec3 col = vec3(0.0314, 0.0118, 0.1255) + rayDir.y * 0.4; vec3 hitPos = vec3(0.); vec3 t = castRay(camPos, rayDir, hitPos); vec3 rd = rayDir; - - vec3 col = vec3(0.051, 0.0667, 0.1529); + if (t.z > 0.) { vec3 nor = calcNormal(hitPos); col = shading(hitPos, nor, rayDir , t.y); @@ -891,15 +948,15 @@ vec3 render(vec2 uv) { nor = calcNormal(hitPos); col += 0.1 * shading(hitPos, nor, rayDir , t2.y); - /* rayDir = normalize(reflect(rayDir, nor)); + /* rayDir = normalize(reflect(rayDir, nor)); rayOrigin = hitPos + (rayDir * 0.01); vec3 t3 = castReflectedRay(rayOrigin, rayDir, hitPos); if (t3.z > 0.) { hitPos = rayOrigin + rayDir * t3.x; nor = calcNormal(hitPos); - col += 0.05 * shading(hitPos, nor, rayDir , t3.y); - } */ + col += 0.025 * shading(hitPos, nor, rayDir , t3.y); + } */ } } // pixelColor*exp(-distance*b) + fogColor*(1.0-exp(-distance*b)); @@ -916,10 +973,9 @@ void main() { vec3 finalColor = vec3(0.); - - /* const float AA_SIZE = 1.; float count = 0.0; +/* for (float aaY = 0.0; aaY < AA_SIZE; aaY++) { for (float aaX = 0.0; aaX < AA_SIZE; aaX++) { finalColor += render(getUV(vec2( aaX, aaY))); @@ -927,17 +983,10 @@ void main() } } finalColor /= count; */ + finalColor += render(getUV(vec2( 0.,0.))); - finalColor += render(getUV(vec2( 0.,0.))); - finalColor = postProcess(finalColor); - - // fade in at the beginning - // finalColor*=vec3(clamp((u_time-.5)*0.35,0., 1.)); - - // fade out at the end - // finalColor*=vec3(clamp((120.-u_time)*.35, 0., 1.)); - + gl_FragColor = vec4(finalColor, 1.); } \ No newline at end of file diff --git a/shader_minified.h b/shader_minified.h index 4a382df..dd1daff 100644 --- a/shader_minified.h +++ b/shader_minified.h @@ -1,210 +1,18 @@ // Generated with Shader Minifier 1.3.6 (https://github.com/laurentlb/Shader_Minifier/) #ifndef SHADER_MINIFIED_H_ # define SHADER_MINIFIED_H_ -# define VAR_texts "f" -# define VAR_texture_sampler "i" -# define VAR_u_mouse "k" -# define VAR_u_resolution "v" -# define VAR_u_time "m" +# define VAR_my_fragColor "v" +# define VAR_u_resolution "r" +# define VAR_u_time "n" const char *__temp_cleaned_shader_glsl = - "uniform vec2 v;" - "uniform float m;" - "uniform vec2 k;" - "uniform sampler2D i,f;struct Ray{vec3 rayDir;vec3 rayOrigin;vec3 hitPos;float distance;bool isHit;};" - "const float r=2.*acos(-1.),d=sqrt(5.)*.5+.5;" - "float x(vec3 v,vec2 m)" - "{" - "vec3 f=abs(v);" - "return max(f.y-m.y,max(f.x*sqrt(3.)*.5+f.z*.5,f.z)-m.x);" - "}" - "void x(inout vec2 v)" - "{" - "float m=acos(-1.)/2.;" - "v=cos(m)*v+sin(m)*vec2(v.y,-v.x);" - "}" - "float p(float v,float m)" - "{" - "return min(-.1,max(v,m))+length(max(vec2(.1+v,.1+m),vec2(0)));" - "}" - "mat2 p(float v)" - "{" - "float m=sin(v),f=cos(v);" - "return mat2(f,-m,m,f);" - "}" - "float p(vec3 v,float m,float i)" - "{" - "vec3 f=v;" - "f.xz*=p(i);" - "f.yz*=p(i);" - "x(f.yz);" - "float r=x(f,vec2(.5+m,.1)),n=x(f,vec2(.2+m,.1));" - "return p(r,-n);" - "}" - "vec2 n(vec3 v)" - "{" - "float f=0.,r=1e10;" - "vec3 i=v;" - "i.xz*=p(.745);" - "for(float x=1.;x<=6.;x++)" - "{" - "float n=p(i,x*.35,m+abs(2.+.4*sin(m))*x*3.1415/6.);" - "r=min(r,n);" - "if(r==n)" - "f=1.+mod(x,3.);" - "}" - "return vec2(r,f);" - "}" - "vec3 n(vec3 v,vec3 m,inout vec3 f)" - "{" - "float r=0.,i=0.,x=0.;" - "for(int e=0;e<150;e++)" - "{" - "f=v+m*r;" - "vec2 d=n(f);" - "r+=d.x;" - "i=d.y;" - "if(r>80.)" - "break;" - "if(d.x80.)" - "r=-1.;" - "return vec3(r,i,x);" - "}" - "vec3 x(vec3 v,vec3 m,vec3 f)" - "{" - "float r=0.,i=0.,x=0.;" - "for(int e=0;e<50;e++)" - "{" - "f=v+m*r;" - "vec2 d=n(f);" - "r+=d.x;" - "i=d.y;" - "if(r>40.)" - "break;" - "if(d.x40.)" - "r=-1.;" - "return vec3(r,i,x);" - "}" - "float n(vec3 v,vec3 m)" - "{" - "float r=1.,f=.01;" - "for(int i=0;i<40;i++)" - "{" - "if(f>30.)" - "break;" - "float x=n(v+f*m).x;" - "r=min(r,x/(18.*f));" - "f+=clamp(x,.005,.5);" - "if(r<-1.||f>30.)" - "break;" - "}" - "r=max(r,-1.);" - "return.25*(1.+r)*(1.+r)*(2.-r);" - "}" - "vec3 s(vec3 v)" - "{" - "vec2 m=vec2(.001,0);" - "vec3 r=vec3(n(v+m.xyy).x-n(v-m.xyy).x,n(v+m.yxy).x-n(v-m.yxy).x,n(v+m.yyx).x-n(v-m.yyx).x);" - "return normalize(r);" - "}" - "vec3 s(vec3 v,vec3 m)" - "{" - "vec3 r=vec3(.5454);" - "return r+(1.-r)*pow(clamp(1.-dot(v,m),0.,1.),5.);" - "}" - "vec3 n(vec3 v,vec3 m,float r,vec3 f,vec3 i,vec3 x,float y)" - "{" - "vec3 d=normalize(f),e=vec3(max(0.,dot(d,reflect(i,x)))),z=s(normalize(d-i),d)*y;" - "float k=n(f+x*.01,v);" - "e=pow(e,vec3(r))*y;" - "return m*mix(vec3(1)*vec3(max(0.,dot(d,x))),e,z)*k;" - "}" - "float t(vec3 v,vec3 m)" - "{" - "float r=0.,f=1.;" - "for(int i=0;i<8;i++)" - "{" - "float x=.01+.02*float(i*i),d=n(v+m*x).x;" - "r+=(x-d)*f;" - "f*=.85;" - "}" - "return 1.-clamp(.6*r,0.,1.);" - "}" - "vec3 n(vec3 v,vec3 m,vec3 f,float r)" - "{" - "float i=1.,x=t(v,m);" - "vec3 d=vec3(.1529);" - "if(r==0.)" - "d=vec3(.2863,.1059,.2431),i=1.5;" - "else if(r==1.)" - "d=vec3(.3294,.0941,.6),i=.6;" - "else if(r==2.)" - "d=vec3(.5804,.9647,1),i=1.;" - "else if(r==3.)" - "d=vec3(0),i=1e2;" - "else if(r==4.)" - "d=vec3(.9961,1,.9922),i=.3;" - "vec3 e=vec3(0);" - "e+=n(vec3(0,-40,-1),vec3(.56,.44,.18)*2.,i,v,f,m,x);" - "e+=n(vec3(-20,4,10),vec3(.04,.2,.71)*2.,i,v,f,m,x);" - "vec3 z=vec3(0,4,2);" - "float s=n(v+m*.01,z);" - "e+=vec3(.6627,.7098,.8863)*clamp(dot(m,z),0.,1.)*s*x;" - "e+=vec3(.1333,.1333,.1216)*clamp(dot(m,normalize(z*vec3(-1,0,-1))),0.,1.)*x;" - "return d*max(vec3(0),e);" - "}" - "vec3 t(vec3 r)" - "{" - "vec2 i=gl_FragCoord.xy/v.xy;" - "r=mix(r,r*smoothstep(.8,.4,length(i-vec2(.5))),.9);" - "r=mix(r,smoothstep(0.,1.,r),.5);" - "r*=vec3(1);" - "return pow(r,vec3(1./2.2));" - "}" - "mat3 e(vec3 m)" - "{" - "vec3 v=normalize(vec3(0)-m),r=normalize(cross(vec3(0,1,0),v));" - "return mat3(r,cross(v,r),v);" - "}" - "vec3 w(vec2 v)" - "{" - "vec3 r=vec3(sin(m)*2.,0,4),f=e(r)*normalize(vec3(v,1.2)),i=vec3(0),d=n(r,f,i),z=f,k=vec3(.051,.0667,.1529);" - "if(d.z>0.)" - "{" - "vec3 y=s(i);" - "k=n(i,y,f,d.y);" - "k=k*exp(-d.x*.04)+mix(k,mix(vec3(.3686,.2431,.4392),vec3(.4,.7294,.9216),pow(max(dot(z,vec3(0,.3,-1)),0.),8.)),1.-exp(-d.x*.04))*(1.-exp(-d.x*.04));" - "f=normalize(reflect(f,y));" - "vec3 c=i+f*.01,p=x(c,f,i);" - "if(p.z>0.)" - "i=c+f*p.x,y=s(i),k+=.1*n(i,y,f,p.y);" - "}" - "return k;" - "}" - "vec2 e()" - "{" - "vec2 r=2.*((gl_FragCoord.xy+vec2(0)*.5)/v.xy-.5);" - "r.x*=v.x/v.y;" - "return r;" - "}" + "#version 330\n" + "uniform float n;" + "uniform vec2 r;" + "layout(location=1) out vec4 v;struct Light{vec3 position;float intensity;vec3 color;};struct Surface{float dist;vec3 position;vec3 baseColor;vec3 normal;vec3 emissiveColor;};struct Hit{Surface surface;Surface near;};" "void main()" "{" - "vec3 r=vec3(0);" - "r+=w(e());" - "r=t(r);" - "gl_FragColor=vec4(r,1);" + "v=vec4(vec3(0,1,0),1);" "}"; #endif // SHADER_MINIFIED_H_ diff --git a/shader_test.glsl b/shader_test.glsl new file mode 100644 index 0000000..2de883d --- /dev/null +++ b/shader_test.glsl @@ -0,0 +1,363 @@ +#version 330 + +precision mediump float; + +float EPS = 0.0001; +float PI = 3.14159265359; +float FLT_MAX = 900.; + +uniform float u_time; +uniform vec2 u_resolution; + +layout(location = 1) out vec4 my_fragColor; + +const int maxIterations = 64; +const float stepScale = 1.; +const float stopThreshold = .005; + +float fov = .6; +float nearClip = 0.; +float farClip = 80.; + +struct Light { + vec3 position; + float intensity; + vec3 color; +}; + +struct Surface { + float dist; + vec3 position; + vec3 baseColor; + vec3 normal; + vec3 emissiveColor; +}; + +struct Hit { + Surface surface; + Surface near; +}; + +float saturate(float s) { + return clamp(s, 0., 1.); +} + +float distanceToLine(vec3 origin, vec3 dir, vec3 point) { + vec3 pointToOrigin = point - origin; + float pointToOriginLength = length(pointToOrigin); + vec3 pointToOriginNorm = normalize(pointToOrigin); + float theta = dot(dir, pointToOriginNorm); + return pointToOriginLength * sqrt(1. - theta * theta); +} + +mat4 scale(vec3 s) { + mat4 m = mat4( + s.x, 0., 0., 0., + 0., s.y, 0., 0., + 0., 0., s.z, 0., + 0., 0., 0., 1. + ); + return inverse(m); +} + +mat4 rotateX(float angle) { + return inverse(mat4( + 1., 0., 0., 0., + 0., cos(angle), -sin(angle), 0., + 0., sin(angle), cos(angle), 0., + 0., 0., 0., 1. + )); +} + +mat4 rotateY(float angle) { + return inverse(mat4( + cos(angle), 0., sin(angle), 0., + 0., 1., 0., 0., + -sin(angle), 0., cos(angle), 0., + 0., 0., 0., 1. + )); +} + +mat4 rotateZ(float angle) { + return inverse(mat4( + cos(angle), -sin(angle), 0., 0., + sin(angle), cos(angle), 0., 0., + 0., 0., 1., 0., + 0., 0., 0., 1. + )); +} + +mat4 translate(vec3 p) { + return inverse(mat4( + 1., 0., 0., p.x, + 0., 1., 0., p.y, + 0., 0., 1., p.z, + 0., 0., 0., 1. + )); +} + +float sphere(vec3 p, float size) { + return length(p) - size; +} + +float box(vec3 p, vec3 size) { + vec3 d = abs(p) - size; + return length(max(d, 0.)) + min(max(d.x, max(d.y, d.z)), 0.); +} + +float tube2(vec2 p, float size) { + return length(p) - size; +} + +float box2(vec2 p, float size) { + return length(max(abs(p) - size, 0.)); +} + +float cylindar(vec3 p, vec3 c) { + return length(p.xz - c.xy) - c.z; +} + +float displacement(vec3 p, vec3 power) { + return sin(power.x * p.x) * sin(power.y * p.y) * sin(power.z * p.z); +} + +vec3 repeat(vec3 p, float c) { + return mod(p, c) - c * .5; +} + +float smin(float a, float b, float k) { + float res = exp(-k * a) + exp(-k * b); + return -log(res) / k; +} + +float scene(vec3 p) { + vec3 _p = p; + + _p = (vec4(_p, 1.)).xyz; + + return min( + sphere((vec4(p, 1.) * translate(vec3(-.7, 0., 0.))).xyz, .5), + box((vec4(p, 1.) * translate(vec3(.7, 0., 0))).xyz, vec3(.45)) + ); +} + +float calcAO(vec3 p, vec3 n) { + float k = 1.; + float occ = 0.; + for(int i = 0; i < 5; i++) { + float len = .15 * (float(i) + 1.); + float distance = scene(n * len + p); + occ += (len - distance) * k; + k *= .5; + } + return clamp(1. - occ, 0., 1.); +} + +vec3 getNormal(vec3 p) { + const float e = EPS; + return normalize(vec3( + scene(p + vec3(e, 0.0, 0.0)) - scene(p + vec3(-e, 0.0, 0.0)), + scene(p + vec3(0.0, e, 0.0)) - scene(p + vec3(0.0, -e, 0.0)), + scene(p + vec3(0.0, 0.0, e)) - scene(p + vec3(0.0, 0.0, -e)) + )); +} + +Surface near(Surface needle, Surface target) { + if(needle.dist < 0. || needle.dist < target.dist) { + return needle; + } + return target; +} + +Hit rayMarching(vec3 origin, vec3 dir, float start, float end) { + Surface cs; // current surface + cs.dist = -1.; + + Surface ns; // near surface + ns.dist = FLT_MAX; + + Hit hit; + + float sceneDist = 0.; + float rayDepth = start; + + for(int i = 0; i < maxIterations; i++) { + sceneDist = scene(origin + dir * rayDepth); + + // cache near distance + if(sceneDist < ns.dist) { + ns.dist = sceneDist; + } + + if((sceneDist < stopThreshold) || (rayDepth >= end)) { + break; + } + rayDepth += sceneDist * stepScale; + cs.dist = rayDepth; + } + + if (sceneDist >= stopThreshold) { + rayDepth = end; + } + + cs.dist = rayDepth; + hit.surface = cs; + hit.near = ns; + + return hit; +} + +float getSpecular(vec3 position, vec3 normal, Light light, float diffuse, vec3 cameraPos) { + vec3 lightDir = light.position - position; + vec3 ref = reflect(-normalize(lightDir), normal); + float specular = 0.; + if(diffuse > 0.) { + specular = max(0., dot(ref, normalize(cameraPos - normal))); + float specularPower = 32.; + specular = pow(specular, specularPower) * light.intensity; + } + return specular; +} + +vec3 lighting(Surface surface, vec3 cameraPos) { + vec3 position = surface.position; + + vec3 color = vec3(0.); + vec3 sceneColor = vec3(0.); + vec3 normal = getNormal(position); + + vec3 objColor = vec3(.4, .4, .4); + vec3 specularColor = vec3(.6, .6, .6); + + Light directionalLight; + directionalLight.position = vec3(5., 5., 5.); + directionalLight.intensity = .8; + directionalLight.color = vec3(.4, .4, .4); + + Light pointLight; + pointLight.position = vec3(5., 5., 5.); + pointLight.intensity = .8; + pointLight.color = vec3(.5, .5, .5); + + Light ambientLight; + ambientLight.color = vec3(.1, .1, .1); + ambientLight.intensity = .3; + + // directional light + float dDiffuse = max(0., dot(normal, normalize(directionalLight.position))); + dDiffuse *= directionalLight.intensity; + vec3 dDiffuseColor = dDiffuse * directionalLight.color * objColor; + float dSpecular = getSpecular(position, normal, directionalLight, dDiffuse, cameraPos); + vec3 dSpecularColor = dSpecular * specularColor; + + // point light + vec3 pLightDir = pointLight.position - position; + float pDiffuse = max(0., dot(normal, normalize(pLightDir))); + vec3 pDiffuseColor = pDiffuse * pointLight.color * objColor; + float d = distance(pointLight.position, position); + vec3 k = vec3(.05, .9, .06); + float attenuation = 1. / (k.x + (k.y * d) + (k.z * d * d)); + pDiffuse *= pointLight.intensity; + pDiffuse *= attenuation; + float pSpecular = getSpecular(position, normal, pointLight, pDiffuse, cameraPos); + pSpecular *= attenuation; + vec3 pSpecularColor = pSpecular * specularColor; + + // ambient + vec3 ambientColor = ambientLight.color * ambientLight.intensity * objColor; + float ao = calcAO(position, normal); + + vec3 diffuse = dDiffuseColor + pDiffuseColor; + vec3 specular = dSpecularColor + pSpecularColor; + vec3 ambient = ambientColor * ao; + + // color += objColor * diffuse + specular + ambient * ao; + color += objColor * diffuse + ambient * ao; + + return color; +} + +vec3 emissiveLight(Light light, Surface surface, vec3 rayOrigin, vec3 rayDirection) { + vec3 eyeDirection = rayOrigin + rayDirection; + + float lightEmissive = pow(distanceToLine(eyeDirection, rayDirection, light.position) + .95, -2.); + + float c = dot(surface.normal, normalize(light.position - surface.position)); + c = clamp(c, 0., 1.); + float em = 0.; + + em = c + (1. - c) * step(farClip, surface.dist); + + return lightEmissive * light.color * light.intensity * em; +} + +vec3 emissiveLighting(Surface surface, vec3 rayOrigin, vec3 rayDirection) { + vec3 eyeDirection = rayOrigin + rayDirection; + vec3 normal = surface.normal; + + Light pointLightRed; + pointLightRed.color = vec3(1., .1, .1); + pointLightRed.intensity = 1.; + pointLightRed.position = vec3(cos(u_time * 1.4) * 2., sin(u_time * 1.4) * 2., 0.); + + Light pointLightGreen; + pointLightGreen.color = vec3(.1, 1., .1); + pointLightGreen.intensity = 1.; + pointLightGreen.position = vec3(cos(u_time * 1.6) * 2., 0., sin(u_time * 1.6) * 2.); + + Light pointLightBlue; + pointLightBlue.color = vec3(.1, .1, 1.); + pointLightBlue.intensity = 1.; + pointLightBlue.position = vec3(0., sin(u_time * 1.8) * 2., cos(u_time * 1.8) * 2.); + + + vec3 color = vec3(0.); + color += emissiveLight(pointLightRed, surface, rayOrigin, rayDirection); + color += emissiveLight(pointLightGreen, surface, rayOrigin, rayDirection); + color += emissiveLight(pointLightBlue, surface, rayOrigin, rayDirection); + + return color; +} + + +void main() { + /*vec2 aspect = vec2(u_resolution.x / u_resolution.y, 1.); + vec2 screenCoord = (2. * gl_FragCoord.xy / u_resolution.xy - 1.) * aspect; + // vec2 mouse = u_mouse.xy / u_resolution.xy - .5; + + // camera settings + vec3 lookAt = vec3(0., 0., 0.); + vec3 cameraPos = vec3(0.,0., 5.); + + // camera vectors + vec3 forward = normalize(lookAt - cameraPos); + vec3 right = normalize(cross(forward, vec3(0., 1., 0.))); + vec3 up = normalize(cross(right, forward)); + + // raymarch + vec3 rayOrigin = cameraPos; + vec3 rayDirection = normalize(forward + fov * screenCoord.x * right + fov * screenCoord.y * up); + Hit hit = rayMarching(rayOrigin, rayDirection, nearClip, farClip); + Surface surface = hit.surface; + Surface near = hit.near; + + surface.position = rayOrigin + rayDirection * surface.dist; + + // color + vec3 sceneColor = vec3(0.); + + // no hit or too far + if(surface.dist >= farClip) { + vec3 bgColor = vec3(0.); + sceneColor = bgColor; + } else { + sceneColor += lighting(surface, cameraPos); + } + + surface.normal = getNormal(surface.position); + sceneColor += emissiveLighting(surface, rayOrigin, rayDirection); +*/ + + // my_fragColor = vec4(sceneColor, 1.); + my_fragColor = vec4(vec3(0., 1., 0.), 1.); +} diff --git a/sointu.bat b/sointu.bat new file mode 100644 index 0000000..d36f1f2 --- /dev/null +++ b/sointu.bat @@ -0,0 +1 @@ +sointu-compile -i -o . -arch=386 -t d:\dev\4kdemo\sointu_templates 4klang.yml \ No newline at end of file diff --git a/sointu_templates/arithmetic.asm b/sointu_templates/arithmetic.asm new file mode 100644 index 0000000..da7e57c --- /dev/null +++ b/sointu_templates/arithmetic.asm @@ -0,0 +1,205 @@ +{{- if .HasOp "pop"}} +;------------------------------------------------------------------------------- +; POP opcode: remove (discard) the topmost signal from the stack +;------------------------------------------------------------------------------- +{{- if .Mono "pop" -}} +; Mono: a -> (empty) +{{- end}} +{{- if .Stereo "pop" -}} +; Stereo: a b -> (empty) +{{- end}} +;------------------------------------------------------------------------------- +{{.Func "su_op_pop" "Opcode"}} +{{- if .StereoAndMono "pop"}} + jnc su_op_pop_mono +{{- end}} +{{- if .Stereo "pop"}} + fstp st0 +{{- end}} +{{- if .StereoAndMono "pop"}} +su_op_pop_mono: +{{- end}} + fstp st0 + ret +{{end}} + + +{{- if .HasOp "add"}} +;------------------------------------------------------------------------------- +; ADD opcode: add the two top most signals on the stack +;------------------------------------------------------------------------------- +{{- if .Mono "add"}} +; Mono: a b -> a+b b +{{- end}} +{{- if .Stereo "add" -}} +; Stereo: a b c d -> a+c b+d c d +{{- end}} +;------------------------------------------------------------------------------- +{{.Func "su_op_add" "Opcode"}} +{{- if .StereoAndMono "add"}} + jnc su_op_add_mono +{{- end}} +{{- if .Stereo "add"}} + fadd st0, st2 + fxch + fadd st0, st3 + fxch + ret +{{- end}} +{{- if .StereoAndMono "add"}} +su_op_add_mono: +{{- end}} +{{- if .Mono "add"}} + fadd st1 +{{- end}} +{{- if .Mono "add"}} + ret + {{- end}} +{{end}} + + +{{- if .HasOp "addp"}} +;------------------------------------------------------------------------------- +; ADDP opcode: add the two top most signals on the stack and pop +;------------------------------------------------------------------------------- +; Mono: a b -> a+b +; Stereo: a b c d -> a+c b+d +;------------------------------------------------------------------------------- +{{.Func "su_op_addp" "Opcode"}} +{{- if .StereoAndMono "addp"}} + jnc su_op_addp_mono +{{- end}} +{{- if .Stereo "addp"}} + faddp st2, st0 + faddp st2, st0 + ret +{{- end}} +{{- if .StereoAndMono "addp"}} +su_op_addp_mono: +{{- end}} +{{- if (.Mono "addp")}} + faddp st1, st0 + ret +{{- end}} +{{end}} + + +{{- if .HasOp "loadnote"}} +;------------------------------------------------------------------------------- +; LOADNOTE opcode: load the current note, scaled to [-1,1] +;------------------------------------------------------------------------------- +{{if (.Mono "loadnote") -}} ; Mono: (empty) -> n, where n is the note{{end}} +{{if (.Stereo "loadnote") -}}; Stereo: (empty) -> n n{{end}} +;------------------------------------------------------------------------------- +{{.Func "su_op_loadnote" "Opcode"}} +{{- if .StereoAndMono "loadnote"}} + jnc su_op_loadnote_mono +{{- end}} +{{- if .Stereo "loadnote"}} + call su_op_loadnote_mono + su_op_loadnote_mono: +{{- end}} + fild dword [{{.INP}}-su_voice.inputs+su_voice.note] + {{.Prepare (.Float 0.0078125)}} + fmul dword [{{.Use (.Float 0.0078125)}}] ; s=n/128.0 + {{.Prepare (.Float 0.5)}} + fsub dword [{{.Use (.Float 0.5)}}] ; s-.5 + fadd st0, st0 ; 2*s-1 + ret +{{end}} + + +{{- if .HasOp "mul"}} +;------------------------------------------------------------------------------- +; MUL opcode: multiply the two top most signals on the stack +;------------------------------------------------------------------------------- +; Mono: a b -> a*b a +; Stereo: a b c d -> a*c b*d c d +;------------------------------------------------------------------------------- +{{.Func "su_op_mul" "Opcode"}} + jnc su_op_mul_mono + fmul st0, st2 + fxch + fadd st0, st3 + fxch + ret +su_op_mul_mono: + fmul st1 + ret +{{end}} + + +{{- if .HasOp "mulp"}} +;------------------------------------------------------------------------------- +; MULP opcode: multiply the two top most signals on the stack and pop +;------------------------------------------------------------------------------- +; Mono: a b -> a*b +; Stereo: a b c d -> a*c b*d +;------------------------------------------------------------------------------- +{{.Func "su_op_mulp" "Opcode"}} +{{- if .StereoAndMono "mulp"}} + jnc su_op_mulp_mono +{{- end}} +{{- if .Stereo "mulp"}} + fmulp st2, st0 + fmulp st2, st0 + ret +{{- end}} +{{- if .StereoAndMono "mulp"}} +su_op_mulp_mono: +{{- end}} +{{- if .Mono "mulp"}} + fmulp st1 + ret +{{- end}} +{{end}} + + +{{- if .HasOp "push"}} +;------------------------------------------------------------------------------- +; PUSH opcode: push the topmost signal on the stack +;------------------------------------------------------------------------------- +; Mono: a -> a a +; Stereo: a b -> a b a b +;------------------------------------------------------------------------------- +{{.Func "su_op_push" "Opcode"}} +{{- if .StereoAndMono "push"}} + jnc su_op_push_mono +{{- end}} +{{- if .Stereo "push"}} + fld st1 + fld st1 + ret +{{- end}} +{{- if .StereoAndMono "push"}} +su_op_push_mono: +{{- end}} +{{- if .Mono "push"}} + fld st0 + ret + {{- end}} +{{end}} + + +{{- if .HasOp "xch"}} +;------------------------------------------------------------------------------- +; XCH opcode: exchange the signals on the stack +;------------------------------------------------------------------------------- +; Mono: a b -> b a +; stereo: a b c d -> c d a b +;------------------------------------------------------------------------------- +{{.Func "su_op_xch" "Opcode"}} +{{- if .StereoAndMono "xch"}} + jnc su_op_xch_mono +{{- end}} +{{- if .Stereo "xch"}} + fxch st0, st2 ; c b a d + fxch st0, st1 ; b c a d + fxch st0, st3 ; d c a b +{{- end}} +{{- if .StereoAndMono "xch"}} +su_op_xch_mono: +{{- end}} + fxch st0, st1 + ret +{{end}} diff --git a/sointu_templates/effects.asm b/sointu_templates/effects.asm new file mode 100644 index 0000000..6d7d371 --- /dev/null +++ b/sointu_templates/effects.asm @@ -0,0 +1,428 @@ +{{- if .HasOp "hold"}} +;------------------------------------------------------------------------------- +; HOLD opcode: sample and hold the signal, reducing sample rate +;------------------------------------------------------------------------------- +; Mono version: holds the signal at a rate defined by the freq parameter +; Stereo version: holds both channels +;------------------------------------------------------------------------------- +{{.Func "su_op_hold" "Opcode"}} +{{- if .Stereo "hold"}} + {{.Call "su_effects_stereohelper"}} +{{- end}} + fld dword [{{.Input "hold" "holdfreq"}}] ; f x + fmul st0, st0 ; f^2 x + fchs ; -f^2 x + fadd dword [{{.WRK}}] ; p-f^2 x + fst dword [{{.WRK}}] ; p <- p-f^2 + fldz ; 0 p x + fucomip st1 ; p x + fstp dword [{{.SP}}-4] ; t=p, x + jc short su_op_hold_holding ; if (0 < p) goto holding + fld1 ; 1 x + fadd dword [{{.SP}}-4] ; 1+t x + fstp dword [{{.WRK}}] ; x + fst dword [{{.WRK}}+4] ; save holded value + ret ; x +su_op_hold_holding: + fstp st0 ; + fld dword [{{.WRK}}+4] ; x + ret +{{end}} + + +{{- if .HasOp "crush"}} +;------------------------------------------------------------------------------- +; CRUSH opcode: quantize the signal to finite number of levels +;------------------------------------------------------------------------------- +; Mono: x -> e*int(x/e) where e=2**(-24*resolution) +; Stereo: l r -> e*int(l/e) e*int(r/e) +;------------------------------------------------------------------------------- +{{.Func "su_op_crush" "Opcode"}} +{{- if .Stereo "crush"}} + {{.Call "su_effects_stereohelper"}} +{{- end}} + xor eax, eax + {{.Call "su_nonlinear_map"}} + fxch st0, st1 + fdiv st0, st1 + frndint + fmulp st1, st0 + ret +{{end}} + + +{{- if .HasOp "gain"}} +;------------------------------------------------------------------------------- +; GAIN opcode: apply gain on the signal +;------------------------------------------------------------------------------- +; Mono: x -> x*g +; Stereo: l r -> l*g r*g +;------------------------------------------------------------------------------- +{{.Func "su_op_gain" "Opcode"}} +{{- if .Stereo "gain"}} + fld dword [{{.Input "gain" "gain"}}] ; g l (r) +{{- if .Mono "invgain"}} + jnc su_op_gain_mono +{{- end}} + fmul st2, st0 ; g l r/g +su_op_gain_mono: + fmulp st1, st0 ; l/g (r/) + ret +{{- else}} + fmul dword [{{.Input "gain" "gain"}}] + ret +{{- end}} +{{end}} + + +{{- if .HasOp "invgain"}} +;------------------------------------------------------------------------------- +; INVGAIN opcode: apply inverse gain on the signal +;------------------------------------------------------------------------------- +; Mono: x -> x/g +; Stereo: l r -> l/g r/g +;------------------------------------------------------------------------------- +{{.Func "su_op_invgain" "Opcode"}} +{{- if .Stereo "invgain"}} + fld dword [{{.Input "invgain" "invgain"}}] ; g l (r) +{{- if .Mono "invgain"}} + jnc su_op_invgain_mono +{{- end}} + fdiv st2, st0 ; g l r/g +su_op_invgain_mono: + fdivp st1, st0 ; l/g (r/) + ret +{{- else}} + fdiv dword [{{.Input "invgain" "invgain"}}] + ret +{{- end}} +{{end}} + +{{- if .HasOp "dbgain"}} +;------------------------------------------------------------------------------- +; DBGAIN opcode: apply gain on the signal, with gain given in decibels +;------------------------------------------------------------------------------- +; Mono: x -> x*g, where g = 2**((2*d-1)*6.643856189774724) i.e. -40dB to 40dB, d=[0..1] +; Stereo: l r -> l*g r*g +;------------------------------------------------------------------------------- +{{.Func "su_op_dbgain" "Opcode"}} +{{- if .Stereo "dbgain"}} + fld dword [{{.Input "dbgain" "decibels"}}] ; d l r +{{- .Prepare (.Float 0.5)}} + fsub dword [{{.Use (.Float 0.5)}}] ; d-.5 + fadd st0, st0 ; 2*d-1 +{{- .Prepare (.Float 6.643856189774724)}} + fmul dword [{{.Use (.Float 6.643856189774724)}}] ; (2*d-1)*6.643856189774724 + {{.Call "su_power"}} +{{- if .Mono "dbgain"}} + jnc su_op_dbgain_mono +{{- end}} + fmul st2, st0 ; g l r/g +su_op_dbgain_mono: + fmulp st1, st0 ; l/g (r/) + ret +{{- else}} + fld dword [{{.Input "dbgain" "decibels"}}] ; d l +{{- .Prepare (.Float 0.5)}} + fsub dword [{{.Use (.Float 0.5)}}] ; d-.5 + fadd st0, st0 ; 2*d-1 +{{- .Prepare (.Float 6.643856189774724)}} + fmul dword [{{.Use (.Float 6.643856189774724)}}] ; (2*d-1)*6.643856189774724 + {{.Call "su_power"}} + fmulp st1, st0 + ret +{{- end}} +{{end}} + +{{- if .HasOp "filter"}} +;------------------------------------------------------------------------------- +; FILTER opcode: perform low/high/band-pass/notch etc. filtering on the signal +;------------------------------------------------------------------------------- +; Mono: x -> filtered(x) +; Stereo: l r -> filtered(l) filtered(r) +;------------------------------------------------------------------------------- +{{.Func "su_op_filter" "Opcode"}} + lodsb ; load the flags to al +{{- if .Stereo "filter"}} + {{.Call "su_effects_stereohelper"}} +{{- end}} + fld dword [{{.Input "filter" "resonance"}}] ; r x + fld dword [{{.Input "filter" "frequency"}}]; f r x + fmul st0, st0 ; f2 x (square the input so we never get negative and also have a smoother behaviour in the lower frequencies) + fst dword [{{.WRK}}+12] ; f2 r x + fmul dword [{{.WRK}}+8] ; f2*b r x + fadd dword [{{.WRK}}] ; f2*b+l r x + fst dword [{{.WRK}}] ; l'=f2*b+l r x + fsubp st2, st0 ; r x-l' + fmul dword [{{.WRK}}+8] ; r*b x-l' + fsubp st1, st0 ; x-l'-r*b + fst dword [{{.WRK}}+4] ; h'=x-l'-r*b + fmul dword [{{.WRK}}+12] ; f2*h' + fadd dword [{{.WRK}}+8] ; f2*h'+b + fstp dword [{{.WRK}}+8] ; b'=f2*h'+b + fldz ; 0 +{{- if .SupportsParamValue "filter" "lowpass" 1}} + test al, byte 0x40 + jz short su_op_filter_skiplowpass + fadd dword [{{.WRK}}] +su_op_filter_skiplowpass: +{{- end}} +{{- if .SupportsParamValue "filter" "bandpass" 1}} + test al, byte 0x20 + jz short su_op_filter_skipbandpass + fadd dword [{{.WRK}}+8] +su_op_filter_skipbandpass: +{{- end}} +{{- if .SupportsParamValue "filter" "highpass" 1}} + test al, byte 0x10 + jz short su_op_filter_skiphighpass + fadd dword [{{.WRK}}+4] +su_op_filter_skiphighpass: +{{- end}} +{{- if .SupportsParamValue "filter" "negbandpass" 1}} + test al, byte 0x08 + jz short su_op_filter_skipnegbandpass + fsub dword [{{.WRK}}+8] +su_op_filter_skipnegbandpass: +{{- end}} +{{- if .SupportsParamValue "filter" "neghighpass" 1}} + test al, byte 0x04 + jz short su_op_filter_skipneghighpass + fsub dword [{{.WRK}}+4] +su_op_filter_skipneghighpass: +{{- end}} + ret +{{end}} + + +{{- if .HasOp "clip"}} +;------------------------------------------------------------------------------- +; CLIP opcode: clips the signal into [-1,1] range +;------------------------------------------------------------------------------- +; Mono: x -> min(max(x,-1),1) +; Stereo: l r -> min(max(l,-1),1) min(max(r,-1),1) +;------------------------------------------------------------------------------- +{{.Func "su_op_clip" "Opcode"}} +{{- if .Stereo "clip"}} + {{.Call "su_effects_stereohelper"}} +{{- end}} + {{.TailCall "su_clip"}} +{{end}} + + +{{- if .HasOp "pan" -}} +;------------------------------------------------------------------------------- +; PAN opcode: pan the signal +;------------------------------------------------------------------------------- +; Mono: s -> s*(1-p) s*p +; Stereo: l r -> l*(1-p) r*p +; +; where p is the panning in [0,1] range +;------------------------------------------------------------------------------- +{{.Func "su_op_pan" "Opcode"}} +{{- if .Stereo "pan"}} + jc su_op_pan_do ; this time, if this is mono op... + fld st0 ; ...we duplicate the mono into stereo first +su_op_pan_do: + fld dword [{{.Input "pan" "panning"}}] ; p l r + fld1 ; 1 p l r + fsub st1 ; 1-p p l r + fmulp st2 ; p (1-p)*l r + fmulp st2 ; (1-p)*l p*r + ret +{{- else}} + fld dword [{{.Input "pan" "panning"}}] ; p s + fmul st1 ; p*s s + fsub st1, st0 ; p*s s-p*s + ; Equal to + ; s*p s*(1-p) + fxch ; s*(1-p) s*p SHOULD PROBABLY DELETE, WHY BOTHER + ret +{{- end}} +{{end}} + + +{{- if .HasOp "delay"}} +;------------------------------------------------------------------------------- +; DELAY opcode: adds delay effect to the signal +;------------------------------------------------------------------------------- +; Mono: perform delay on ST0, using delaycount delaylines starting +; at delayindex from the delaytable +; Stereo: perform delay on ST1, using delaycount delaylines starting +; at delayindex + delaycount from the delaytable (so the right delays +; can be different) +;------------------------------------------------------------------------------- +{{.Func "su_op_delay" "Opcode"}} + lodsw ; al = delay index, ah = delay count + {{- .PushRegs .VAL "DelayVal" .COM "DelayCom" | indent 4}} + movzx ebx, al +{{- if .Library}} + mov {{.SI}}, [{{.Stack "DelayTable"}}] ; when using runtime tables, delaytimes is pulled from the stack so can be a pointer to heap + lea {{.BX}}, [{{.SI}} + {{.BX}}*2] +{{- else}} +{{- .Prepare "su_delay_times" | indent 4}} + lea {{.BX}},[{{.Use "su_delay_times"}} + {{.BX}}*2] ; BX now points to the right position within delay time table +{{- end}} + movzx esi, word [{{.Stack "GlobalTick"}}] ; notice that we load word, so we wrap at 65536 + mov {{.CX}}, {{.PTRWORD}} [{{.Stack "DelayWorkSpace"}}] ; {{.WRK}} is now the separate delay workspace, as they require a lot more space +{{- if .StereoAndMono "delay"}} + jnc su_op_delay_mono +{{- end}} +{{- if .Stereo "delay"}} + push {{.AX}} ; save _ah (delay count) + fxch ; r l + call su_op_delay_do ; D(r) l process delay for the right channel + pop {{.AX}} ; restore the count for second run + fxch ; l D(r) +su_op_delay_mono: ; flow into mono delay +{{- end}} + call su_op_delay_do ; when stereo delay is not enabled, we could inline this to save 5 bytes, but I expect stereo delay to be farely popular so maybe not worth the hassle + mov {{.PTRWORD}} [{{.Stack "DelayWorkSpace"}}],{{.CX}} ; move delay workspace pointer back to stack. + {{- .PopRegs .VAL .COM | indent 4}} +{{- if .SupportsModulation "delay" "delaytime"}} + xor eax, eax + mov dword [{{.Modulation "delay" "delaytime"}}], eax +{{- end}} + ret + +;------------------------------------------------------------------------------- +; su_op_delay_do: executes the actual delay +;------------------------------------------------------------------------------- +; Pseudocode: +; q = dr*x +; for (i = 0;i < count;i++) +; s = b[(t-delaytime[i+offset])&65535] +; q += s +; o[i] = o[i]*da+s*(1-da) +; b[t] = f*o[i] +p^2*x +; Perform dc-filtering q and output q +;------------------------------------------------------------------------------- +{{.Func "su_op_delay_do"}} ; x y + fld st0 + fmul dword [{{.Input "delay" "pregain"}}] ; p*x y + fmul dword [{{.Input "delay" "pregain"}}] ; p*p*x y + fxch ; y p*p*x + fmul dword [{{.Input "delay" "dry"}}] ; dr*y p*p*x +su_op_delay_loop: + {{- if or (.SupportsModulation "delay" "delaytime") (.SupportsParamValue "delay" "notetracking" 1)}} ; delaytime modulation or note syncing require computing the delay time in floats + fild word [{{.BX}}] ; k dr*y p*p*x, where k = delay time + {{- if .SupportsParamValue "delay" "notetracking" 1}} + test ah, 1 ; note syncing is the least significant bit of ah, 0 = ON, 1 = OFF + jne su_op_delay_skipnotesync + fild dword [{{.INP}}-su_voice.inputs+su_voice.note] + {{.Int 0x3DAAAAAA | .Prepare | indent 8}} + fmul dword [{{.Int 0x3DAAAAAA | .Use}}] + {{.Call "su_power"}} + fdivp st1, st0 ; use 10787 for delaytime to have neutral transpose + su_op_delay_skipnotesync: + {{- end}} + {{- if .SupportsModulation "delay" "delaytime"}} + fld dword [{{.Modulation "delay" "delaytime"}}] + {{- .Float 32767.0 | .Prepare | indent 8}} + fmul dword [{{.Float 32767.0 | .Use}}] ; scale it up, as the modulations would be too small otherwise + faddp st1, st0 + {{- end}} + fistp dword [{{.SP}}-4] ; dr*y p*p*x, dword [{{.SP}}-4] = integer amount of delay (samples) + mov edi, esi ; edi = esi = current time + sub di, word [{{.SP}}-4] ; we perform the math in 16-bit to wrap around + {{- else}} + mov edi, esi + sub di, word [{{.BX}}] ; we perform the math in 16-bit to wrap around + {{- end}} + fld dword [{{.CX}}+su_delayline_wrk.buffer+{{.DI}}*4]; s dr*y p*p*x, where s is the sample from delay buffer + fadd st1, st0 ; s dr*y+s p*p*x (add comb output to current output) + fld1 ; 1 s dr*y+s p*p*x + fsub dword [{{.Input "delay" "damp"}}] ; 1-da s dr*y+s p*p*x + fmulp st1, st0 ; s*(1-da) dr*y+s p*p*x + fld dword [{{.Input "delay" "damp"}}] ; da s*(1-da) dr*y+s p*p*x + fmul dword [{{.CX}}+su_delayline_wrk.filtstate] ; o*da s*(1-da) dr*y+s p*p*x, where o is stored + faddp st1, st0 ; o*da+s*(1-da) dr*y+s p*p*x + {{- .Float 0.5 | .Prepare | indent 4}} + fadd dword [{{.Float 0.5 | .Use}}] ; add and sub small offset to prevent denormalization. WARNING: this is highly important, as the damp filters might denormalize and give 100x CPU penalty + fsub dword [{{.Float 0.5 | .Use}}] ; See for example: https://stackoverflow.com/questions/36781881/why-denormalized-floats-are-so-much-slower-than-other-floats-from-hardware-arch + fst dword [{{.CX}}+su_delayline_wrk.filtstate] ; o'=o*da+s*(1-da), o' dr*y+s p*p*x + fmul dword [{{.Input "delay" "feedback"}}] ; f*o' dr*y+s p*p*x + fadd st0, st2 ; f*o'+p*p*x dr*y+s p*p*x + fstp dword [{{.CX}}+su_delayline_wrk.buffer+{{.SI}}*4]; save f*o'+p*p*x to delay buffer + add {{.BX}},2 ; move to next index + add {{.CX}}, su_delayline_wrk.size ; go to next delay delay workspace + sub ah, 2 + jg su_op_delay_loop ; if ah > 0, goto loop + fstp st1 ; dr*y+s1+s2+s3+... + ; DC-filtering + fld dword [{{.CX}}+su_delayline_wrk.dcout] ; o s +{{- .Float 0.99609375 | .Prepare | indent 4}} + fmul dword [{{.Float 0.99609375 | .Use}}] ; c*o s + fsub dword [{{.CX}}+su_delayline_wrk.dcin] ; c*o-i s + fxch ; s c*o-i + fst dword [{{.CX}}+su_delayline_wrk.dcin] ; i'=s, s c*o-i + faddp st1 ; s+c*o-i +{{- .Float 0.5 | .Prepare | indent 4}} + fadd dword [{{.Float 0.5 | .Use}}] ; add and sub small offset to prevent denormalization. WARNING: this is highly important, as low pass filters might denormalize and give 100x CPU penalty + fsub dword [{{.Float 0.5 | .Use}}] ; See for example: https://stackoverflow.com/questions/36781881/why-denormalized-floats-are-so-much-slower-than-other-floats-from-hardware-arch + fst dword [{{.CX}}+su_delayline_wrk.dcout] ; o'=s+c*o-i + ret +{{end}} + + +{{- if .HasOp "compressor"}} +;------------------------------------------------------------------------------- +; COMPRES opcode: push compressor gain to stack +;------------------------------------------------------------------------------- +; Mono: push g on stack, where g is a suitable gain for the signal +; you can either MULP to compress the signal or SEND it to a GAIN +; somewhere else for compressor side-chaining. +; Stereo: push g g on stack, where g is calculated using l^2 + r^2 +;------------------------------------------------------------------------------- +{{.Func "su_op_compressor" "Opcode"}} + fld st0 ; x x + fmul st0, st0 ; x^2 x +{{- if .StereoAndMono "compressor"}} + jnc su_op_compressor_mono +{{- end}} +{{- if .Stereo "compressor"}} + fld st2 ; r x^2 l r + fst st3 ; y x^2 l r + fmul st0, st0 ; y^2 x^2 l r + faddp st1, st0 ; y^2+x^2 l r +{{- if .StereoAndMono "compressor"}} + call su_op_compressor_mono ; So, for stereo, we square both left & right and add them up + fld st0 ; and return the computed gain two times, ready for MULP STEREO + ret +su_op_compressor_mono: +{{- end}} +{{- end}} + fld dword [{{.WRK}}] ; l x^2 x + fucomi st0, st1 + setnb al ; if (st0 >= st1) al = 1; else al = 0; + fsubp st1, st0 ; x^2-l x + {{.Call "su_nonlinear_map"}} ; c x^2-l x, c is either attack or release parameter mapped in a nonlinear way + fmulp st1, st0 ; c*(x^2-l) x + fadd dword [{{.WRK}}] ; l+c*(x^2-l) x // we could've kept level in the stack and save a few bytes, but su_env_map uses 3 stack (c + 2 temp), so the stack was getting quite big. + ; TODO: make this denormalization optional, if the user wants to save some space + {{- .Float 0.5 | .Prepare | indent 4}} + fadd dword [{{.Float 0.5 | .Use}}] ; add and sub small offset to prevent denormalization. WARNING: this is highly important, as the damp filters might denormalize and give 100x CPU penalty + fsub dword [{{.Float 0.5 | .Use}}] ; See for example: https://stackoverflow.com/questions/36781881/why-denormalized-floats-are-so-much-slower-than-other-floats-from-hardware-arch + fst dword [{{.WRK}}] ; l'=l+c*(x^2-l), l' x + fld dword [{{.Input "compressor" "threshold"}}] ; t l' x + fmul st0, st0 ; t*t l' x + fxch ; l' t*t x + fucomi st0, st1 ; if l' < t*t + fcmovb st0, st1 ; l'=t*t + fdivp st1, st0 ; t*t/l' x + fld dword [{{.Input "compressor" "ratio"}}] ; r t*t/l' x +{{.Float 0.5 | .Prepare | indent 4}} + fmul dword [{{.Float 0.5 | .Use}}] ; p=r/2 t*t/l' x + fxch ; t*t/l' p x + fyl2x ; p*log2(t*t/l') x + {{.Call "su_power"}} ; 2^(p*log2(t*t/l')) x + ; Equal to: + ; (t*t/l')^p x + ; if ratio is at minimum => p=0 => 1 x + ; if ratio is at maximum => p=0.5 => t/x => t/x*x=t + fdiv dword [{{.Input "compressor" "invgain"}}]; this used to be pregain but that ran into problems with getting back up to 0 dB so postgain should be better at that +{{- if and (.Stereo "compressor") (not (.Mono "compressor"))}} + fld st0 ; and return the computed gain two times, ready for MULP STEREO +{{- end}} + ret +{{- end}} diff --git a/sointu_templates/flowcontrol.asm b/sointu_templates/flowcontrol.asm new file mode 100644 index 0000000..6e34e66 --- /dev/null +++ b/sointu_templates/flowcontrol.asm @@ -0,0 +1,44 @@ +{{- if .HasOp "speed" -}} +;------------------------------------------------------------------------------- +; SPEED opcode: modulate the speed (bpm) of the song based on ST0 +;------------------------------------------------------------------------------- +; Mono: adds or subtracts the ticks, a value of 0.5 is neutral & will7 +; result in no speed change. +; There is no STEREO version. +;------------------------------------------------------------------------------- +{{.Func "su_op_speed" "Opcode"}} +{{- .Float 2.206896551724138 | .Prepare | indent 4}} + fmul dword [{{.Float 2.206896551724138 | .Use}}] ; (2*s-1)*64/24, let's call this p from now on + {{.Call "su_power"}} + fld1 ; 1 2^p + fsubp st1, st0 ; 2^p-1, the player is advancing 1 tick by its own + fadd dword [{{.WRK}}] ; t+2^p-1, t is the remainder from previous rounds as ticks have to be rounded to 1 + push {{.AX}} + fist dword [{{.SP}}] ; Main stack: k=int(t+2^p-1) + fisub dword [{{.SP}}] ; t+2^p-1-k, the remainder + pop {{.AX}} + add dword [{{.Stack "Sample"}}], eax ; add the whole ticks to row tick count + fstp dword [{{.WRK}}] ; save the remainder for future + ret +{{end}} + + +{{- if or .RowSync (.HasOp "sync")}} +;------------------------------------------------------------------------------- +; SYNC opcode: save the stack top to sync buffer +;------------------------------------------------------------------------------- +{{.Func "su_op_sync" "Opcode"}} +{{- if not .Library}} + ; TODO: syncs are NOPs when compiling as library, should figure out a way to + ; make them work when compiling to use the native track also + mov {{.AX}}, [{{.Stack "GlobalTick"}}] + test al, al + jne su_op_sync_skip + xchg {{.AX}}, [{{.Stack "SyncBufPtr"}}] + fst dword [{{.AX}}] + add {{.AX}}, 4 + xchg {{.AX}}, [{{.Stack "SyncBufPtr"}}] +su_op_sync_skip: +{{- end}} + ret +{{end}} diff --git a/sointu_templates/gmdls.asm b/sointu_templates/gmdls.asm new file mode 100644 index 0000000..c25e35b --- /dev/null +++ b/sointu_templates/gmdls.asm @@ -0,0 +1,52 @@ +{{- if .SupportsParamValue "oscillator" "type" .Sample}} + +{{- if eq .OS "windows"}} +{{.ExportFunc "su_load_gmdls"}} +{{- if .Amd64}} + extern OpenFile ; requires windows + extern ReadFile ; requires windows + ; Win64 ABI: RCX, RDX, R8, and R9 + sub rsp, 40 ; Win64 ABI requires "shadow space" + space for one parameter. + mov rdx, qword su_sample_table + mov rcx, qword su_gmdls_path1 + xor r8,r8 ; OF_READ + push rdx ; &ofstruct, blatantly reuse the sample table + push rcx + call OpenFile ; eax = OpenFile(path,&ofstruct,OF_READ) + pop rcx + pop rdx + movsxd rcx, eax + mov qword [rsp+32], 0 + mov r9, rdx + mov r8d, 3440660 ; number of bytes to read + call ReadFile ; Readfile(handle,&su_sample_table,SAMPLE_TABLE_SIZE,&bytes_read,NULL) + add rsp, 40 ; shadow space, as required by Win64 ABI + ret +{{else}} + mov eax, su_sample_table + ; these are the arguments for ReadFile + push 0 ; NULL + push eax ; &bytes_read, reusing sample table again; it does not matter that the first four bytes are trashed + push 3440660 ; number of bytes to read + push eax ; here we actually pass the sample table to readfile + ; these are for OpenFile + push 0 ; OF_READ + push eax ; &ofstruct, blatantly reuse the sample table + push su_gmdls_path1 ; path + call dword [__imp__OpenFile@12]; eax = OpenFile(path,&ofstruct,OF_READ) + push eax ; handle to file + call dword [__imp__ReadFile@20] ; Readfile(handle,&su_sample_table,SAMPLE_TABLE_SIZE,&bytes_read,NULL) + ret +extern __imp__OpenFile@12 ; requires windows +extern __imp__ReadFile@20 + ; requires windows +{{end}} + +{{.Data "su_gmdls_path1"}} + db 'drivers/gm.dls',0 +{{end}} + +{{.SectBss "susamtable"}} +su_sample_table: + resb 3440660 ; size of gmdls. +{{end}} diff --git a/sointu_templates/library.asm b/sointu_templates/library.asm new file mode 100644 index 0000000..27b0084 --- /dev/null +++ b/sointu_templates/library.asm @@ -0,0 +1,134 @@ +{{template "structs.asm" .}} + +struc su_synth + .synth_wrk resb su_synthworkspace.size + .delay_wrks resb su_delayline_wrk.size * 64 + .delaytimes resw 768 + .sampleoffs resb su_sample_offset.size * 256 + .randseed resd 1 + .globaltime resd 1 + .opcodes resb 32 * 64 + .operands resb 32 * 64 * 8 + .polyphony resd 1 + .numvoices resd 1 +endstruc + +{{.ExportFunc "su_render" "SynthStateParam" "BufferPtrParam" "SamplesParam" "TimeParam"}} + {{- if .Amd64}} + {{- if eq .OS "windows"}} + {{- .PushRegs "rdi" "NonVolatileRDI" "rsi" "NonVolatileRSI" "rbx" "NonVolatileRBX" "rbp" "NonVolatileRBP" | indent 4}} + mov rsi, r8 ; rsi = &samples + mov rbx, r9 ; rbx = &time + {{- else}} ; SystemV amd64 ABI, linux mac or hopefully something similar + {{- .PushRegs "rbx" "NonVolatileRBX" "rbp" "NonVolatileRBP" | indent 4}} + mov rbx, rcx ; rbx points to time + xchg rsi, rdx ; rdx points to buffer, rsi points to samples + mov rcx, rdi ; rcx = &Synthstate + {{- end}} + {{- else}} + {{- .PushRegs | indent 4 }} ; push registers + mov ecx, [{{.Stack "SynthStateParam"}}] ; ecx = &synthState + mov edx, [{{.Stack "BufferPtrParam"}}] ; edx = &buffer + mov esi, [{{.Stack "SamplesParam"}}] ; esi = &samples + mov ebx, [{{.Stack "TimeParam"}}] ; ebx = &time + {{- end}} + {{.SaveFPUState | indent 4}} ; save the FPU state to stack & reset the FPU + {{.Push .SI "Samples"}} + {{.Push .BX "Time"}} + xor eax, eax ; samplenumber starts at 0 + {{.Push .AX "BufSample"}} + mov esi, [{{.SI}}] ; zero extend dereferenced pointer + {{.Push .SI "BufSize"}} + {{.Push .DX "BufPtr"}} + {{.Push .CX "SynthState"}} + lea {{.AX}}, [{{.CX}} + su_synth.sampleoffs] + {{.Push .AX "SampleTable"}} + lea {{.AX}}, [{{.CX}} + su_synth.delaytimes] + {{.Push .AX "DelayTable"}} + mov eax, [{{.CX}} + su_synth.randseed] + {{.Push .AX "RandSeed"}} + mov eax, [{{.CX}} + su_synth.globaltime] + {{.Push .AX "GlobalTick"}} + mov ebx, dword [{{.BX}}] ; zero extend dereferenced pointer + {{.Push .BX "RowLength"}} ; the nominal rowlength should be time_in + xor eax, eax ; rowtick starts at 0 +su_render_samples_loop: + push {{.DI}} + fnstsw [{{.SP}}] ; store the FPU status flag to stack top + pop {{.DI}} ; {{.DI}} = FPU status flag + and {{.DI}}, 0011100001000101b ; mask TOP pointer, stack error, zero divide and in{{.VAL}}id operation + test {{.DI}},{{.DI}} ; all the aforementioned bits should be 0! + jne su_render_samples_time_finish ; otherwise, we exit due to error + cmp eax, [{{.Stack "RowLength"}}] ; if rowtick >= maxtime + jge su_render_samples_time_finish ; goto finish + mov ecx, [{{.Stack "BufSize"}}] ; ecx = buffer length in samples + cmp [{{.Stack "BufSample"}}], ecx ; if samples >= maxsamples + jge su_render_samples_time_finish ; goto finish + inc eax ; time++ + inc dword [{{.Stack "BufSample"}}] ; samples++ + mov {{.CX}}, [{{.Stack "SynthState"}}] + {{.Push .AX "Sample"}} + mov eax, [{{.CX}} + su_synth.polyphony] + {{.Push .AX "PolyphonyBitmask"}} + mov eax, [{{.CX}} + su_synth.numvoices] + {{.Push .AX "VoicesRemain"}} + lea {{.DX}}, [{{.CX}}+ su_synth.synth_wrk] + lea {{.COM}}, [{{.CX}}+ su_synth.opcodes] + lea {{.VAL}}, [{{.CX}}+ su_synth.operands] + lea {{.WRK}}, [{{.DX}} + su_synthworkspace.voices] + lea {{.CX}}, [{{.CX}}+ su_synth.delay_wrks - su_delayline_wrk.filtstate] + {{.Call "su_run_vm"}} + {{.Pop .AX}} + {{.Pop .AX}} + mov {{.DI}}, [{{.Stack "BufPtr"}}] ; edi containts buffer ptr + mov {{.CX}}, [{{.Stack "SynthState"}}] + lea {{.SI}}, [{{.CX}} + su_synth.synth_wrk + su_synthworkspace.left] + movsd ; copy left channel to output buffer + movsd ; copy right channel to output buffer + mov [{{.Stack "BufPtr"}}], {{.DI}} ; save back the updated ptr + lea {{.DI}}, [{{.SI}}-8] + xor eax, eax + stosd ; clear left channel so the VM is ready to write them again + stosd ; clear right channel so the VM is ready to write them again + {{.Pop .AX}} + inc dword [{{.Stack "GlobalTick"}}] ; increment global time, used by delays + jmp su_render_samples_loop +su_render_samples_time_finish: + {{.Pop .CX}} + {{.Pop .BX}} + {{.Pop .DX}} + {{.Pop .CX}} + {{.Pop .CX}} + {{.Pop .CX}} + mov [{{.CX}} + su_synth.randseed], edx + mov [{{.CX}} + su_synth.globaltime], ebx + {{.Pop .BX}} + {{.Pop .BX}} + {{.Pop .DX}} + {{.Pop .BX}} + {{.Pop .SI}} + mov dword [{{.SI}}], edx ; *samples = samples rendered + mov dword [{{.BX}}], eax ; *time = time ticks rendered + mov {{.AX}},{{.DI}} ; {{.DI}} was the masked FPU status flag, {{.AX}} is return {{.VAL}}ue + {{.LoadFPUState | indent 4}} ; load the FPU state from stack + {{- if .Amd64}} + {{- if eq .OS "windows"}} + {{- .PopRegs "rdi" "rsi" "rbx" "rbp" | indent 4}} + {{- else}} ; SystemV amd64 ABI, linux mac or hopefully something similar + {{- .PopRegs "rbx" "rbp" | indent 4}} + {{- end}} + ret + {{- else}} + mov [{{.Stack "eax"}}],eax ; we want to return eax, but popad pops everything, so put eax to stack for popad to pop + {{- .PopRegs | indent 4 }} ; popad + ret 16 + {{- end}} + + +{{template "patch.asm" .}} + +;------------------------------------------------------------------------------- +; Constants +;------------------------------------------------------------------------------- +{{.SectData "constants"}} +{{.Constants}} diff --git a/sointu_templates/library.h b/sointu_templates/library.h new file mode 100644 index 0000000..1776c1c --- /dev/null +++ b/sointu_templates/library.h @@ -0,0 +1,100 @@ +#ifndef _SOINTU_H +#define _SOINTU_H + +#pragma pack(push,1) // this should be fine for both Go and assembly +typedef struct Unit { + float State[8]; + float Ports[8]; +} Unit; + +typedef struct Voice { + int Note; + int Sustain; + float Inputs[8]; + float Reserved[6]; + struct Unit Units[63]; +} Voice; + +typedef struct DelayWorkspace { + float Buffer[65536]; + float Dcin; + float Dcout; + float Filtstate; +} DelayWorkspace; + +typedef struct SynthWorkspace { + unsigned char Curvoices[32]; + float Left; + float Right; + float Aux[6]; + struct Voice Voices[32]; +} SynthWorkspace; + +typedef struct SampleOffset { + unsigned int Start; + unsigned short LoopStart; + unsigned short LoopLength; +} SampleOffset; + +typedef struct Synth { + struct SynthWorkspace SynthWrk; + struct DelayWorkspace DelayWrks[64]; // let's keep this as 64 for now, so the delays take 16 meg. If that's too little or too much, we can change this in future. + unsigned short DelayTimes[768]; + struct SampleOffset SampleOffsets[256]; + unsigned int RandSeed; + unsigned int GlobalTick; + unsigned char Opcodes[32 * 64]; + unsigned char Operands[32 * 64 * 8]; + unsigned int Polyphony; + unsigned int NumVoices; +} Synth; +#pragma pack(pop) + +#if UINTPTR_MAX == 0xffffffff // are we 32-bit? +#if defined(__clang__) || defined(__GNUC__) +#define CALLCONV __attribute__ ((stdcall)) +#elif defined(_WIN32) +#define CALLCONV __stdcall // on 32-bit platforms, we just use stdcall, as all know it +#endif +#else // 64-bit +#define CALLCONV // the asm will use honor honor correct x64 ABI on all 64-bit platforms +#endif + +void CALLCONV su_load_gmdls(void); + +// int su_render(Synth* synth, float* buffer, int* samples, int* time): +// Renders samples until 'samples' number of samples are reached or 'time' number of +// modulated time ticks are reached, whichever happens first. 'samples' and 'time' are +// are passed by reference as the function modifies to tell how many samples were +// actually rendered and how many time ticks were actually advanced. +// +// Parameters: +// synth pointer to the synthesizer used. RandSeed should be > 0 e.g. 1 +// buffer audio sample buffer, L R L R ... +// samples pointer to the maximum number of samples to be rendered. +// buffer should have a length of 2 * maxsamples as the audio +// is stereo. +// time maximum modulated time rendered. +// +// The value referred by samples is changed to contain the actual number of samples rendered +// Similarly, the value referred by time is changed to contain the number of time ticks advanced. +// If samples_out == samples_in, then is must be that time_in <= time_out. +// If samples_out < samples_in, then time_out >= time_in. Note that it could happen that +// time_out > time_in, as it is modulated and the time could advance by 2 or more, so the loop +// exit condition would fire when the current time is already past time_in +// +// Returns an error code, which is actually just masked version of the FPU Status Word +// On a succesful run, the return value should be 0 +// Error code bits: +// bit 0 FPU invalid operation (stack over/underflow OR invalid arithmetic e.g. NaNs) +// bit 2 Divide by zero occurred +// bit 6 Stack overflow or underflow occurred +// bits 11-13 The top pointer of the fpu stack. Any other value than 0 indicates that some values were left on the stack. +int CALLCONV su_render(Synth* synth, float* buffer, int* samples, int* time); + +#define SU_ADVANCE_ID 0 +{{- range $index, $element := .Instructions}} +#define {{printf "su_%v_id" $element | upper | printf "%-20v"}}{{add1 $index | mul 2}} +{{- end}} + +#endif // _SOINTU_H diff --git a/sointu_templates/output_sound.asm b/sointu_templates/output_sound.asm new file mode 100644 index 0000000..4306c0f --- /dev/null +++ b/sointu_templates/output_sound.asm @@ -0,0 +1,44 @@ +{{- if not .Output16Bit }} + {{- if not .Clip }} + mov {{.DI}}, [{{.Stack "OutputBufPtr"}}] ; edi containts ptr + mov {{.SI}}, {{.PTRWORD}} su_synth_obj + su_synthworkspace.left + movsd ; copy left channel to output buffer + movsd ; copy right channel to output buffer + mov [{{.Stack "OutputBufPtr"}}], {{.DI}} ; save back the updated ptr + lea {{.DI}}, [{{.SI}}-8] + xor eax, eax + stosd ; clear left channel so the VM is ready to write them again + stosd ; clear right channel so the VM is ready to write them again + {{ else }} + mov {{.SI}}, qword [{{.Stack "OutputBufPtr"}}] ; esi points to the output buffer + xor ecx,ecx + xor eax,eax + %%loop: ; loop over two channels, left & right + do fld dword [,su_synth_obj+su_synthworkspace.left,_CX*4,] + {{.Call "su_clip"}} + fstp dword [_SI] + do mov dword [,su_synth_obj+su_synthworkspace.left,_CX*4,{],eax} ; clear the sample so the VM is ready to write it + add _SI,4 + cmp ecx,2 + jl %%loop + mov dword [_SP+su_stack.bufferptr - su_stack.output_sound], _SI ; save esi back to stack + {{ end }} +{{- else}} + mov {{.SI}}, [{{.Stack "OutputBufPtr"}}] ; esi points to the output buffer + mov {{.DI}}, {{.PTRWORD}} su_synth_obj+su_synthworkspace.left + mov ecx, 2 + output_sound16bit_loop: ; loop over two channels, left & right + fld dword [{{.DI}}] + {{.Call "su_clip"}} + {{- .Float 32767.0 | .Prepare | indent 16}} + fmul dword [{{.Float 32767.0 | .Use}}] + push {{.AX}} + fistp dword [{{.SP}}] + pop {{.AX}} + mov word [{{.SI}}],ax ; // store integer converted right sample + xor eax,eax + stosd + add {{.SI}},2 + loop output_sound16bit_loop + mov [{{.Stack "OutputBufPtr"}}], {{.SI}} ; save esi back to stack +{{- end }} \ No newline at end of file diff --git a/sointu_templates/patch.asm b/sointu_templates/patch.asm new file mode 100644 index 0000000..eded3d0 --- /dev/null +++ b/sointu_templates/patch.asm @@ -0,0 +1,216 @@ +;------------------------------------------------------------------------------- +; su_run_vm function: runs the entire virtual machine once, creating 1 sample +;------------------------------------------------------------------------------- +; Input: su_synth_obj.left : Set to 0 before calling +; su_synth_obj.right : Set to 0 before calling +; _CX : Pointer to delay workspace (if needed) +; _DX : Pointer to synth object +; COM : Pointer to opcode stream +; VAL : Pointer to operand stream +; WRK : Pointer to the last workspace processed +; Output: su_synth_obj.left : left sample +; su_synth_obj.right : right sample +; Dirty: everything +;------------------------------------------------------------------------------- +{{.Func "su_run_vm"}} + {{- .PushRegs .CX "DelayWorkSpace" .DX "Synth" .COM "OpcodeStream" .WRK "Voice" .VAL "OperandStream" | indent 4}} + {{- if .RowSync}} + fild dword [{{.Stack "Sample"}}] + {{.Int .Song.SamplesPerRow | .Prepare | indent 8}} + fidiv dword [{{.Int .Song.SamplesPerRow | .Use}}] + fiadd dword [{{.Stack "Row"}}] + {{.Call "su_op_sync"}} + fstp st0 + {{- end}} +su_run_vm_loop: ; loop until all voices done + movzx edi, byte [{{.COM}}] ; edi = command byte + inc {{.COM}} ; move to next instruction + add {{.WRK}}, su_unit.size ; move WRK to next unit + shr edi, 1 ; shift out the LSB bit = stereo bit + je su_run_vm_advance ; the opcode is zero, jump to advance + mov {{.INP}}, [{{.Stack "Voice"}}] ; reset INP to point to the inputs part of voice + pushf ; push flags to save carry = stereo bit + add {{.INP}}, su_voice.inputs + xor ecx, ecx ; counter = 0 + xor eax, eax ; clear out high bits of eax, as lodsb only sets al +su_transform_operands_loop: + {{- .Prepare "su_vm_transformcounts-1" | indent 4}} + cmp cl, byte [{{.Use "su_vm_transformcounts-1"}}+{{.DI}}] ; compare the counter to the value in the param count table + je su_transform_operands_out + lodsb ; load the operand from VAL stream + push {{.AX}} ; push it to memory so FPU can read it + fild dword [{{.SP}}] ; load the operand value to FPU stack + {{- .Prepare (.Float 0.0078125) | indent 4}} + fmul dword [{{.Use (.Float 0.0078125)}}] ; divide it by 128 (0 => 0, 128 => 1.0) + fadd dword [{{.WRK}}+su_unit.ports+{{.CX}}*4] ; add the modulations in the current workspace + fstp dword [{{.INP}}+{{.CX}}*4] ; store the modulated value in the inputs section of voice + xor eax, eax + mov dword [{{.WRK}}+su_unit.ports+{{.CX}}*4], eax ; clear out the modulation ports + pop {{.AX}} + inc ecx + jmp su_transform_operands_loop +su_transform_operands_out: + popf ; pop flags for the carry bit = stereo bit + {{- .SaveStack "Opcode"}} + {{- $x := printf "su_vm_jumptable-%v" .PTRSIZE}} + {{- .Prepare $x | indent 4}} + call [{{.Use $x}}+{{.DI}}*{{.PTRSIZE}}] ; call the function corresponding to the instruction + jmp su_run_vm_loop +su_run_vm_advance: + {{- if .SupportsPolyphony}} + mov {{.WRK}}, [{{.Stack "Voice"}}] ; WRK points to start of current voice + add {{.WRK}}, su_voice.size ; move to next voice + mov [{{.Stack "Voice"}}], {{.WRK}} ; update the pointer in the stack to point to the new voice + mov ecx, [{{.Stack "VoicesRemain"}}] ; ecx = how many voices remain to process + dec ecx ; decrement number of voices to process + bt dword [{{.Stack "PolyphonyBitmask"}}], ecx ; if voice bit of su_polyphonism not set + jnc su_op_advance_next_instrument ; goto next_instrument + mov {{.VAL}}, [{{.Stack "OperandStream"}}] ; if it was set, then repeat the opcodes for the current voice + mov {{.COM}}, [{{.Stack "OpcodeStream"}}] +su_op_advance_next_instrument: + mov [{{.Stack "OperandStream"}}], {{.VAL}} ; save current VAL as a checkpoint + mov [{{.Stack "OpcodeStream"}}], {{.COM}} ; save current COM as a checkpoint +su_op_advance_finish: + mov [{{.Stack "VoicesRemain"}}], ecx + jne su_run_vm_loop ; ZF was set by dec ecx + {{- else}} + mov {{.WRK}}, {{.PTRWORD}} [{{.Stack "Voice"}}] ; load pointer to voice to register + add {{.WRK}}, su_voice.size ; shift it to point to following voice + mov {{.PTRWORD}} [{{.Stack "Voice"}}], {{.WRK}} ; save back to stack + dec dword [{{.Stack "VoicesRemain"}}] ; voices-- + jne su_run_vm_loop ; if there's more voices to process, goto vm_loop + {{- end}} + {{- .PopRegs .CX .DX .COM .WRK .VAL | indent 4}} + ret + +{{- template "arithmetic.asm" .}} +{{- template "effects.asm" .}} +{{- template "flowcontrol.asm" .}} +{{- template "sinks.asm" .}} +{{- template "sources.asm" .}} +{{- template "gmdls.asm" .}} + +{{- if .HasCall "su_nonlinear_map"}} +;------------------------------------------------------------------------------- +; su_nonlinear_map function: returns 2^(-24*x) of parameter number _AX +;------------------------------------------------------------------------------- +; Input: _AX : parameter number (e.g. for envelope: 0 = attac, 1 = decay...) +; INP : pointer to transformed operands +; Output: st0 : 2^(-24*x), where x is the parameter in the range 0-1 +;------------------------------------------------------------------------------- +{{.Func "su_nonlinear_map"}} + fld dword [{{.INP}}+{{.AX}}*4] ; x, where x is the parameter in the range 0-1 + {{.Prepare (.Int 24)}} + fimul dword [{{.Use (.Int 24)}}] ; 24*x + fchs ; -24*x + +{{end}} + +{{- if or (.HasCall "su_power") (.HasCall "su_nonlinear_map")}} +;------------------------------------------------------------------------------- +; su_power function: computes 2^x +;------------------------------------------------------------------------------- +; Input: st0 : x +; Output: st0 : 2^x +;------------------------------------------------------------------------------- +{{- if not (.HasCall "su_nonlinear_map")}}{{.SectText "su_power"}}{{end}} +{{.Export "su_pow" 0}} +su_power: + fld1 ; 1 x + fld st1 ; x 1 x + fprem ; mod(x,1) 1 x + f2xm1 ; 2^mod(x,1)-1 1 x + faddp st1,st0 ; 2^mod(x,1) x + fscale ; 2^mod(x,1)*2^trunc(x) x + ; Equal to: + ; 2^x x + fstp st1 ; 2^x + ret + +{{end}} + +{{- if .HasOp "distort"}} +;------------------------------------------------------------------------------- +; DISTORT opcode: apply distortion on the signal +;------------------------------------------------------------------------------- +; Mono: x -> x*a/(1-a+(2*a-1)*abs(x)) where x is clamped first +; Stereo: l r -> l*a/(1-a+(2*a-1)*abs(l)) r*a/(1-a+(2*a-1)*abs(r)) +; This is placed here to be able to flow into waveshaper & also include +; wave shaper if needed by some other function; need to investigate the +; best way to do this +;------------------------------------------------------------------------------- +{{.Func "su_op_distort" "Opcode"}} +{{- if .Stereo "distort" -}} + {{.Call "su_effects_stereohelper"}} +{{- end}} + fld dword [{{.Input "distort" "drive"}}] +{{end}} + +{{- if or (.HasCall "su_waveshaper") (.HasOp "distort")}} +{{- if .HasOp "distort"}} +su_waveshaper: +{{- else}} +{{.Func "su_waveshaper"}} +{{- end}} + fld st0 ; a a x + {{.Prepare (.Float 0.5)}} + fsub dword [{{.Use (.Float 0.5)}}] ; a-.5 a x + fadd st0 ; 2*a-1 a x + fld st2 ; x 2*a-1 a x + fabs ; abs(x) 2*a-1 a x + fmulp st1 ; (2*a-1)*abs(x) a x + fld1 ; 1 (2*a-1)*abs(x) a x + faddp st1 ; 1+(2*a-1)*abs(x) a x + fsub st1 ; 1-a+(2*a-1)*abs(x) a x + fdivp st1, st0 ; a/(1-a+(2*a-1)*abs(x)) x + fmulp st1 ; x*a/(1-a+(2*a-1)*abs(x)) + ret +{{end}} + +{{- if .HasCall "su_effects_stereohelper" }} +;------------------------------------------------------------------------------- +; su_effects_stereohelper: moves the workspace to next, does the filtering for +; right channel (pulling the calling address from stack), rewinds the +; workspace and returns +;------------------------------------------------------------------------------- +{{.Func "su_effects_stereohelper"}} + jnc su_effects_stereohelper_mono ; carry is still the stereo bit + add {{.WRK}}, 16 + fxch ; r l + call [{{.SP}}] ; call whoever called me... + fxch ; l r + sub {{.WRK}}, 16 ; move WRK back to where it was +su_effects_stereohelper_mono: + ret ; return to process l/mono sound + +{{end}} + +{{- if .HasCall "su_clip"}} +{{.Func "su_clip"}} + fld1 ; 1 x a + fucomi st1 ; if (1 <= x) + jbe short su_clip_do ; goto Clip_Do + fchs ; -1 x a + fucomi st1 ; if (-1 < x) + fcmovb st0, st1 ; x x a +su_clip_do: + fstp st1 ; x' a, where x' = clamp(x) + ret +{{end}} + +;------------------------------------------------------------------------------- +; The opcode table jump table. This is constructed to only include the opcodes +; that are used so that the jump table is as small as possible. +;------------------------------------------------------------------------------- +{{.Data "su_vm_jumptable"}} +{{- range .Instructions}} + {{$.DPTR}} su_op_{{.}} +{{- end}} + +;------------------------------------------------------------------------------- +; The number of transformed parameters each opcode takes +;------------------------------------------------------------------------------- +{{.Data "su_vm_transformcounts"}} +{{- range .Instructions}} + db {{$.TransformCount .}} +{{- end}} diff --git a/sointu_templates/player.asm b/sointu_templates/player.asm new file mode 100644 index 0000000..a52efcc --- /dev/null +++ b/sointu_templates/player.asm @@ -0,0 +1,256 @@ +{{template "structs.asm" .}} +;------------------------------------------------------------------------------- +; Uninitialized data: The synth object +;------------------------------------------------------------------------------- +{{.SectBss "synth_object"}} +su_synth_obj: + resb su_synthworkspace.size + resb {{.Song.Patch.NumDelayLines}}*su_delayline_wrk.size + +{{- if or .RowSync (.HasOp "sync")}} +{{- if or (and (eq .OS "windows") (not .Amd64)) (eq .OS "darwin")}} +extern _syncBuf +{{- else}} +extern syncBuf +{{- end}} +{{- end}} + + +;------------------------------------------------------------------------------- +; su_render_song function: the entry point for the synth +;------------------------------------------------------------------------------- +; Has the signature su_render_song(void *ptr), where ptr is a pointer to +; the output buffer. Renders the compile time hard-coded song to the buffer. +; Stack: output_ptr +;------------------------------------------------------------------------------- +{{.ExportFunc "su_render_song" "OutputBufPtr"}} + {{- if .Amd64}} + {{- if eq .OS "windows"}} + {{- .PushRegs "rcx" "OutputBufPtr" "rdi" "NonVolatileRsi" "rsi" "NonVolatile" "rbx" "NonVolatileRbx" "rbp" "NonVolatileRbp" | indent 4}} ; rcx = ptr to buf. rdi,rsi,rbx,rbp nonvolatile + {{- else}} ; SystemV amd64 ABI, linux mac or hopefully something similar + {{- .PushRegs "rdi" "OutputBufPtr" "rbx" "NonVolatileRbx" "rbp" "NonVolatileRbp" | indent 4}} + {{- end}} + {{- else}} + {{- .PushRegs | indent 4}} + {{- end}} + {{- $prologsize := len .Stacklocs}} + {{- if or .RowSync (.HasOp "sync")}} + {{- if or (and (eq .OS "windows") (not .Amd64)) (eq .OS "darwin")}} + {{- .Prepare "_syncBuf"}} + {{.Push (.Use "_syncBuf") "SyncBufPtr"}} + {{- else}} + {{- .Prepare "syncBuf"}} + {{.Push (.Use "syncBuf") "SyncBufPtr"}} + {{- end}} + {{- end}} + xor eax, eax + {{- if ne .VoiceTrackBitmask 0}} + {{.Push (.VoiceTrackBitmask | printf "%v") "VoiceTrackBitmask"}} + {{- end}} + {{.Push "1" "RandSeed"}} + {{.Push .AX "GlobalTick"}} +su_render_rowloop: ; loop through every row in the song + {{.Push .AX "Row"}} + {{.Call "su_update_voices"}} ; update instruments for the new row + xor eax, eax ; ecx is the current sample within row +su_render_sampleloop: ; loop through every sample in the row + {{.Push .AX "Sample"}} + {{- if .SupportsPolyphony}} + {{.Push (.PolyphonyBitmask | printf "%v") "PolyphonyBitmask"}} ; does the next voice reuse the current opcodes? + {{- end}} + {{.Push (.Song.Patch.NumVoices | printf "%v") "VoicesRemain"}} + mov {{.DX}}, {{.PTRWORD}} su_synth_obj ; {{.DX}} points to the synth object + mov {{.COM}}, {{.PTRWORD}} su_patch_opcodes ; COM points to vm code + mov {{.VAL}}, {{.PTRWORD}} su_patch_operands ; VAL points to unit params + {{- if .HasOp "delay"}} + mov {{.CX}}, {{.PTRWORD}} su_synth_obj + su_synthworkspace.size - su_delayline_wrk.filtstate + {{- end}} + lea {{.WRK}}, [{{.DX}} + su_synthworkspace.voices] ; WRK points to the first voice + {{.Call "su_run_vm"}} ; run through the VM code + {{.Pop .AX}} + {{- if .SupportsPolyphony}} + {{.Pop .AX}} + {{- end}} + {{- template "output_sound.asm" .}} ; *ptr++ = left, *ptr++ = right + {{.Pop .AX}} + inc dword [{{.Stack "GlobalTick"}}] ; increment global time, used by delays + inc eax + cmp eax, {{.Song.SamplesPerRow}} + jl su_render_sampleloop + {{.Pop .AX}} ; Stack: pushad ptr + inc eax + cmp eax, {{mul .PatternLength .SequenceLength}} + jl su_render_rowloop + ; rewind the stack the entropy of multiple pop {{.AX}} is probably lower than add + {{- range slice .Stacklocs $prologsize}} + {{$.Pop $.AX}} + {{- end}} + {{- if .Amd64}} + {{- if eq .OS "windows"}} + ; Windows64 ABI, rdi rsi rbx rbp non-volatile + {{- .PopRegs "rcx" "rdi" "rsi" "rbx" "rbp" | indent 4}} + {{- else}} + ; SystemV64 ABI (linux mac or hopefully something similar), rbx rbp non-volatile + {{- .PopRegs "rdi" "rbx" "rbp" | indent 4}} + {{- end}} + ret + {{- else}} + {{- .PopRegs | indent 4}} + ret 4 + {{- end}} + +;------------------------------------------------------------------------------- +; su_update_voices function: polyphonic & chord implementation +;------------------------------------------------------------------------------- +; Input: eax : current row within song +; Dirty: pretty much everything +;------------------------------------------------------------------------------- +{{.Func "su_update_voices"}} +{{- if ne .VoiceTrackBitmask 0}} +; The more complicated implementation: one track can trigger multiple voices + xor edx, edx + mov ebx, {{.PatternLength}} ; we could do xor ebx,ebx; mov bl,PATTERN_SIZE, but that would limit patternsize to 256... + div ebx ; eax = current pattern, edx = current row in pattern + {{.Prepare "su_tracks"}} + lea {{.SI}}, [{{.Use "su_tracks"}}+{{.AX}}] ; esi points to the pattern data for current track + xor eax, eax ; eax is the first voice of next track + xor ebx, ebx ; ebx is the first voice of current track + mov {{.BP}}, {{.PTRWORD}} su_synth_obj ; ebp points to the current_voiceno array +su_update_voices_trackloop: + movzx eax, byte [{{.SI}}] ; eax = current pattern + imul eax, {{.PatternLength}} ; eax = offset to current pattern data +{{- .Prepare "su_patterns" .AX | indent 4}} + movzx eax,byte [{{.Use "su_patterns" .AX}} + {{.DX}}] ; eax = note + push {{.DX}} ; Stack: ptrnrow + xor edx, edx ; edx=0 + mov ecx, ebx ; ecx=first voice of the track to be done +su_calculate_voices_loop: ; do { + bt dword [{{.Stack "VoiceTrackBitmask"}} + {{.PTRSIZE}}],ecx ; test voicetrack_bitmask// notice that the incs don't set carry + inc edx ; edx++ // edx=numvoices + inc ecx ; ecx++ // ecx=the first voice of next track + jc su_calculate_voices_loop ; } while bit ecx-1 of bitmask is on + push {{.CX}} ; Stack: next_instr ptrnrow + cmp al, {{.Hold}} ; anything but hold causes action + je short su_update_voices_nexttrack + mov cl, byte [{{.BP}}] + mov edi, ecx + add edi, ebx + shl edi, 12 ; each unit = 64 bytes and there are 1<= num_voices) + jl su_update_voices_skipreset + xor ecx,ecx ; curvoice = 0 +su_update_voices_skipreset: + mov byte [{{.BP}}],cl + add ecx, ebx + shl ecx, 12 ; each unit = 64 bytes and there are 1<<6 units + small header + lea {{.DI}},[{{.Use "su_synth_obj"}} + su_synthworkspace.voices + {{.CX}}] + stosd ; save note + stosd ; save release + mov ecx, (su_voice.size - su_voice.inputs)/4 + xor eax, eax + rep stosd ; clear the workspace of the new voice, retriggering oscillators +su_update_voices_nexttrack: + pop {{.BX}} ; ebx=first voice of next instrument, Stack: ptrnrow + pop {{.DX}} ; edx=patrnrow + add {{.SI}}, {{.SequenceLength}} + inc {{.BP}} +{{- $addrname := len .Song.Score.Tracks | printf "su_synth_obj + %v"}} +{{- .Prepare $addrname | indent 8}} + cmp {{.BP}},{{.Use $addrname}} + jl su_update_voices_trackloop + ret +{{- else}} +; The simple implementation: each track triggers always the same voice + xor edx, edx + xor ebx, ebx + mov bl, {{.PatternLength}} ; rows per pattern + div ebx ; eax = current pattern, edx = current row in pattern +{{- .Prepare "su_tracks" | indent 4}} + lea {{.SI}}, [{{.Use "su_tracks"}}+{{.AX}}]; esi points to the pattern data for current track + mov {{.DI}}, {{.PTRWORD}} su_synth_obj+su_synthworkspace.voices + mov bl, {{len .Song.Score.Tracks}} ; MAX_TRACKS is always <= 32 so this is ok +su_update_voices_trackloop: + movzx eax, byte [{{.SI}}] ; eax = current pattern + imul eax, {{.PatternLength}} ; multiply by rows per pattern, eax = offset to current pattern data +{{- .Prepare "su_patterns" .AX | indent 8}} + movzx eax, byte [{{.Use "su_patterns" .AX}} + {{.DX}}] ; ecx = note + cmp al, {{.Hold}} ; anything but hold causes action + je short su_update_voices_nexttrack + mov dword [{{.DI}}+su_voice.sustain], eax ; set the voice currently active to release + jb su_update_voices_nexttrack ; if cl < HLD (no new note triggered) goto nexttrack +su_update_voices_retrigger: + stosd ; save note + stosd ; save sustain + mov ecx, (su_voice.size - su_voice.inputs)/4 ; could be xor ecx, ecx; mov ch,...>>8, but will it actually be smaller after compression? + xor eax, eax + rep stosd ; clear the workspace of the new voice, retriggering oscillators + jmp short su_update_voices_skipadd +su_update_voices_nexttrack: + add {{.DI}}, su_voice.size +su_update_voices_skipadd: + add {{.SI}}, {{.SequenceLength}} + dec ebx + jnz short su_update_voices_trackloop + ret +{{- end}} + +{{template "patch.asm" .}} + +;------------------------------------------------------------------------------- +; Patterns +;------------------------------------------------------------------------------- +{{.Data "su_patterns"}} +{{- range .Patterns}} + db {{. | toStrings | join ","}} +{{- end}} + +;------------------------------------------------------------------------------- +; Tracks +;------------------------------------------------------------------------------- +{{.Data "su_tracks"}} +{{- range .Sequences}} + db {{. | toStrings | join ","}} +{{- end}} + +{{- if gt (.SampleOffsets | len) 0}} +;------------------------------------------------------------------------------- +; Sample offsets +;------------------------------------------------------------------------------- +{{.Data "su_sample_offsets"}} +{{- range .SampleOffsets}} + dd {{.Start}} + dw {{.LoopStart}} + dw {{.LoopLength}} +{{- end}} +{{end}} + +{{- if gt (.DelayTimes | len ) 0}} +;------------------------------------------------------------------------------- +; Delay times +;------------------------------------------------------------------------------- +{{.Data "su_delay_times"}} + dw {{.DelayTimes | toStrings | join ","}} +{{end}} + +;------------------------------------------------------------------------------- +; The code for this patch, basically indices to vm jump table +;------------------------------------------------------------------------------- +{{.Data "su_patch_opcodes"}} + db {{.Opcodes | toStrings | join ","}} + +;------------------------------------------------------------------------------- +; The parameters / inputs to each opcode +;------------------------------------------------------------------------------- +{{.Data "su_patch_operands"}} + db {{.Operands | toStrings | join ","}} + +;------------------------------------------------------------------------------- +; Constants +;------------------------------------------------------------------------------- +{{.SectData "constants"}} +{{.Constants}} diff --git a/sointu_templates/player.h b/sointu_templates/player.h new file mode 100644 index 0000000..35de63f --- /dev/null +++ b/sointu_templates/player.h @@ -0,0 +1,69 @@ +// auto-generated by Sointu, editing not recommended +#ifndef SU_RENDER_H +#define SU_RENDER_H + +#define SU_CHANNEL_COUNT 2 +#define SU_LENGTH_IN_SAMPLES {{.MaxSamples}} +#define SU_BUFFER_LENGTH (SU_LENGTH_IN_SAMPLES*SU_CHANNEL_COUNT) + +#define SU_SAMPLE_RATE 44100 +#define SU_BPM {{.Song.BPM}} +#define SU_ROWS_PER_BEAT {{.Song.RowsPerBeat}} +#define SU_ROWS_PER_PATTERN {{.Song.Score.RowsPerPattern}} +#define SU_LENGTH_IN_PATTERNS {{.Song.Score.Length}} +#define SU_LENGTH_IN_ROWS (SU_LENGTH_IN_PATTERNS*SU_PATTERN_SIZE) +#define SU_SAMPLES_PER_ROW (SU_SAMPLE_RATE*60/(SU_BPM*SU_ROWS_PER_BEAT)) + +{{- if or .RowSync (.HasOp "sync")}} +{{- if .RowSync}} +#define SU_NUMSYNCS {{add1 .Song.Patch.NumSyncs}} +{{- else}} +#define SU_NUMSYNCS {{.Song.Patch.NumSyncs}} +{{- end}} +#define SU_SYNCBUFFER_LENGTH ((SU_LENGTH_IN_SAMPLES+255)>>8)*SU_NUMSYNCS +{{- end}} + +#include +#if UINTPTR_MAX == 0xffffffff + #if defined(__clang__) || defined(__GNUC__) + #define SU_CALLCONV __attribute__ ((stdcall)) + #elif defined(_WIN32) + #define SU_CALLCONV __stdcall + #endif +#else + #define SU_CALLCONV +#endif + +{{- if .Output16Bit}} +typedef short SUsample; +#define SU_SAMPLE_RANGE 32767.0 +#define SU_SAMPLE_PCM16 +#define SU_SAMPLE_SIZE 2 +{{- else}} +typedef float SUsample; +#define SU_SAMPLE_RANGE 1.0 +#define SU_SAMPLE_FLOAT +#define SU_SAMPLE_SIZE 4 +{{- end}} + + +#ifdef __cplusplus +extern "C" { +#endif + +{{- if or .RowSync (.HasOp "sync")}} +#define SU_SYNC +{{- end}} +void SU_CALLCONV su_render_song(SUsample *buffer); + +{{- if gt (.SampleOffsets | len) 0}} +void SU_CALLCONV su_load_gmdls(); +#define SU_LOAD_GMDLS +{{- end}} + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/sointu_templates/player.inc b/sointu_templates/player.inc new file mode 100644 index 0000000..d2e3f99 --- /dev/null +++ b/sointu_templates/player.inc @@ -0,0 +1,52 @@ +; auto-generated by Sointu, editing not recommended +%ifndef PLAYER_INC +%define PLAYER_INC + +%define SU_CHANNEL_COUNT 2 +%define SU_LENGTH_IN_SAMPLES {{.MaxSamples}} +%define SU_BUFFER_LENGTH (SU_LENGTH_IN_SAMPLES*SU_CHANNEL_COUNT) + +%define SU_SAMPLE_RATE 44100 +%define SU_BPM {{.Song.BPM}} +%define SU_ROWS_PER_BEAT {{.Song.RowsPerBeat}} +%define SU_ROWS_PER_PATTERN {{.Song.Score.RowsPerPattern}} +%define SU_LENGTH_IN_PATTERNS {{.Song.Score.Length}} +%define SU_LENGTH_IN_ROWS (SU_LENGTH_IN_PATTERNS*SU_PATTERN_SIZE) +%define SU_SAMPLES_PER_ROW (SU_SAMPLE_RATE*60/(SU_BPM*SU_ROWS_PER_BEAT)) + +{{- if or .RowSync (.HasOp "sync")}} +{{- if .RowSync}} +%define SU_NUMSYNCS {{add1 .Song.Patch.NumSyncs}} +{{- else}} +%define SU_NUMSYNCS {{.Song.Patch.NumSyncs}} +{{- end}} +%define SU_SYNCBUFFER_LENGTH ((SU_LENGTH_IN_SAMPLES+255)>>8)*SU_NUMSYNCS +{{- end}} + +{{- if .Output16Bit}} +%define SU_SAMPLE_SIZE 2 +%define SU_SAMPLE_RANGE 32767.0 +%define SU_SAMPLE_PCM16 +{{- else}} +%define SU_SAMPLE_SIZE 4 +%define SU_SAMPLE_RANGE 1.0 +%define SU_SAMPLE_FLOAT +{{- end}} + +{{- if or .RowSync (.HasOp "sync")}} +%define SU_SYNC +{{- end}} + +_su_symbols: +%ifdef MANGLED + extern _su_render_song@4 +%else ; MANGLED + extern su_render_song +%endif ; MANGLED + +{{- if gt (.SampleOffsets | len) 0}} + extern _su_load_gmdls +%define SU_LOAD_GMDLS +{{- end}} + +%endif ; PLAYER_INC diff --git a/sointu_templates/sinks.asm b/sointu_templates/sinks.asm new file mode 100644 index 0000000..dc43c3e --- /dev/null +++ b/sointu_templates/sinks.asm @@ -0,0 +1,128 @@ +{{- if .HasOp "out"}} +;------------------------------------------------------------------------------- +; OUT opcode: outputs and pops the signal +;------------------------------------------------------------------------------- +{{- if .Mono "out"}} +; Mono: add ST0 to main left port, then pop +{{- end}} +{{- if .Stereo "out"}} +; Stereo: add ST0 to left out and ST1 to right out, then pop +{{- end}} +;------------------------------------------------------------------------------- +{{.Func "su_op_out" "Opcode"}} ; l r + mov {{.DI}}, [{{.Stack "Synth"}}] ; DI points to the synth object, use DI consistently in sinks/sources presumably to increase compression rate +{{- if .StereoAndMono "out" }} + jnc su_op_out_mono +{{- end }} +{{- if .Stereo "out" }} + call su_op_out_mono + add {{.DI}}, 4 ; shift from left to right channel +su_op_out_mono: +{{- end}} + fmul dword [{{.Input "out" "gain"}}] ; multiply by gain + fadd dword [{{.DI}} + su_synthworkspace.left] ; add current value of the output + fstp dword [{{.DI}} + su_synthworkspace.left] ; store the new value of the output + ret +{{end}} + + +{{- if .HasOp "outaux"}} +;------------------------------------------------------------------------------- +; OUTAUX opcode: outputs to main and aux1 outputs and pops the signal +;------------------------------------------------------------------------------- +; Mono: add outgain*ST0 to main left port and auxgain*ST0 to aux1 left +; Stereo: also add outgain*ST1 to main right port and auxgain*ST1 to aux1 right +;------------------------------------------------------------------------------- +{{.Func "su_op_outaux" "Opcode"}} ; l r + mov {{.DI}}, [{{.Stack "Synth"}}] +{{- if .StereoAndMono "outaux" }} + jnc su_op_outaux_mono +{{- end}} +{{- if .Stereo "outaux" }} + call su_op_outaux_mono + add {{.DI}}, 4 +su_op_outaux_mono: +{{- end}} + fld st0 ; l l + fmul dword [{{.Input "outaux" "outgain"}}] ; g*l + fadd dword [{{.DI}} + su_synthworkspace.left] ; g*l+o + fstp dword [{{.DI}} + su_synthworkspace.left] ; o'=g*l+o + fmul dword [{{.Input "outaux" "auxgain"}}] ; h*l + fadd dword [{{.DI}} + su_synthworkspace.aux] ; h*l+a + fstp dword [{{.DI}} + su_synthworkspace.aux] ; a'=h*l+a + ret +{{end}} + + +{{- if .HasOp "aux"}} +;------------------------------------------------------------------------------- +; AUX opcode: outputs the signal to aux (or main) port and pops the signal +;------------------------------------------------------------------------------- +; Mono: add gain*ST0 to left port +; Stereo: also add gain*ST1 to right port +;------------------------------------------------------------------------------- +{{.Func "su_op_aux" "Opcode"}} ; l r + lodsb + mov {{.DI}}, [{{.Stack "Synth"}}] +{{- if .StereoAndMono "aux" }} + jnc su_op_aux_mono +{{- end}} +{{- if .Stereo "aux" }} + call su_op_aux_mono + add {{.DI}}, 4 +su_op_aux_mono: +{{- end}} + fmul dword [{{.Input "aux" "gain"}}] ; g*l + fadd dword [{{.DI}} + su_synthworkspace.left + {{.AX}}*4] ; g*l+o + fstp dword [{{.DI}} + su_synthworkspace.left + {{.AX}}*4] ; o'=g*l+o + ret +{{end}} + + +{{- if .HasOp "send"}} +;------------------------------------------------------------------------------- +; SEND opcode: adds the signal to a port +;------------------------------------------------------------------------------- +; Mono: adds signal to a memory address, defined by a word in VAL stream +; Stereo: also add right signal to the following address +;------------------------------------------------------------------------------- +{{.Func "su_op_send" "Opcode"}} + lodsw + mov {{.CX}}, [{{.Stack "Voice"}}] ; load pointer to voice +{{- if .SupportsGlobalSend}} + pushf ; uh ugly: we save the flags just for the stereo carry bit. Doing the .CX loading later crashed the synth for stereo sends as loading the synth address from stack was f'd up by the "call su_op_send_mono" + test ah, 0x80 + jz su_op_send_skipglobal + mov {{.CX}}, [{{.Stack "Synth"}} + {{.PTRSIZE}}] +su_op_send_skipglobal: + popf +{{- end}} +{{- if .StereoAndMono "send"}} + jnc su_op_send_mono +{{- end}} +{{- if .Stereo "send"}} + mov {{.DI}}, {{.AX}} + inc {{.AX}} ; send the right channel first + fxch ; r l + call su_op_send_mono ; (r) l + mov {{.AX}}, {{.DI}} ; move back to original address + test al, 0x8 ; if r was not popped and is still in the stack + jnz su_op_send_mono + fxch ; swap them back: l r +su_op_send_mono: +{{- end}} + test al, 0x8 ; if the SEND_POP bit is not set + jnz su_op_send_skippush + fld st0 ; duplicate the signal on stack: s s +su_op_send_skippush: ; there is signal s, but maybe also another: s (s) + fld dword [{{.Input "send" "amount"}}] ; a l (l) +{{- .Float 0.5 | .Prepare | indent 4}} + fsub dword [{{.Float 0.5 | .Use}}] ; a-.5 l (l) + fadd st0 ; g=2*a-1 l (l) + and ah, 0x7f ; eax = send address, clear the global bit + or al, 0x8 ; set the POP bit always, at the same time shifting to ports instead of wrk + fmulp st1, st0 ; g*l (l) + fadd dword [{{.CX}} + {{.AX}}*4] ; g*l+L (l),where L is the current value + fstp dword [{{.CX}} + {{.AX}}*4] ; (l) + ret +{{end}} diff --git a/sointu_templates/sources.asm b/sointu_templates/sources.asm new file mode 100644 index 0000000..f6bf727 --- /dev/null +++ b/sointu_templates/sources.asm @@ -0,0 +1,441 @@ +{{if .HasOp "envelope" -}} +;------------------------------------------------------------------------------- +; ENVELOPE opcode: pushes an ADSR envelope value on stack [0,1] +;------------------------------------------------------------------------------- +; Mono: push the envelope value on stack +; Stereo: push the envelope valeu on stack twice +;------------------------------------------------------------------------------- +{{.Func "su_op_envelope" "Opcode"}} +{{- if .StereoAndMono "envelope"}} + jnc su_op_envelope_mono +{{- end}} +{{- if .Stereo "envelope"}} + call su_op_envelope_mono + fld st0 + ret +su_op_envelope_mono: +{{- end}} + mov eax, dword [{{.INP}}-su_voice.inputs+su_voice.sustain] ; eax = su_instrument.sustain + test eax, eax ; if (eax != 0) + jne su_op_envelope_process ; goto process + mov al, {{.InputNumber "envelope" "release"}} ; [state]=RELEASE + mov dword [{{.WRK}}], eax ; note that mov al, XXX; mov ..., eax is less bytes than doing it directly +su_op_envelope_process: + mov eax, dword [{{.WRK}}] ; al=[state] + fld dword [{{.WRK}}+4] ; x=[level] + cmp al, {{.InputNumber "envelope" "sustain"}} ; if (al==SUSTAIN) + je short su_op_envelope_leave2 ; goto leave2 +su_op_envelope_attac: + cmp al, {{.InputNumber "envelope" "attack"}} ; if (al!=ATTAC) + jne short su_op_envelope_decay ; goto decay + {{.Call "su_nonlinear_map"}} ; a x, where a=attack + faddp st1, st0 ; a+x + fld1 ; 1 a+x + fucomi st1 ; if (a+x<=1) // is attack complete? + fcmovnb st0, st1 ; a+x a+x + jbe short su_op_envelope_statechange ; else goto statechange +su_op_envelope_decay: + cmp al, {{.InputNumber "envelope" "decay"}} ; if (al!=DECAY) + jne short su_op_envelope_release ; goto release + {{.Call "su_nonlinear_map"}} ; d x, where d=decay + fsubp st1, st0 ; x-d + fld dword [{{.Input "envelope" "sustain"}}] ; s x-d, where s=sustain + fucomi st1 ; if (x-d>s) // is decay complete? + fcmovb st0, st1 ; x-d x-d + jnc short su_op_envelope_statechange ; else goto statechange +su_op_envelope_release: + cmp al, {{.InputNumber "envelope" "release"}} ; if (al!=RELEASE) + jne short su_op_envelope_leave ; goto leave + {{.Call "su_nonlinear_map"}} ; r x, where r=release + fsubp st1, st0 ; x-r + fldz ; 0 x-r + fucomi st1 ; if (x-r>0) // is release complete? + fcmovb st0, st1 ; x-r x-r, then goto leave + jc short su_op_envelope_leave +su_op_envelope_statechange: + inc dword [{{.WRK}}] ; [state]++ +su_op_envelope_leave: + fstp st1 ; x', where x' is the new value + fst dword [{{.WRK}}+4] ; [level]=x' +su_op_envelope_leave2: + fmul dword [{{.Input "envelope" "gain"}}] ; [gain]*x' + ret +{{end}} + + +{{- if .HasOp "noise"}} +;------------------------------------------------------------------------------- +; NOISE opcode: creates noise +;------------------------------------------------------------------------------- +; Mono: push a random value [-1,1] value on stack +; Stereo: push two (differeent) random values on stack +;------------------------------------------------------------------------------- +{{.Func "su_op_noise" "Opcode"}} + lea {{.CX}},[{{.Stack "RandSeed"}}] +{{- if .StereoAndMono "noise"}} + jnc su_op_noise_mono +{{- end}} +{{- if .Stereo "noise"}} + call su_op_noise_mono +su_op_noise_mono: +{{- end}} + imul eax, [{{.CX}}],16007 + mov [{{.CX}}],eax + fild dword [{{.CX}}] +{{- .Prepare (.Int 2147483648)}} + fidiv dword [{{.Use (.Int 2147483648)}}] ; 65536*32768 + fld dword [{{.Input "noise" "shape"}}] + {{.Call "su_waveshaper"}} + fmul dword [{{.Input "noise" "gain"}}] + ret +{{end}} + + +{{- if .HasOp "oscillator"}} +;------------------------------------------------------------------------------- +; OSCILLAT opcode: oscillator, the heart of the synth +;------------------------------------------------------------------------------- +; Mono: push oscillator value on stack +; Stereo: push l r on stack, where l has opposite detune compared to r +;------------------------------------------------------------------------------- +{{.Func "su_op_oscillator" "Opcode"}} +{{- if .SupportsModulation "oscillator" "frequency"}} + push 0 + pop {{.CX}} ; clear cx without affecting flags + xchg ecx, dword [{{.Modulation "oscillator" "frequency"}}] +{{- end}} + lodsb ; load the flags +{{- if .Library}} + mov {{.DI}}, [{{.Stack "SampleTable"}}]; we need to put this in a register, as the stereo & unisons screw the stack positions + ; ain't we lucky that {{.DI}} was unused throughout +{{- end}} + fld dword [{{.Input "oscillator" "detune"}}] ; e, where e is the detune [0,1] +{{- .Prepare (.Float 0.5)}} + fsub dword [{{.Use (.Float 0.5)}}] ; e-.5 + fadd st0, st0 ; d=2*e-.5, where d is the detune [-1,1] +{{- if .StereoAndMono "oscillator"}} + jnc su_op_oscillat_mono +{{- end}} +{{- if .Stereo "oscillator"}} + fld st0 ; d d + add {{.WRK}}, 4 ; move wrk... + call su_op_oscillat_mono ; r d + sub {{.WRK}}, 4 ; ...restore wrk + fxch ; d r + fchs ; -d r, negate the detune for second round +su_op_oscillat_mono: +{{- end}} +{{- if .SupportsParamValueOtherThan "oscillator" "unison" 0}} + {{.PushRegs .AX "" .WRK "OscWRK" .AX "OscFlags"}} + fldz ; 0 d + fxch ; d a=0, "accumulated signal" +su_op_oscillat_unison_loop: + fst dword [{{.SP}}] ; save the current detune, d. We could keep it in fpu stack but it was getting big. + call su_op_oscillat_single ; s a + faddp st1, st0 ; a+=s + test al, 3 + je su_op_oscillat_unison_out + add {{.WRK}}, 8 ; this is ok after all, as there's a pop in the end of unison loop + fld dword [{{.Input "oscillator" "phase"}}] ; p s +{{.Int 0x3DAAAAAA | .Prepare}} + fadd dword [{{.Int 0x3DAAAAAA | .Use}}] ; 1/12 p s, add some little phase offset to unison oscillators so they don't start in sync + fstp dword [{{.Input "oscillator" "phase"}}] ; s note that this changes the phase for second, possible stereo run. That's probably ok + fld dword [{{.SP}}] ; d s +{{.Float 0.5 | .Prepare}} + fmul dword [{{.Float 0.5 | .Use}}] ; .5*d s // negate and halve the detune of each oscillator + fchs ; -.5*d s // negate and halve the detune of each oscillator + dec eax + jmp short su_op_oscillat_unison_loop +su_op_oscillat_unison_out: + {{.PopRegs .AX .WRK .AX}} + ret +su_op_oscillat_single: +{{- end}} + fld dword [{{.Input "oscillator" "transpose"}}] +{{- .Float 0.5 | .Prepare}} + fsub dword [{{.Float 0.5 | .Use}}] +{{- .Float 0.0078125 | .Prepare}} + fdiv dword [{{.Float 0.0078125 | .Use}}] + faddp st1 +{{- if .SupportsParamValue "oscillator" "lfo" 1}} + test al, byte 0x08 + jnz su_op_oscillat_skipnote +{{- end}} + fiadd dword [{{.INP}}-su_voice.inputs+su_voice.note] ; // st0 is note, st1 is t+d offset +{{- if .SupportsParamValue "oscillator" "lfo" 1}} +su_op_oscillat_skipnote: +{{- end}} +{{- .Int 0x3DAAAAAA | .Prepare}} + fmul dword [{{.Int 0x3DAAAAAA | .Use}}] + {{.Call "su_power"}} +{{- if .SupportsParamValue "oscillator" "lfo" 1}} + test al, byte 0x08 + jz short su_op_oscillat_normalize_note +{{- .Float 0.000038 | .Prepare}} + fmul dword [{{.Float 0.000038 | .Use}}] ; // st0 is now frequency for lfo + jmp short su_op_oscillat_normalized +su_op_oscillat_normalize_note: +{{- end}} +{{- .Float 0.000092696138 | .Prepare}} + fmul dword [{{.Float 0.000092696138 | .Use}}] ; // st0 is now frequency +su_op_oscillat_normalized: + fadd dword [{{.WRK}}] +{{- if .SupportsModulation "oscillator" "frequency"}} + push {{.CX}} + fadd dword [{{.SP}}] + pop {{.CX}} +{{- end}} +{{- if .SupportsParamValue "oscillator" "type" .Sample}} + test al, byte 0x80 + jz short su_op_oscillat_not_sample + fst dword [{{.WRK}}] ; for samples, we store the phase without mod(p,1) +{{- if or (.SupportsParamValueOtherThan "oscillator" "phase" 0) (.SupportsModulation "oscillator" "phase")}} + fadd dword [{{.Input "oscillator" "phase"}}] +{{- end}} + {{.Call "su_oscillat_sample"}} + jmp su_op_oscillat_shaping ; skip the rest to avoid color phase normalization and colorloading +su_op_oscillat_not_sample: +{{- end}} + fld1 ; we need to take mod(p,1) so the frequency does not drift as the float + fadd st1, st0 ; make no mistake: without this, there is audible drifts in oscillator pitch + fxch ; as the actual period changes once the phase becomes too big + fprem ; we actually computed mod(p+1,1) instead of mod(p,1) as the fprem takes mod + fstp st1 ; towards zero + fst dword [{{.WRK}}] ; store back the updated phase +{{- if or (.SupportsParamValueOtherThan "oscillator" "phase" 0) (.SupportsModulation "oscillator" "phase")}} + fadd dword [{{.Input "oscillator" "phase"}}] + fld1 ; this is a bit stupid, but we need to take mod(x,1) again after phase modulations + fadd st1, st0 ; as the actual oscillator functions expect x in [0,1] + fxch + fprem + fstp st1 +{{- end}} + fld dword [{{.Input "oscillator" "color"}}] ; // c p + ; every oscillator test included if needed +{{- if .SupportsParamValue "oscillator" "type" .Sine}} + test al, byte 0x40 + jz short su_op_oscillat_notsine + {{.Call "su_oscillat_sine"}} +su_op_oscillat_notsine: +{{- end}} +{{- if .SupportsParamValue "oscillator" "type" .Trisaw}} + test al, byte 0x20 + jz short su_op_oscillat_not_trisaw + {{.Call "su_oscillat_trisaw"}} +su_op_oscillat_not_trisaw: +{{- end}} +{{- if .SupportsParamValue "oscillator" "type" .Pulse}} + test al, byte 0x10 + jz short su_op_oscillat_not_pulse + {{.Call "su_oscillat_pulse"}} +su_op_oscillat_not_pulse: +{{- end}} +{{- if .SupportsParamValue "oscillator" "type" .Gate}} + test al, byte 0x04 + jz short su_op_oscillat_not_gate + {{.Call "su_oscillat_gate"}} + jmp su_op_oscillat_gain ; skip waveshaping as the shape parameter is reused for gateshigh +su_op_oscillat_not_gate: +{{- end}} +su_op_oscillat_shaping: + ; finally, shape the oscillator and apply gain + fld dword [{{.Input "oscillator" "shape"}}] + {{.Call "su_waveshaper"}} +su_op_oscillat_gain: + fmul dword [{{.Input "oscillator" "gain"}}] + ret +{{end}} + + +{{- if .HasCall "su_oscillat_pulse"}} +{{.Func "su_oscillat_pulse"}} + fucomi st1 ; // c p + fld1 + jnc short su_oscillat_pulse_up ; // +1 c p + fchs ; // -1 c p +su_oscillat_pulse_up: + fstp st1 ; // +-1 p + fstp st1 ; // +-1 + ret +{{end}} + + +{{- if .HasCall "su_oscillat_trisaw"}} +{{.Func "su_oscillat_trisaw"}} + fucomi st1 ; // c p + jnc short su_oscillat_trisaw_up + fld1 ; // 1 c p + fsubr st2, st0 ; // 1 c 1-p + fsubrp st1, st0 ; // 1-c 1-p +su_oscillat_trisaw_up: + fdivp st1, st0 ; // tp'/tc + fadd st0 ; // 2*'' + fld1 ; // 1 2*'' + fsubp st1, st0 ; // 2*''-1 + ret +{{end}} + + +{{- if .HasCall "su_oscillat_sine"}} +{{.Func "su_oscillat_sine"}} + fucomi st1 ; // c p + jnc short su_oscillat_sine_do + fstp st1 + fsub st0, st0 ; // 0 + ret +su_oscillat_sine_do: + fdivp st1, st0 ; // p/c + fldpi ; // pi p + fadd st0 ; // 2*pi p + fmulp st1, st0 ; // 2*pi*p + fsin ; // sin(2*pi*p) + ret +{{end}} + + +{{- if .HasCall "su_oscillat_gate"}} +{{.Func "su_oscillat_gate"}} + fxch ; p c + fstp st1 ; p +{{- .Float 16.0 | .Prepare | indent 4}} + fmul dword [{{.Float 16.0 | .Use}}] ; 16*p + push {{.AX}} + push {{.AX}} + fistp dword [{{.SP}}] ; s=int(16*p), stack empty + fld1 ; 1 + pop {{.AX}} + and al, 0xf ; ax=int(16*p) & 15, stack: 1 + bt word [{{.VAL}}-4],ax ; if bit ax of the gate word is set + jc su_oscillat_gate_bit ; goto gate_bit + fsub st0, st0 ; stack: 0 +su_oscillat_gate_bit: ; stack: 0/1, let's call it x + fld dword [{{.WRK}}+16] ; g x, g is gatestate, x is the input to this filter 0/1 + fsub st1 ; g-x x +{{- .Float 0.99609375 | .Prepare | indent 4}} + fmul dword [{{.Float 0.99609375 | .Use}}] ; c(g-x) x + faddp st1, st0 ; x+c(g-x) + fst dword [{{.WRK}}+16]; g'=x+c(g-x) NOTE THAT UNISON 2 & UNISON 3 ALSO USE {{.WRK}}+16, so gate and unison 2 & 3 don't work. Probably should delete that low pass altogether + pop {{.AX}} ; Another way to see this (c~0.996) + ret ; g'=cg+(1-c)x + ; This is a low-pass to smooth the gate transitions +{{end}} + + +{{- if .HasCall "su_oscillat_sample"}} +{{.Func "su_oscillat_sample"}} + {{- .PushRegs .AX "SampleAx" .DX "SampleDx" .CX "SampleCx" .BX "SampleBx" .DI "SampleDi" | indent 4}} ; edx must be saved, eax & ecx if this is stereo osc + push {{.AX}} + mov al, byte [{{.VAL}}-4] ; reuse "color" as the sample number +{{- if .Library}} + lea {{.DI}}, [{{.DI}} + {{.AX}}*8] ; edi points now to the sample table entry +{{- else}} +{{- .Prepare "su_sample_offsets" | indent 4}} + lea {{.DI}}, [{{.Use "su_sample_offsets"}} + {{.AX}}*8]; edi points now to the sample table entry +{{- end}} +{{- .Float 84.28074964676522 | .Prepare | indent 4}} + fmul dword [{{.Float 84.28074964676522 | .Use}}] ; p*r + fistp dword [{{.SP}}] + pop {{.DX}} ; edx is now the sample number + movzx ebx, word [{{.DI}} + 4] ; ecx = loopstart + sub edx, ebx ; if sample number < loop start + jl su_oscillat_sample_not_looping ; then we're not looping yet + mov eax, edx ; eax = sample number + movzx ecx, word [{{.DI}} + 6] ; edi is now the loop length + xor edx, edx ; div wants edx to be empty + div ecx ; edx is now the remainder +su_oscillat_sample_not_looping: + add edx, ebx ; sampleno += loopstart + add edx, dword [{{.DI}}] +{{- .Prepare "su_sample_table" | indent 4}} + fild word [{{.Use "su_sample_table"}} + {{.DX}}*2] +{{- .Float 32767.0 | .Prepare | indent 4}} + fdiv dword [{{.Float 32767.0 | .Use}}] + {{- .PopRegs .AX .DX .CX .BX .DI | indent 4}} + ret +{{end}} + + +{{- if .HasOp "loadval"}} +;------------------------------------------------------------------------------- +; LOADVAL opcode +;------------------------------------------------------------------------------- +{{- if .Mono "loadval"}} +; Mono: push 2*v-1 on stack, where v is the input to port "value" +{{- end}} +{{- if .Stereo "loadval"}} +; Stereo: push 2*v-1 twice on stack +{{- end}} +;------------------------------------------------------------------------------- +{{.Func "su_op_loadval" "Opcode"}} + {{- if .StereoAndMono "loadval" }} + jnc su_op_loadval_mono + {{- end}} + {{- if .Stereo "loadval" }} + call su_op_loadval_mono +su_op_loadval_mono: + {{- end }} + fld dword [{{.Input "loadval" "value"}}] ; v +{{- .Float 0.5 | .Prepare | indent 4}} + fsub dword [{{.Float 0.5 | .Use}}] + fadd st0 ; 2*v-1 + ret +{{end}} + + +{{- if .HasOp "receive"}} +;------------------------------------------------------------------------------- +; RECEIVE opcode +;------------------------------------------------------------------------------- +{{- if .Mono "receive"}} +; Mono: push l on stack, where l is the left channel received +{{- end}} +{{- if .Stereo "receive"}} +; Stereo: push l r on stack +{{- end}} +;------------------------------------------------------------------------------- +{{.Func "su_op_receive" "Opcode"}} + lea {{.DI}}, [{{.WRK}}+su_unit.ports] +{{- if .StereoAndMono "receive"}} + jnc su_op_receive_mono +{{- end}} +{{- if .Stereo "receive"}} + xor ecx,ecx + fld dword [{{.DI}}+4] + mov dword [{{.DI}}+4],ecx +{{- end}} +{{- if .StereoAndMono "receive"}} +su_op_receive_mono: + xor ecx,ecx +{{- end}} + fld dword [{{.DI}}] + mov dword [{{.DI}}],ecx + ret +{{end}} + + +{{- if .HasOp "in"}} +;------------------------------------------------------------------------------- +; IN opcode: inputs and clears a global port +;------------------------------------------------------------------------------- +; Mono: push the left channel of a global port (out or aux) +; Stereo: also push the right channel (stack in l r order) +;------------------------------------------------------------------------------- +{{.Func "su_op_in" "Opcode"}} + lodsb + mov {{.DI}}, [{{.Stack "Synth"}}] +{{- if .StereoAndMono "in"}} + jnc su_op_in_mono +{{- end}} +{{- if .Stereo "in"}} + xor ecx, ecx ; we cannot xor before jnc, so we have to do it mono & stereo. LAHF / SAHF could do it, but is the same number of bytes with more entropy + fld dword [{{.DI}} + su_synthworkspace.right + {{.AX}}*4] + mov dword [{{.DI}} + su_synthworkspace.right + {{.AX}}*4], ecx +{{- end}} +{{- if .StereoAndMono "in"}} +su_op_in_mono: + xor ecx, ecx +{{- end}} + fld dword [{{.DI}} + su_synthworkspace.left + {{.AX}}*4] + mov dword [{{.DI}} + su_synthworkspace.left + {{.AX}}*4], ecx + ret +{{end}} diff --git a/sointu_templates/structs.asm b/sointu_templates/structs.asm new file mode 100644 index 0000000..c2780e7 --- /dev/null +++ b/sointu_templates/structs.asm @@ -0,0 +1,57 @@ +%define SU_LENGTH_IN_SAMPLES {{.MaxSamples}} +%define SU_SAMPLE_RATE 44100 +%define SU_BPM {{.Song.BPM}}. + +;------------------------------------------------------------------------------- +; unit struct +;------------------------------------------------------------------------------- +struc su_unit + .state resd 8 + .ports resd 8 + .size: +endstruc + +;------------------------------------------------------------------------------- +; voice struct +;------------------------------------------------------------------------------- +struc su_voice + .note resd 1 + .sustain resd 1 + .inputs resd 8 + .reserved resd 6 ; this is done to so the whole voice is 2^n long, see polyphonic player + .workspace resb 63 * su_unit.size + .size: +endstruc + +;------------------------------------------------------------------------------- +; synthworkspace struct +;------------------------------------------------------------------------------- +struc su_synthworkspace + .curvoices resb 32 ; these are used by the multitrack player to store which voice is playing on which track + .left resd 1 + .right resd 1 + .aux resd 6 ; 3 auxiliary signals + .voices resb 32 * su_voice.size + .size: +endstruc + +;------------------------------------------------------------------------------- +; su_delayline_wrk struct +;------------------------------------------------------------------------------- +struc su_delayline_wrk + .dcin resd 1 + .dcout resd 1 + .filtstate resd 1 + .buffer resd 65536 + .size: +endstruc + +;------------------------------------------------------------------------------- +; su_sample_offset struct +;------------------------------------------------------------------------------- +struc su_sample_offset ; length conveniently 8 bytes, so easy to index + .start resd 1 + .loopstart resw 1 + .looplength resw 1 + .size: +endstruc \ No newline at end of file diff --git a/song.yml b/song.yml new file mode 100644 index 0000000..4ed31b1 --- /dev/null +++ b/song.yml @@ -0,0 +1,412 @@ +bpm: 165 +rowsperbeat: 4 +score: + tracks: + - numvoices: 1 + order: [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 2, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 4, 1, 1, 1, 2, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 4, -1, -1, 5] + patterns: [[1, 1, 1, 1, 1, 1, 1, 1, 57, 1, 1, 1, 57], [1, 1, 1, 1, 57, 1, 1, 1, 1, 1, 1, 1, 57], [1, 1, 1, 1, 57, 1, 1, 1, 1, 1, 1, 57, 57], [1, 1, 1, 1, 57, 57, 57, 57, 1, 1, 1, 1, 57], [1, 1, 1, 1, 57, 1, 1, 1, 57, 57, 1, 1, 57], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0]] + - numvoices: 1 + order: [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 2, 1, 1, 1, 3, 1, 1, 1, 2, 1, 1, 1, 4, 1, 1, 1, 2, 1, 1, 1, 3, 1, 1, 1, 2, 1, 1, 1, 4, -1, -1, 5] + patterns: [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 46, 1, 1, 1, 46], [46, 1, 1, 1, 1, 1, 1, 1, 1, 1, 46], [46, 1, 1, 1, 1, 1, 1, 1, 46, 1, 46], [46, 1, 1, 1, 1, 1, 1, 1, 46, 46, 46, 46], [46, 1, 1, 1, 1, 1, 1, 1, 1, 1, 46, 46], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0]] + - numvoices: 1 + order: [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, 1] + patterns: [[48, 1, 48, 1, 1, 1, 48, 1, 48, 1, 48, 1, 1, 1, 48], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0]] + - numvoices: 1 + order: [0, -1, 1, -1, 2, 3, 4, 5, 2, 6, 7, 8, 0, -1, -1, 9] + patterns: [[50], [48], [46], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 48, 1, 50, 1, 53], [43], [1, 1, 1, 1, 1, 1, 1, 1, 43, 1, 45, 1, 46, 1, 48], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 48, 1, 50], [52], [45], [1, 1, 1, 1, 1, 1, 1, 1, 0]] + - numvoices: 1 + order: [0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2] + patterns: [[62, 0, 64, 0, 65, 0, 69, 0, 65, 0, 64, 0, 62, 0, 64, 0], [65, 0, 72, 0, 65, 0, 64, 0, 62, 0, 64, 0, 65, 0, 71, 0], [65, 0, 69, 0, 65, 0, 64, 0, 62, 0, 64, 0, 65, 0, 72, 0]] + - numvoices: 1 + order: [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 1, 1, 2, 2, 3, 3, 0, 0, 1, 1, 2, 2, 3, 3, 0, 0, 1, 1, 2, 2, 3, 3, 0, 0, 1, 1, 2, 2, 3, 4] + patterns: [[50, 0, 50, 1, 50, 0, 50, 1, 50, 0, 50, 1, 50, 0, 50], [48, 0, 48, 1, 48, 0, 48, 1, 48, 0, 48, 1, 48, 0, 48], [46, 0, 46, 1, 46, 0, 46, 1, 46, 0, 46, 1, 46, 0, 46], [43, 0, 43, 1, 43, 0, 43, 1, 43, 0, 43, 1, 43, 0, 43], [43, 0, 43, 1, 43, 0, 43, 1, 43, 0, 43, 1, 43, 0, 43, 0]] + rowsperpattern: 16 + length: 52 +patch: + - name: snare + numvoices: 1 + units: + - type: envelope + id: 1 + parameters: {attack: 22, decay: 64, gain: 38, release: 0, stereo: 0, sustain: 0} + - type: oscillator + id: 2 + parameters: {color: 128, detune: 64, gain: 70, lfo: 0, phase: 19, shape: 66, stereo: 0, transpose: 77, type: 0} + - type: distort + id: 3 + parameters: {drive: 78, stereo: 0} + - type: hold + id: 4 + parameters: {damp: 0, dry: 128, feedback: 96, holdfreq: 128, notetracking: 2, pregain: 40, stereo: 0} + - type: mulp + id: 5 + parameters: {panning: 64, stereo: 0} + - type: envelope + id: 6 + parameters: {attack: 0, auxgain: 64, decay: 68, gain: 70, outgain: 64, release: 0, stereo: 0, sustain: 0} + - type: noise + id: 81 + parameters: {gain: 128, shape: 2, stereo: 0} + - type: mulp + id: 82 + parameters: {stereo: 0} + - type: filter + id: 83 + parameters: {bandpass: 0, frequency: 97, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 128, stereo: 0} + - type: filter + id: 84 + parameters: {bandpass: 0, frequency: 108, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 128, stereo: 0} + - type: addp + id: 85 + parameters: {stereo: 0} + - type: filter + id: 86 + parameters: {bandpass: 0, frequency: 100, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 128, stereo: 0} + - type: filter + id: 87 + parameters: {bandpass: 0, frequency: 19, highpass: 1, lowpass: 0, negbandpass: 0, neghighpass: 0, resonance: 98, stereo: 0} + - type: filter + id: 88 + parameters: {bandpass: 0, frequency: 15, highpass: 1, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 128, stereo: 0} + - type: filter + id: 89 + parameters: {bandpass: 0, frequency: 40, highpass: 1, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 74, stereo: 0} + - type: filter + id: 90 + parameters: {bandpass: 0, frequency: 23, highpass: 1, lowpass: 0, negbandpass: 0, neghighpass: 0, resonance: 42, stereo: 0} + - type: distort + id: 91 + parameters: {drive: 97, stereo: 0} + - type: hold + id: 92 + parameters: {holdfreq: 128, stereo: 0} + - type: pan + id: 93 + parameters: {panning: 59, stereo: 0} + - type: delay + id: 94 + parameters: {damp: 0, dry: 128, feedback: 112, notetracking: 0, pregain: 27, stereo: 0} + varargs: [1116, 1188, 1276, 1356, 1422, 1492, 1556, 1618] + - type: xch + id: 95 + parameters: {stereo: 0} + - type: delay + id: 96 + parameters: {damp: 0, dry: 128, feedback: 112, notetracking: 0, pregain: 27, stereo: 0} + varargs: [1140, 1212, 1300, 1380, 1446, 1516, 1580, 1642] + - type: xch + id: 97 + parameters: {stereo: 0} + - type: outaux + id: 98 + parameters: {auxgain: 0, outgain: 128, stereo: 1} + - type: envelope + id: 99 + parameters: {attack: 38, decay: 49, gain: 128, release: 0, stereo: 0, sustain: 0} + - type: send + id: 100 + parameters: {amount: 70, port: 0, sendpop: 1, target: 2} + - type: envelope + id: 101 + parameters: {attack: 47, decay: 54, gain: 128, release: 0, stereo: 0, sustain: 0} + - type: send + id: 102 + parameters: {amount: 19, port: 0, sendpop: 1, target: 83} + - type: envelope + id: 103 + parameters: {attack: 54, decay: 68, gain: 128, release: 0, stereo: 0, sustain: 70} + - type: send + id: 104 + parameters: {amount: 26, port: 0, sendpop: 1, target: 86} + - name: kick + numvoices: 1 + units: + - type: envelope + id: 105 + parameters: {attack: 39, channel: 2, decay: 71, gain: 128, release: 0, stereo: 0, sustain: 0} + - type: send + id: 205 + parameters: {amount: 7, damp: 64, dry: 128, feedback: 125, notetracking: 0, port: 0, pregain: 40, sendpop: 0, stereo: 0, target: 184, unit: 0, voice: 0} + - type: oscillator + id: 106 + parameters: {color: 115, detune: 64, gain: 54, lfo: 0, phase: 42, shape: 114, stereo: 0, transpose: 88, type: 1, unison: 1} + - type: distort + id: 107 + parameters: {drive: 58, stereo: 0} + - type: hold + id: 108 + parameters: {holdfreq: 100, stereo: 0} + - type: mulp + id: 126 + parameters: {stereo: 0} + - type: envelope + id: 110 + parameters: {attack: 28, decay: 45, gain: 128, release: 0, stereo: 0, sustain: 0} + - type: noise + id: 111 + parameters: {gain: 102, shape: 84, stereo: 0} + - type: filter + id: 112 + parameters: {bandpass: 0, frequency: 93, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 63, stereo: 0} + - type: filter + id: 113 + parameters: {bandpass: 0, frequency: 75, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 87, stereo: 0} + - type: mulp + id: 10 + parameters: {stereo: 0} + - type: addp + id: 127 + parameters: {stereo: 0} + - type: filter + id: 12 + parameters: {bandpass: 0, frequency: 11, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 66, stereo: 0} + - type: filter + id: 13 + parameters: {bandpass: 0, frequency: 53, highpass: 1, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 128, stereo: 0} + - type: filter + id: 14 + parameters: {bandpass: 0, frequency: 7, highpass: 1, lowpass: 0, negbandpass: 0, neghighpass: 0, resonance: 76, stereo: 0} + - type: filter + id: 15 + parameters: {bandpass: 0, frequency: 13, highpass: 1, lowpass: 0, negbandpass: 0, neghighpass: 0, resonance: 54, stereo: 0} + - type: pan + id: 114 + parameters: {panning: 64, stereo: 0} + - type: delay + id: 115 + parameters: {damp: 128, dry: 128, feedback: 20, notetracking: 0, pregain: 21, stereo: 0} + varargs: [1140, 1212, 1300, 1380, 1446, 1516, 1580, 1642] + - type: xch + id: 116 + parameters: {stereo: 0} + - type: delay + id: 117 + parameters: {damp: 128, dry: 128, feedback: 20, notetracking: 0, pregain: 20, stereo: 0} + varargs: [1116, 1188, 1276, 1356, 1422, 1492, 1556, 1618] + - type: outaux + id: 118 + parameters: {auxgain: 0, outgain: 128, stereo: 1} + - type: envelope + id: 119 + parameters: {attack: 71, decay: 128, gain: 128, release: 0, stereo: 0, sustain: 74} + - type: envelope + id: 120 + parameters: {attack: 49, decay: 71, gain: 128, release: 0, stereo: 0, sustain: 121} + - type: mulp + id: 121 + parameters: {stereo: 0} + - type: send + id: 122 + parameters: {amount: 0, port: 0, sendpop: 1, target: 106} + - name: hihat + numvoices: 1 + units: + - type: envelope + id: 128 + parameters: {attack: 4, decay: 53, gain: 128, release: 0, stereo: 0, sustain: 0} + - type: noise + id: 129 + parameters: {gain: 128, shape: 102, stereo: 0} + - type: mulp + id: 130 + parameters: {stereo: 0} + - type: filter + id: 131 + parameters: {bandpass: 0, frequency: 87, highpass: 1, lowpass: 0, negbandpass: 0, neghighpass: 0, resonance: 128, stereo: 0} + - type: filter + id: 132 + parameters: {bandpass: 0, frequency: 95, highpass: 1, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 128, stereo: 0} + - type: filter + id: 133 + parameters: {bandpass: 1, frequency: 89, highpass: 0, lowpass: 0, negbandpass: 0, neghighpass: 0, resonance: 71, stereo: 0} + - type: pan + id: 134 + parameters: {panning: 61, stereo: 0} + - type: delay + id: 135 + parameters: {damp: 29, dry: 128, feedback: 50, notetracking: 0, pregain: 28, stereo: 0} + varargs: [1116, 1188, 1276, 1356, 1422, 1492, 1556, 1618] + - type: xch + id: 136 + parameters: {stereo: 0} + - type: delay + id: 137 + parameters: {damp: 0, dry: 128, feedback: 40, notetracking: 0, pregain: 32, stereo: 0} + varargs: [1140, 1212, 1300, 1380, 1446, 1516, 1580, 1642] + - type: outaux + id: 11 + parameters: {auxgain: 0, outgain: 45, stereo: 1} + - name: ba_reese + numvoices: 1 + units: + - type: oscillator + id: 149 + parameters: {color: 128, detune: 64, gain: 128, lfo: 1, phase: 4, shape: 64, stereo: 0, transpose: 64, type: 1} + - type: send + id: 150 + parameters: {amount: 52, port: 1, sendpop: 1, stereo: 0, target: 159} + - type: oscillator + id: 152 + parameters: {color: 54, detune: 42, gain: 128, lfo: 1, phase: 55, shape: 63, stereo: 0, transpose: 41, type: 0} + - type: send + id: 153 + parameters: {amount: 75, port: 1, sendpop: 0, stereo: 0, target: 160} + - type: send + id: 154 + parameters: {amount: 59, port: 3, sendpop: 1, stereo: 0, target: 160} + - type: oscillator + id: 156 + parameters: {color: 128, detune: 64, gain: 128, lfo: 1, phase: 0, shape: 64, stereo: 0, transpose: 74, type: 0} + - type: send + id: 157 + parameters: {amount: 69, port: 1, sendpop: 1, stereo: 0, target: 161} + - type: envelope + id: 189 + parameters: {attack: 22, decay: 54, gain: 64, release: 64, stereo: 1, sustain: 64} + - type: oscillator + id: 159 + parameters: {color: 128, detune: 87, gain: 128, lfo: 0, phase: 64, shape: 65, stereo: 1, transpose: 64, type: 1, unison: 3} + - type: oscillator + id: 160 + parameters: {color: 128, detune: 60, gain: 128, lfo: 0, phase: 87, shape: 64, stereo: 1, transpose: 64, type: 1, unison: 2} + - type: oscillator + id: 161 + parameters: {color: 128, detune: 69, gain: 128, lfo: 0, phase: 0, shape: 106, stereo: 1, transpose: 64, type: 0, unison: 2} + - type: addp + id: 162 + parameters: {stereo: 1} + - type: addp + id: 16 + parameters: {stereo: 1} + - type: filter + id: 17 + parameters: {bandpass: 0, frequency: 62, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 91, stereo: 1} + - type: filter + id: 190 + parameters: {bandpass: 0, frequency: 93, highpass: 1, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 74, stereo: 1} + - type: xch + id: 195 + parameters: {stereo: 1} + - type: filter + id: 196 + parameters: {bandpass: 0, frequency: 94, highpass: 1, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 78, stereo: 1} + - type: mulp + id: 19 + parameters: {stereo: 1} + - type: distort + id: 193 + parameters: {drive: 96, stereo: 1} + - type: filter + id: 194 + parameters: {bandpass: 0, frequency: 10, highpass: 1, lowpass: 1, negbandpass: 1, neghighpass: 0, resonance: 128, stereo: 1} + - type: outaux + id: 21 + parameters: {auxgain: 21, outgain: 29, stereo: 1} + - type: oscillator + id: 191 + parameters: {color: 128, detune: 64, gain: 128, lfo: 1, phase: 0, shape: 64, stereo: 0, transpose: 40, type: 0, unison: 0} + - type: send + id: 192 + parameters: {amount: 71, port: 0, sendpop: 1, stereo: 0, target: 190, unit: 0, voice: 0} + - type: oscillator + id: 197 + parameters: {color: 128, detune: 64, gain: 128, lfo: 1, phase: 0, shape: 64, stereo: 0, transpose: 40, type: 0} + - type: send + id: 198 + parameters: {amount: 86, port: 0, sendpop: 1, stereo: 0, target: 196, unit: 0, voice: 0} + - name: ky_sine + numvoices: 1 + units: + - type: envelope + id: 163 + parameters: {attack: 39, decay: 62, gain: 71, release: 30, stereo: 0, sustain: 0} + - type: oscillator + id: 164 + parameters: {color: 53, detune: 62, gain: 72, lfo: 0, phase: 14, shape: 27, stereo: 0, transpose: 88, type: 0} + - type: oscillator + id: 165 + parameters: {color: 113, detune: 66, gain: 128, lfo: 0, phase: 64, shape: 64, stereo: 0, transpose: 88, type: 0} + - type: addp + id: 166 + parameters: {stereo: 0} + - type: mulp + id: 167 + parameters: {stereo: 0} + - type: pan + id: 168 + parameters: {panning: 64, stereo: 0} + - type: delay + id: 169 + parameters: {damp: 0, dry: 128, feedback: 86, notetracking: 2, pregain: 71, stereo: 0} + varargs: [64] + - type: xch + id: 170 + parameters: {stereo: 0} + - type: delay + id: 171 + parameters: {damp: 0, dry: 128, feedback: 90, notetracking: 2, pregain: 73, stereo: 0} + varargs: [36] + - type: outaux + id: 172 + parameters: {auxgain: 52, outgain: 84, stereo: 1} + - name: ba_trana + numvoices: 1 + units: + - type: envelope + id: 173 + parameters: {attack: 39, decay: 81, gain: 128, release: 0, stereo: 0, sustain: 0} + - type: send + id: 174 + parameters: {amount: 87, port: 0, sendpop: 0, target: 181} + - type: send + id: 175 + parameters: {amount: 90, port: 3, sendpop: 1, target: 179} + - type: envelope + id: 176 + parameters: {attack: 33, decay: 57, gain: 128, release: 57, stereo: 0, sustain: 67} + - type: oscillator + id: 177 + parameters: {color: 98, detune: 64, gain: 101, lfo: 0, phase: 0, shape: 64, stereo: 0, transpose: 76, type: 0, unison: 0} + - type: oscillator + id: 179 + parameters: {color: 78, detune: 48, gain: 64, lfo: 0, phase: 85, shape: 97, stereo: 0, transpose: 64, type: 1, unison: 3} + - type: addp + id: 178 + parameters: {stereo: 0} + - type: mulp + id: 185 + parameters: {stereo: 0} + - type: filter + id: 181 + parameters: {bandpass: 0, frequency: 39, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 0, resonance: 82, stereo: 0} + - type: filter + id: 182 + parameters: {bandpass: 0, frequency: 15, highpass: 0, lowpass: 1, negbandpass: 0, neghighpass: 1, resonance: 128, stereo: 0} + - type: pan + id: 183 + parameters: {panning: 64, stereo: 0} + - type: distort + id: 200 + parameters: {drive: 80, stereo: 1} + - type: compressor + id: 186 + parameters: {attack: 48, invgain: 87, ratio: 54, release: 56, stereo: 1, threshold: 46} + - type: mulp + id: 187 + parameters: {stereo: 1} + - type: outaux + id: 184 + parameters: {auxgain: 35, outgain: 85, stereo: 1} + - name: Global + numvoices: 1 + units: + - type: in + id: 7 + parameters: {channel: 2, stereo: 1} + - type: delay + id: 8 + parameters: {damp: 77, dry: 128, feedback: 114, notetracking: 0, pregain: 50, stereo: 1} + varargs: [1116, 1188, 1276, 1356, 1422, 1492, 1556, 1618, 1140, 1212, 1300, 1380, 1446, 1516, 1580, 1642] + - type: out + id: 9 + parameters: {gain: 128, stereo: 1}