diff --git a/src/player.asm b/src/player.asm deleted file mode 100644 index f279b27..0000000 --- a/src/player.asm +++ /dev/null @@ -1,241 +0,0 @@ -%if BITS == 32 - %define render_prologue pushad ; stdcall & everything nonvolatile except eax, ecx, edx - %macro render_epilogue 0 - popad - ret 4 ; clean the passed parameter from stack. - %endmacro -%elifidn __OUTPUT_FORMAT__,win64 - %define render_prologue push_registers rcx,rdi,rsi,rbx,rbp ; rcx = ptr to buf. rdi,rsi,rbx,rbp nonvolatile - %macro render_epilogue 0 - pop_registers rcx,rdi,rsi,rbx,rbp - ret - %endmacro -%else ; 64 bit mac & linux - %define render_prologue push_registers rdi,rbx,rbp ; rdi = ptr to buf. rbx & rbp nonvolatile - %macro render_epilogue 0 - pop_registers rdi,rbx,rbp - ret - %endmacro -%endif - -;=============================================================================== -; Uninitialized data: The one and only synth object -;=============================================================================== -SECT_BSS(susynth) - -su_synth_obj resb su_synth.size - -%if DELAY_ID > -1 ; if we use delay, then the synth obj should be immediately followed by the delay workspaces - resb NUM_DELAY_LINES*su_delayline_wrk.size -%endif - -;------------------------------------------------------------------------------- -; output_sound macro: used by the render function to write sound to buffer -;------------------------------------------------------------------------------- -; The macro contains the ifdef hell to handle 16bit output and clipping cases -; to keep the main function more readable -; Stack : sample row pushad output_ptr -;------------------------------------------------------------------------------- -%macro output_sound 0 - %ifndef SU_USE_16BIT_OUTPUT - %ifndef SU_CLIP_OUTPUT ; The modern way. No need to clip; OS can do it. - mov _DI, [_SP+su_stack.bufferptr - su_stack.output_sound] ; edi containts ptr - mov _SI, PTRWORD su_synth_obj + su_synth.left - movsd ; copy left channel to output buffer - movsd ; copy right channel to output buffer - mov [_SP+su_stack.bufferptr - su_stack.output_sound], _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 [_SP+su_stack.bufferptr - su_stack.output_sound] ; esi points to the output buffer - xor _CX,_CX - xor eax,eax - %%loop: ; loop over two channels, left & right - do fld dword [,su_synth_obj+su_synth.left,_CX*4,] - call su_clip - fstp dword [_SI] - do mov dword [,su_synth_obj+su_synth.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 - %endif - %else ; 16-bit output, always clipped. This is a bit legacy method. - mov _SI, [_SP+su_stack.bufferptr - su_stack.output_sound] ; esi points to the output buffer - mov _DI, PTRWORD su_synth_obj+su_synth.left - mov ecx, 2 - %%loop: ; loop over two channels, left & right - fld dword [_DI] - call su_clip - do fmul dword [,c_32767,] - push _AX - fistp dword [_SP] - pop _AX - mov word [_SI],ax ; // store integer converted right sample - xor eax,eax - stosd - add _SI,2 - loop %%loop - mov [_SP+su_stack.bufferptr - su_stack.output_sound], _SI ; save esi back to stack - %define USE_C_32767 - %endif -%endmacro - -;------------------------------------------------------------------------------- -; su_render function: the entry point for the synth -;------------------------------------------------------------------------------- -; Has the signature su_render(void *ptr), where ptr is a pointer to -; the output buffer -; Stack: output_ptr -;------------------------------------------------------------------------------- -SECT_TEXT(surender) - -EXPORT MANGLE_FUNC(su_render,PTRSIZE) ; Stack: ptr - render_prologue -%ifdef INCLUDE_GMDLS - call su_gmdls_load -%endif - xor eax, eax -%ifdef INCLUDE_MULTIVOICE_TRACKS - push VOICETRACK_BITMASK -%endif - push 1 ; randseed - push _AX ; global tick time -su_render_rowloop: ; loop through every row in the song - push _AX ; Stack: row pushad ptr - 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 ; Stack: sample row pushad ptr - %ifdef INCLUDE_POLYPHONY - push POLYPHONY_BITMASK ; does the next voice reuse the current opcodes? - %endif - push MAX_VOICES - mov _DX, PTRWORD su_synth_obj ; _DX points to the synth object - mov COM, PTRWORD MANGLE_DATA(su_commands) ; COM points to vm code - mov VAL, PTRWORD MANGLE_DATA(su_params) ; VAL points to unit params - %if DELAY_ID > -1 - lea _CX, [_DX + su_synth.size - su_delayline_wrk.filtstate] - %endif - lea WRK, [_DX + su_synth.voices] ; WRK points to the first voice - call MANGLE_FUNC(su_run_vm,0) ; run through the VM code - pop _AX - %ifdef INCLUDE_POLYPHONY - pop _AX - %endif - output_sound ; *ptr++ = left, *ptr++ = right - pop _AX - inc dword [_SP + PTRSIZE] ; increment global time, used by delays - inc eax - cmp eax, SAMPLES_PER_ROW - jl su_render_sampleloop - pop _AX ; Stack: pushad ptr - inc eax - cmp eax, TOTAL_ROWS - jl su_render_rowloop -%ifdef INCLUDE_MULTIVOICE_TRACKS - add _SP, su_stack.render_epilogue - su_stack.tick ; rewind the remaining tack -%else - pop _AX - pop _AX -%endif - render_epilogue - -;------------------------------------------------------------------------------- -; su_update_voices function: polyphonic & chord implementation -;------------------------------------------------------------------------------- -; Input: eax : current row within song -; Dirty: pretty much everything -;------------------------------------------------------------------------------- -SECT_TEXT(suupdvce) - -%ifdef INCLUDE_MULTIVOICE_TRACKS - -su_update_voices: ; Stack: retaddr row - xor edx, edx - mov ebx, PATTERN_SIZE ; 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 - do{lea _SI, [},MANGLE_DATA(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, PATTERN_SIZE ; eax = offset to current pattern data - do{movzx eax,byte [},MANGLE_DATA(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 [_SP + su_stack.voicetrack - su_stack.update_voices + 2*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, HLD ; anything but hold causes action - je short su_update_voices_nexttrack - mov cl, byte [_BP] - mov edi, ecx - add edi, ebx - shl edi, MAX_UNITS_SHIFT + 6 ; 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, MAX_UNITS_SHIFT + 6 ; each unit = 64 bytes and there are 1< one voice per track ve_SIon - -su_update_voices: ; Stack: retaddr row - xor edx, edx - xor ebx, ebx - mov bl, PATTERN_SIZE - div ebx ; eax = current pattern, edx = current row in pattern - do{lea _SI, [},MANGLE_DATA(su_tracks),_AX,]; esi points to the pattern data for current track - mov _DI, PTRWORD su_synth_obj+su_synth.voices - mov bl, MAX_TRACKS ; MAX_TRACKS is always <= 32 so this is ok -su_update_voices_trackloop: - movzx eax, byte [_SI] ; eax = current pattern - imul eax, PATTERN_SIZE ; eax = offset to current pattern data - do{movzx eax, byte [}, MANGLE_DATA(su_patterns),_AX,_DX,] ; ecx = note - cmp al, HLD ; anything but hold causes action - je short su_update_voices_nexttrack - inc dword [_DI+su_voice.release] ; set the voice currently active to release; notice that it could increment any number of times - cmp al, HLD - jl su_update_voices_nexttrack ; if cl < HLD (no new note triggered) goto nexttrack -su_update_voices_retrigger: - stosd ; save note - mov ecx, (su_voice.size - su_voice.release)/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, MAX_PATTERNS - dec ebx - jnz short su_update_voices_trackloop - ret - -%endif ;INCLUDE_MULTIVOICE_TRACKS diff --git a/src/sointu.asm b/src/sointu.asm index dc64508..1941b50 100644 --- a/src/sointu.asm +++ b/src/sointu.asm @@ -52,6 +52,20 @@ %endmacro %define PUSH_REG_SIZE(n) (n*8) + + %ifidn __OUTPUT_FORMAT__,win64 + %define render_prologue push_registers rcx,rdi,rsi,rbx,rbp ; rcx = ptr to buf. rdi,rsi,rbx,rbp nonvolatile + %macro render_epilogue 0 + pop_registers rcx,rdi,rsi,rbx,rbp + ret + %endmacro + %else ; 64 bit mac & linux + %define render_prologue push_registers rdi,rbx,rbp ; rdi = ptr to buf. rbx & rbp nonvolatile + %macro render_epilogue 0 + pop_registers rdi,rbx,rbp + ret + %endmacro + %endif %else %define WRK ebp ; alias for unit workspace %define VAL esi ; alias for unit values (transformed/untransformed) @@ -95,6 +109,13 @@ %endmacro %define PUSH_REG_SIZE(n) 32 + + %define render_prologue pushad ; stdcall & everything nonvolatile except eax, ecx, edx + + %macro render_epilogue 0 + popad + ret 4 ; clean the passed parameter from stack. + %endmacro %endif struc su_stack ; the structure of stack _as the units see it_ @@ -140,6 +161,17 @@ struc su_stack ; the structure of stack _as the units see it_ .size endstruc +;=============================================================================== +; Uninitialized data: The one and only synth object +;=============================================================================== +SECT_BSS(susynth) + +su_synth_obj resb su_synth.size + +%if DELAY_ID > -1 ; if we use delay, then the synth obj should be immediately followed by the delay workspaces + resb NUM_DELAY_LINES*su_delayline_wrk.size +%endif + ;=============================================================================== ; 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. @@ -240,6 +272,217 @@ EXPORT MANGLE_FUNC(su_power,0) fstp st1 ; 2^x ret +;------------------------------------------------------------------------------- +; output_sound macro: used by the render function to write sound to buffer +;------------------------------------------------------------------------------- +; The macro contains the ifdef hell to handle 16bit output and clipping cases +; to keep the main function more readable +; Stack : sample row pushad output_ptr +;------------------------------------------------------------------------------- +%macro output_sound 0 + %ifndef SU_USE_16BIT_OUTPUT + %ifndef SU_CLIP_OUTPUT ; The modern way. No need to clip; OS can do it. + mov _DI, [_SP+su_stack.bufferptr - su_stack.output_sound] ; edi containts ptr + mov _SI, PTRWORD su_synth_obj + su_synth.left + movsd ; copy left channel to output buffer + movsd ; copy right channel to output buffer + mov [_SP+su_stack.bufferptr - su_stack.output_sound], _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 [_SP+su_stack.bufferptr - su_stack.output_sound] ; esi points to the output buffer + xor _CX,_CX + xor eax,eax + %%loop: ; loop over two channels, left & right + do fld dword [,su_synth_obj+su_synth.left,_CX*4,] + call su_clip + fstp dword [_SI] + do mov dword [,su_synth_obj+su_synth.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 + %endif + %else ; 16-bit output, always clipped. This is a bit legacy method. + mov _SI, [_SP+su_stack.bufferptr - su_stack.output_sound] ; esi points to the output buffer + mov _DI, PTRWORD su_synth_obj+su_synth.left + mov ecx, 2 + %%loop: ; loop over two channels, left & right + fld dword [_DI] + call su_clip + do fmul dword [,c_32767,] + push _AX + fistp dword [_SP] + pop _AX + mov word [_SI],ax ; // store integer converted right sample + xor eax,eax + stosd + add _SI,2 + loop %%loop + mov [_SP+su_stack.bufferptr - su_stack.output_sound], _SI ; save esi back to stack + %define USE_C_32767 + %endif +%endmacro + +;------------------------------------------------------------------------------- +; su_render function: the entry point for the synth +;------------------------------------------------------------------------------- +; Has the signature su_render(void *ptr), where ptr is a pointer to +; the output buffer +; Stack: output_ptr +;------------------------------------------------------------------------------- +SECT_TEXT(surender) + +EXPORT MANGLE_FUNC(su_render,PTRSIZE) ; Stack: ptr + render_prologue +%ifdef INCLUDE_GMDLS + call su_gmdls_load +%endif + xor eax, eax +%ifdef INCLUDE_MULTIVOICE_TRACKS + push VOICETRACK_BITMASK +%endif + push 1 ; randseed + push _AX ; global tick time +su_render_rowloop: ; loop through every row in the song + push _AX ; Stack: row pushad ptr + 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 ; Stack: sample row pushad ptr + %ifdef INCLUDE_POLYPHONY + push POLYPHONY_BITMASK ; does the next voice reuse the current opcodes? + %endif + push MAX_VOICES + mov _DX, PTRWORD su_synth_obj ; _DX points to the synth object + mov COM, PTRWORD MANGLE_DATA(su_commands) ; COM points to vm code + mov VAL, PTRWORD MANGLE_DATA(su_params) ; VAL points to unit params + %if DELAY_ID > -1 + lea _CX, [_DX + su_synth.size - su_delayline_wrk.filtstate] + %endif + lea WRK, [_DX + su_synth.voices] ; WRK points to the first voice + call MANGLE_FUNC(su_run_vm,0) ; run through the VM code + pop _AX + %ifdef INCLUDE_POLYPHONY + pop _AX + %endif + output_sound ; *ptr++ = left, *ptr++ = right + pop _AX + inc dword [_SP + PTRSIZE] ; increment global time, used by delays + inc eax + cmp eax, SAMPLES_PER_ROW + jl su_render_sampleloop + pop _AX ; Stack: pushad ptr + inc eax + cmp eax, TOTAL_ROWS + jl su_render_rowloop +%ifdef INCLUDE_MULTIVOICE_TRACKS + add _SP, su_stack.render_epilogue - su_stack.tick ; rewind the remaining tack +%else + pop _AX + pop _AX +%endif + render_epilogue + +;------------------------------------------------------------------------------- +; su_update_voices function: polyphonic & chord implementation +;------------------------------------------------------------------------------- +; Input: eax : current row within song +; Dirty: pretty much everything +;------------------------------------------------------------------------------- +SECT_TEXT(suupdvce) + +%ifdef INCLUDE_MULTIVOICE_TRACKS + +su_update_voices: ; Stack: retaddr row + xor edx, edx + mov ebx, PATTERN_SIZE ; 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 + do{lea _SI, [},MANGLE_DATA(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, PATTERN_SIZE ; eax = offset to current pattern data + do{movzx eax,byte [},MANGLE_DATA(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 [_SP + su_stack.voicetrack - su_stack.update_voices + 2*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, HLD ; anything but hold causes action + je short su_update_voices_nexttrack + mov cl, byte [_BP] + mov edi, ecx + add edi, ebx + shl edi, MAX_UNITS_SHIFT + 6 ; 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, MAX_UNITS_SHIFT + 6 ; each unit = 64 bytes and there are 1< one voice per track ve_SIon + +su_update_voices: ; Stack: retaddr row + xor edx, edx + xor ebx, ebx + mov bl, PATTERN_SIZE + div ebx ; eax = current pattern, edx = current row in pattern + do{lea _SI, [},MANGLE_DATA(su_tracks),_AX,]; esi points to the pattern data for current track + mov _DI, PTRWORD su_synth_obj+su_synth.voices + mov bl, MAX_TRACKS ; MAX_TRACKS is always <= 32 so this is ok +su_update_voices_trackloop: + movzx eax, byte [_SI] ; eax = current pattern + imul eax, PATTERN_SIZE ; eax = offset to current pattern data + do{movzx eax, byte [}, MANGLE_DATA(su_patterns),_AX,_DX,] ; ecx = note + cmp al, HLD ; anything but hold causes action + je short su_update_voices_nexttrack + inc dword [_DI+su_voice.release] ; set the voice currently active to release; notice that it could increment any number of times + cmp al, HLD + jl su_update_voices_nexttrack ; if cl < HLD (no new note triggered) goto nexttrack +su_update_voices_retrigger: + stosd ; save note + mov ecx, (su_voice.size - su_voice.release)/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, MAX_PATTERNS + dec ebx + jnz short su_update_voices_trackloop + ret + +%endif ;INCLUDE_MULTIVOICE_TRACKS + ;------------------------------------------------------------------------------- ; Include the rest of the code ;------------------------------------------------------------------------------- @@ -252,7 +495,6 @@ EXPORT MANGLE_FUNC(su_power,0) ; if needed. %include "opcodes/effects.asm" %include "introspection.asm" -%include "player.asm" %ifidn __OUTPUT_FORMAT__,win64 %include "win64/gmdls_win64.asm"