mirror of
https://github.com/vsariola/sointu.git
synced 2025-05-28 03:10:24 -04:00
Change the C-API to roughly match the new Go-API.
The parameter order is now so that all the in/out int parameters are in the end of the signature.
This commit is contained in:
parent
5f4b85b0a4
commit
64afa9fb48
@ -2,7 +2,6 @@ package bridge
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"math"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// #cgo CFLAGS: -I"${SRCDIR}/../include"
|
// #cgo CFLAGS: -I"${SRCDIR}/../include"
|
||||||
@ -93,10 +92,10 @@ func (s *SynthState) Render(buffer []float32) error {
|
|||||||
return errors.New("Render writes stereo signals, so buffer should have even length")
|
return errors.New("Render writes stereo signals, so buffer should have even length")
|
||||||
}
|
}
|
||||||
maxSamples := len(buffer) / 2
|
maxSamples := len(buffer) / 2
|
||||||
cs := (*C.SynthState)(s)
|
errcode := C.su_render((*C.SynthState)(s), (*C.float)(&buffer[0]), C.int(maxSamples))
|
||||||
cs.SamplesPerRow = C.uint(math.MaxInt32)
|
if errcode > 0 {
|
||||||
cs.RowTick = 0
|
return errors.New("Render failed")
|
||||||
C.su_render_samples((*C.SynthState)(s), C.int(maxSamples), (*C.float)(&buffer[0]))
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +105,8 @@ func (s *SynthState) Render(buffer []float32) error {
|
|||||||
// buffer float32 slice to fill with rendered samples. Stereo signal, so
|
// buffer float32 slice to fill with rendered samples. Stereo signal, so
|
||||||
// should have even length.
|
// should have even length.
|
||||||
// maxtime how long nominal time to render in samples. Speed unit might modulate time
|
// maxtime how long nominal time to render in samples. Speed unit might modulate time
|
||||||
// so the actual number of samples rendered is not the
|
// so the actual number of samples rendered depends on the modulation and if
|
||||||
|
// buffer is full before maxtime is reached.
|
||||||
// Returns a tuple (int, int, error), consisting of:
|
// Returns a tuple (int, int, error), consisting of:
|
||||||
// samples number of samples rendered in the buffer
|
// samples number of samples rendered in the buffer
|
||||||
// time how much the time advanced
|
// time how much the time advanced
|
||||||
@ -118,20 +118,15 @@ func (s *SynthState) Render(buffer []float32) error {
|
|||||||
// Under no conditions, nsamples >= len(buffer)/2 i.e. guaranteed to never overwrite the buffer.
|
// Under no conditions, nsamples >= len(buffer)/2 i.e. guaranteed to never overwrite the buffer.
|
||||||
func (s *SynthState) RenderTime(buffer []float32, maxtime int) (int, int, error) {
|
func (s *SynthState) RenderTime(buffer []float32, maxtime int) (int, int, error) {
|
||||||
if len(buffer)%1 == 1 {
|
if len(buffer)%1 == 1 {
|
||||||
return -1, -1, errors.New("Render writes stereo signals, so buffer should have even length")
|
return -1, -1, errors.New("RenderTime writes stereo signals, so buffer should have even length")
|
||||||
}
|
}
|
||||||
maxSamples := len(buffer) / 2
|
samples := C.int(len(buffer) / 2)
|
||||||
cs := (*C.SynthState)(s)
|
time := C.int(maxtime)
|
||||||
cs.SamplesPerRow = C.uint(maxtime) // these two lines are here just because the C-API is not
|
errcode := int(C.su_render_time((*C.SynthState)(s), (*C.float)(&buffer[0]), &samples, &time))
|
||||||
cs.RowTick = 0 // updated. SamplesPerRow should be "maxtime" and passed as a parameter
|
if errcode > 0 {
|
||||||
retval := int(C.su_render_samples((*C.SynthState)(s), C.int(maxSamples), (*C.float)(&buffer[0])))
|
return -1, -1, errors.New("RenderTime failed")
|
||||||
if retval < 0 { // this ugliness is just because the C-API is not updated yet
|
|
||||||
return maxSamples, int(cs.RowTick), nil
|
|
||||||
} else if retval == 0 {
|
|
||||||
return maxSamples, int(cs.RowTick), nil
|
|
||||||
} else {
|
|
||||||
return maxSamples - retval, int(cs.RowTick), nil
|
|
||||||
}
|
}
|
||||||
|
return int(samples), int(time), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SynthState) SetPatch(patch Patch) error {
|
func (s *SynthState) SetPatch(patch Patch) error {
|
||||||
@ -192,9 +187,5 @@ func (s *SynthState) Release(voice int) {
|
|||||||
func NewSynthState() *SynthState {
|
func NewSynthState() *SynthState {
|
||||||
s := new(SynthState)
|
s := new(SynthState)
|
||||||
s.RandSeed = 1
|
s.RandSeed = 1
|
||||||
// The default behaviour will be to have rows/beats disabled i.e.
|
|
||||||
// fill the whole buffer every call. This is a lot better default
|
|
||||||
// behaviour than leaving this 0 (Render would never render anything)
|
|
||||||
s.SamplesPerRow = math.MaxInt32
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
@ -39,8 +39,6 @@ typedef struct SynthState {
|
|||||||
unsigned int NumVoices;
|
unsigned int NumVoices;
|
||||||
unsigned int RandSeed;
|
unsigned int RandSeed;
|
||||||
unsigned int GlobalTick;
|
unsigned int GlobalTick;
|
||||||
unsigned int RowTick;
|
|
||||||
unsigned int SamplesPerRow; // nominal value, actual rows could be more or less due to speed modulation
|
|
||||||
} SynthState;
|
} SynthState;
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
@ -58,9 +56,26 @@ typedef struct SynthState {
|
|||||||
extern void CALLCONV su_load_gmdls(void);
|
extern void CALLCONV su_load_gmdls(void);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// su_render_samples(SynthState* synthState, int maxSamples, float* buffer):
|
// int su_render(SynthState* synthState, float* buffer, int samples):
|
||||||
// Renders at most maxsamples to the buffer, using and modifying the
|
// Renders 'samples' number of samples to the buffer, using and modifying
|
||||||
// synthesizer state in synthState.
|
// the synthesizer state in synthState.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// synthState pointer to current synthState. RandSeed should be > 0 e.g. 1
|
||||||
|
// buffer audio sample buffer, L R L R ...
|
||||||
|
// samples maximum number of samples to be rendered. WARNING: buffer
|
||||||
|
// should have a length of 2 * samples as the audio is stereo.
|
||||||
|
//
|
||||||
|
// Returns error code:
|
||||||
|
// 0 everything ok
|
||||||
|
// (returns always 0 as no errors are implemented yet)
|
||||||
|
int CALLCONV su_render(SynthState* synthState, float* buffer, int samples);
|
||||||
|
|
||||||
|
// int su_render_time(SynthState* synthState, 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:
|
// Parameters:
|
||||||
// synthState pointer to current synthState. RandSeed should be > 0 e.g. 1
|
// synthState pointer to current synthState. RandSeed should be > 0 e.g. 1
|
||||||
@ -68,39 +83,23 @@ extern void CALLCONV su_load_gmdls(void);
|
|||||||
// rendered; either set it to INT32_MAX to always render full
|
// rendered; either set it to INT32_MAX to always render full
|
||||||
// buffer, or something like SAMPLE_RATE * 60 / (BPM * 4) for
|
// buffer, or something like SAMPLE_RATE * 60 / (BPM * 4) for
|
||||||
// having 4 rows per beat.
|
// having 4 rows per beat.
|
||||||
// maxSamples maximum number of samples to be rendered. buffer should
|
|
||||||
// have a length of 2 * maxsamples as the audio is stereo.
|
|
||||||
// buffer audio sample buffer, L R L R ...
|
// 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.
|
||||||
//
|
//
|
||||||
// Returns:
|
// The value referred by samples is changed to contain the actual number of samples rendered
|
||||||
// -1 end of row was not reached & buffer full
|
// Similarly, the value referred by time is changed to contain the number of time ticks advanced.
|
||||||
// 0 end of row was reached & buffer full (there is space for zero
|
// If samples_out == samples_in, then is must be that time_in <= time_out.
|
||||||
// samples in the buffer)
|
// If samples_out < samples_in, then time_out >= time_in. Note that it could happen that
|
||||||
// n>0 end of row was reached & there is space for n samples in the buffer
|
// 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
|
||||||
//
|
//
|
||||||
// Beware of infinite loops: with a rowlen of 0; or without resetting rowtick
|
// Returns error code:
|
||||||
// between rows; or with a problematic synth patch e.g. if the speed is
|
// 0 everything ok
|
||||||
// modulated to be become infinite, this function might return maxsamples i.e.
|
// (no actual errors implemented yet)
|
||||||
// not render any samples. If you try to call this with your buffer until the
|
int CALLCONV su_render_time(SynthState* synthState, float* buffer, int* samples, int* time);
|
||||||
// whole buffer is filled, you will be stuck in an infinite loop.
|
|
||||||
//
|
|
||||||
// So a reasonable track player would be something like:
|
|
||||||
//
|
|
||||||
// function render_buffer(maxsamples,buffer) {
|
|
||||||
// remaining = maxsamples
|
|
||||||
// for i = 0..MAX_TRIES // limit retries to prevent infinite loop
|
|
||||||
// remaining = su_render_samples(synthState,
|
|
||||||
// remaining,
|
|
||||||
// &buffer[(maxsamples-remaining)*2])
|
|
||||||
// if remaining >= 0 // end of row reached
|
|
||||||
// song_row++ // advance row
|
|
||||||
// retrigger/release voices based on the new row
|
|
||||||
// if remaining <= 0 // buffer full
|
|
||||||
// return
|
|
||||||
// return // could not fill buffer despite MAX_TRIES, something is wrong
|
|
||||||
// // audio will come to sudden end
|
|
||||||
// }
|
|
||||||
extern int CALLCONV su_render_samples(SynthState* synthState, int maxSamples, float* buffer);
|
|
||||||
|
|
||||||
// Arithmetic opcode ids
|
// Arithmetic opcode ids
|
||||||
extern const int su_add_id;
|
extern const int su_add_id;
|
||||||
|
104
src/sointu.asm
104
src/sointu.asm
@ -34,28 +34,34 @@ struc su_synth_state
|
|||||||
.numvoices resd 1
|
.numvoices resd 1
|
||||||
.randseed resd 1
|
.randseed resd 1
|
||||||
.globaltime resd 1
|
.globaltime resd 1
|
||||||
.rowtick resd 1
|
|
||||||
.rowlen resd 1
|
|
||||||
endstruc
|
endstruc
|
||||||
|
|
||||||
SECT_TEXT(sursampl)
|
SECT_TEXT(sursampl)
|
||||||
|
|
||||||
EXPORT MANGLE_FUNC(su_render_samples,12)
|
EXPORT MANGLE_FUNC(su_render_time,16)
|
||||||
%if BITS == 32 ; stdcall
|
%if BITS == 32 ; stdcall
|
||||||
pushad ; push registers
|
pushad ; push registers
|
||||||
mov ecx, [esp + 4 + 32] ; ecx = &synthState
|
mov ecx, [esp + 4 + 32] ; ecx = &synthState
|
||||||
mov esi, [esp + 8 + 32] ; esi = bufsize
|
mov edx, [esp + 8 + 32] ; edx = &buffer
|
||||||
mov edx, [esp + 12 + 32] ; edx = &buffer
|
mov esi, [esp + 12 + 32] ; esi = &samples
|
||||||
|
mov ebx, [esp + 16 + 32] ; ebx = &time
|
||||||
%else
|
%else
|
||||||
%ifidn __OUTPUT_FORMAT__,win64 ; win64 ABI: rdx = bufsize, r8 = &buffer, rcx = &synthstate
|
%ifidn __OUTPUT_FORMAT__,win64 ; win64 ABI: rcx = &synthstate, rdx = &buffer, r8 = &bufsize, r9 = &time
|
||||||
push_registers rdi, rsi, rbx, rbp ; win64 ABI: these registers are non-volatile
|
push_registers rdi, rsi, rbx, rbp ; win64 ABI: these registers are non-volatile
|
||||||
mov rsi, rdx ; rsi = bufsize
|
mov rsi, r8 ; rsi = &samples
|
||||||
mov rdx, r8 ; rdx = &buffer
|
mov rbx, r9 ; rbx = &time
|
||||||
%else ; System V ABI: rsi = bufsize, rdx = &buffer, rdi = &synthstate
|
%else ; System V ABI: rdi = &synthstate, rsi = &buffer, rdx = &samples, rcx = &time
|
||||||
push_registers rbx, rbp ; System V ABI: these registers are non-volatile
|
push_registers rbx, rbp ; System V ABI: these registers are non-volatile
|
||||||
|
mov rbx, rcx ; rbx points to time
|
||||||
|
xchg rsi, rdx ; rdx points to buffer, rsi points to samples
|
||||||
mov rcx, rdi ; rcx = &Synthstate
|
mov rcx, rdi ; rcx = &Synthstate
|
||||||
%endif
|
%endif
|
||||||
%endif
|
%endif
|
||||||
|
push _SI ; push the pointer to samples
|
||||||
|
push _BX ; push the pointer to time
|
||||||
|
xor eax, eax ; samplenumber starts at 0
|
||||||
|
push _AX ; push samplenumber to stack
|
||||||
|
mov esi, [_SI] ; zero extend dereferenced pointer
|
||||||
push _SI ; push bufsize
|
push _SI ; push bufsize
|
||||||
push _DX ; push bufptr
|
push _DX ; push bufptr
|
||||||
push _CX ; this takes place of the voicetrack
|
push _CX ; this takes place of the voicetrack
|
||||||
@ -63,14 +69,17 @@ EXPORT MANGLE_FUNC(su_render_samples,12)
|
|||||||
push _AX ; randseed
|
push _AX ; randseed
|
||||||
mov eax, [_CX + su_synth_state.globaltime]
|
mov eax, [_CX + su_synth_state.globaltime]
|
||||||
push _AX ; global tick time
|
push _AX ; global tick time
|
||||||
mov eax, [_CX + su_synth_state.rowlen]
|
mov ebx, dword [_BX] ; zero extend dereferenced pointer
|
||||||
push _AX ; push the rowlength to stack so we can easily compare to it, normally this would be row
|
push _BX ; the nominal rowlength should be time_in
|
||||||
mov eax, [_CX + su_synth_state.rowtick]
|
xor eax, eax ; rowtick starts at 0
|
||||||
su_render_samples_loop:
|
su_render_samples_loop:
|
||||||
cmp eax, [_SP] ; compare current tick to rowlength
|
cmp eax, [_SP] ; if rowtick >= maxtime
|
||||||
jge su_render_samples_row_advance
|
jge su_render_samples_time_finish ; goto finish
|
||||||
sub dword [_SP + PTRSIZE*5], 1 ; compare current tick to rowlength
|
mov ecx, [_SP + PTRSIZE*5] ; ecx = buffer length in samples
|
||||||
jb su_render_samples_buffer_full
|
cmp [_SP + PTRSIZE*6], ecx ; if samples >= maxsamples
|
||||||
|
jge su_render_samples_time_finish ; goto finish
|
||||||
|
inc eax ; time++
|
||||||
|
inc dword [_SP + PTRSIZE*6] ; samples++
|
||||||
mov _CX, [_SP + PTRSIZE*3]
|
mov _CX, [_SP + PTRSIZE*3]
|
||||||
push _AX ; push rowtick
|
push _AX ; push rowtick
|
||||||
mov eax, [_CX + su_synth_state.polyphony]
|
mov eax, [_CX + su_synth_state.polyphony]
|
||||||
@ -97,24 +106,26 @@ su_render_samples_loop:
|
|||||||
stosd ; clear right 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
|
pop _AX
|
||||||
inc dword [_SP + PTRSIZE] ; increment global time, used by delays
|
inc dword [_SP + PTRSIZE] ; increment global time, used by delays
|
||||||
inc eax
|
|
||||||
jmp su_render_samples_loop
|
jmp su_render_samples_loop
|
||||||
su_render_samples_row_advance:
|
su_render_samples_time_finish:
|
||||||
xor eax, eax ; row has finished, so clear the rowtick for next round
|
|
||||||
su_render_samples_buffer_full:
|
|
||||||
pop _CX
|
pop _CX
|
||||||
pop _BX
|
pop _BX
|
||||||
pop _DX
|
pop _DX
|
||||||
pop _CX
|
pop _CX
|
||||||
mov [_CX + su_synth_state.randseed], edx
|
mov [_CX + su_synth_state.randseed], edx
|
||||||
mov [_CX + su_synth_state.globaltime], ebx
|
mov [_CX + su_synth_state.globaltime], ebx
|
||||||
mov [_CX + su_synth_state.rowtick], eax
|
pop _BX
|
||||||
pop _AX
|
pop _BX
|
||||||
pop _AX
|
pop _DX
|
||||||
|
pop _BX ; pop the pointer to time
|
||||||
|
pop _SI ; pop the pointer to samples
|
||||||
|
mov dword [_SI], edx ; *samples = samples rendered
|
||||||
|
mov dword [_BX], eax ; *time = time ticks rendered
|
||||||
|
xor eax, eax ; TODO: set eax to possible error code, now just 0
|
||||||
%if BITS == 32 ; stdcall
|
%if BITS == 32 ; stdcall
|
||||||
mov [_SP + 28],eax ; we want to return eax, but popad pops everything, so put eax to stack for popad to pop
|
mov [_SP + 28],eax ; we want to return eax, but popad pops everything, so put eax to stack for popad to pop
|
||||||
popad
|
popad
|
||||||
ret 12
|
ret 16
|
||||||
%else
|
%else
|
||||||
%ifidn __OUTPUT_FORMAT__,win64
|
%ifidn __OUTPUT_FORMAT__,win64
|
||||||
pop_registers rdi, rsi, rbx, rbp ; win64 ABI: these registers are non-volatile
|
pop_registers rdi, rsi, rbx, rbp ; win64 ABI: these registers are non-volatile
|
||||||
@ -123,3 +134,48 @@ su_render_samples_buffer_full:
|
|||||||
%endif
|
%endif
|
||||||
ret
|
ret
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
|
EXPORT MANGLE_FUNC(su_render,12)
|
||||||
|
%if BITS == 32 ; stdcall
|
||||||
|
mov eax, 0x7FFFFFFF ; don't care about time, just try to fill the buffer
|
||||||
|
push eax
|
||||||
|
mov eax, [esp + 8] ; eax = &synthState
|
||||||
|
mov ecx, [esp + 12] ; ecx = &buffer
|
||||||
|
mov edx, [esp + 16] ; edx = samples
|
||||||
|
push edx
|
||||||
|
lea edx, [esp + 4]
|
||||||
|
push edx
|
||||||
|
lea edx, [esp + 4]
|
||||||
|
push edx
|
||||||
|
push ecx
|
||||||
|
push eax
|
||||||
|
%else
|
||||||
|
%ifidn __OUTPUT_FORMAT__,win64 ; win64 ABI: rdx = bufsize, r8 = &buffer, rcx = &synthstate
|
||||||
|
push r8
|
||||||
|
mov r8, _SP
|
||||||
|
mov r9, 0x7FFFFFFF ; don't care about time, just try to fill the buffer
|
||||||
|
push r9
|
||||||
|
mov r9, _SP ; still, we have to pass a pointer to time, so pointer to stack
|
||||||
|
%else ; System V ABI: rdi = &synthstate, rsi = &buffer, rdx = samples
|
||||||
|
push rdx
|
||||||
|
mov rdx, _SP
|
||||||
|
mov rcx, 0x7FFFFFFF ; don't care about time, just try to fill the buffer
|
||||||
|
push rcx
|
||||||
|
mov rcx, _SP ; still, we have to pass a pointer to time, so pointer to stack
|
||||||
|
%endif
|
||||||
|
%endif
|
||||||
|
call MANGLE_FUNC(su_render_time,16)
|
||||||
|
%if BITS == 32 ; stdcall
|
||||||
|
pop ecx
|
||||||
|
pop ecx
|
||||||
|
ret 12
|
||||||
|
%else
|
||||||
|
%ifidn __OUTPUT_FORMAT__,win64 ; win64 ABI: rdx = bufsize, r8 = &buffer, rcx = &synthstate
|
||||||
|
pop r9
|
||||||
|
pop r8
|
||||||
|
%else
|
||||||
|
pop rcx
|
||||||
|
pop rdx
|
||||||
|
%endif
|
||||||
|
ret
|
||||||
|
%endif
|
||||||
|
@ -331,15 +331,15 @@ EXPORT MANGLE_FUNC(su_power,0)
|
|||||||
%endmacro
|
%endmacro
|
||||||
|
|
||||||
;-------------------------------------------------------------------------------
|
;-------------------------------------------------------------------------------
|
||||||
; su_render function: the entry point for the synth
|
; su_render_song function: the entry point for the synth
|
||||||
;-------------------------------------------------------------------------------
|
;-------------------------------------------------------------------------------
|
||||||
; Has the signature su_render(void *ptr), where ptr is a pointer to
|
; Has the signature su_render_song(void *ptr), where ptr is a pointer to
|
||||||
; the output buffer
|
; the output buffer. Renders the compile time hard-coded song to the buffer.
|
||||||
; Stack: output_ptr
|
; Stack: output_ptr
|
||||||
;-------------------------------------------------------------------------------
|
;-------------------------------------------------------------------------------
|
||||||
SECT_TEXT(surender)
|
SECT_TEXT(surensng)
|
||||||
|
|
||||||
EXPORT MANGLE_FUNC(su_render,PTRSIZE) ; Stack: ptr
|
EXPORT MANGLE_FUNC(su_render_song,PTRSIZE) ; Stack: ptr
|
||||||
render_prologue
|
render_prologue
|
||||||
xor eax, eax
|
xor eax, eax
|
||||||
%ifdef INCLUDE_MULTIVOICE_TRACKS
|
%ifdef INCLUDE_MULTIVOICE_TRACKS
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
#define SAMPLES_PER_ROW SAMPLE_RATE * 4 * 60 / (BPM * 16)
|
#define SAMPLES_PER_ROW SAMPLE_RATE * 4 * 60 / (BPM * 16)
|
||||||
const int su_max_samples = SAMPLES_PER_ROW * TOTAL_ROWS;
|
const int su_max_samples = SAMPLES_PER_ROW * TOTAL_ROWS;
|
||||||
|
|
||||||
void CALLCONV su_render(float* buffer) {
|
void CALLCONV su_render_song(float* buffer) {
|
||||||
SynthState* synthState;
|
SynthState* synthState;
|
||||||
const unsigned char commands[] = { su_envelope_id, // MONO
|
const unsigned char commands[] = { su_envelope_id, // MONO
|
||||||
su_envelope_id, // MONO
|
su_envelope_id, // MONO
|
||||||
@ -34,13 +34,12 @@ void CALLCONV su_render(float* buffer) {
|
|||||||
memcpy(synthState->Commands, commands, sizeof(commands));
|
memcpy(synthState->Commands, commands, sizeof(commands));
|
||||||
memcpy(synthState->Values, values, sizeof(values));
|
memcpy(synthState->Values, values, sizeof(values));
|
||||||
synthState->RandSeed = 1;
|
synthState->RandSeed = 1;
|
||||||
synthState->SamplesPerRow = INT32_MAX;
|
|
||||||
synthState->NumVoices = 1;
|
synthState->NumVoices = 1;
|
||||||
synthState->Synth.Voices[0].Note = 64;
|
synthState->Synth.Voices[0].Note = 64;
|
||||||
retval = su_render_samples(synthState, su_max_samples / 2, buffer);
|
retval = su_render(synthState, buffer, su_max_samples / 2);
|
||||||
synthState->Synth.Voices[0].Release++;
|
synthState->Synth.Voices[0].Release++;
|
||||||
buffer = buffer + su_max_samples;
|
buffer = buffer + su_max_samples;
|
||||||
retval = su_render_samples(synthState, su_max_samples / 2, buffer);
|
retval = su_render(synthState, buffer, su_max_samples / 2);
|
||||||
free(synthState);
|
free(synthState);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,10 @@ int main(int argc, char* argv[]) {
|
|||||||
const unsigned char values[] = { 64, 64, 64, 80, 128, // envelope 1
|
const unsigned char values[] = { 64, 64, 64, 80, 128, // envelope 1
|
||||||
95, 64, 64, 80, 128, // envelope 2
|
95, 64, 64, 80, 128, // envelope 2
|
||||||
128 };
|
128 };
|
||||||
int remaining, remainingOut;
|
int errcode;
|
||||||
|
int time;
|
||||||
|
int samples;
|
||||||
|
int totalrendered;
|
||||||
int retval;
|
int retval;
|
||||||
synthState = (SynthState*)malloc(sizeof(SynthState));
|
synthState = (SynthState*)malloc(sizeof(SynthState));
|
||||||
buffer = (float*)malloc(2 * sizeof(float) * su_max_samples);
|
buffer = (float*)malloc(2 * sizeof(float) * su_max_samples);
|
||||||
@ -30,55 +33,86 @@ int main(int argc, char* argv[]) {
|
|||||||
synthState->RandSeed = 1;
|
synthState->RandSeed = 1;
|
||||||
synthState->NumVoices = 1;
|
synthState->NumVoices = 1;
|
||||||
synthState->Synth.Voices[0].Note = 64;
|
synthState->Synth.Voices[0].Note = 64;
|
||||||
remaining = su_max_samples;
|
totalrendered = 0;
|
||||||
// First check that when RowLen = 0, we render nothing and remaining does not change
|
// First check that when we render using su_render_time with 0 time
|
||||||
synthState->SamplesPerRow = 0;
|
// we get nothing done
|
||||||
if (su_render_samples(synthState, remaining, buffer) != remaining)
|
samples = su_max_samples;
|
||||||
|
time = 0;
|
||||||
|
errcode = su_render_time(synthState, buffer, &samples, &time);
|
||||||
|
if (errcode != 0)
|
||||||
{
|
{
|
||||||
printf("su_render_samples rendered samples despite number of samples per row being 0");
|
printf("su_render_time returned error");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if (samples > 0)
|
||||||
|
{
|
||||||
|
printf("su_render_time rendered samples, despite it should not");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if (time > 0)
|
||||||
|
{
|
||||||
|
printf("su_render_time advanced time, despite it should not");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
// Then check that when we render using su_render_time with 0 samples,
|
||||||
|
// we get nothing done
|
||||||
|
samples = 0;
|
||||||
|
time = INT32_MAX;
|
||||||
|
errcode = su_render_time(synthState, buffer, &samples, &time);
|
||||||
|
if (errcode != 0)
|
||||||
|
{
|
||||||
|
printf("su_render_time returned error");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if (samples > 0)
|
||||||
|
{
|
||||||
|
printf("su_render_time rendered samples, despite it should not");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if (time > 0)
|
||||||
|
{
|
||||||
|
printf("su_render_time advanced time, despite it should not");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
// Then check that each time we call render, only SAMPLES_PER_ROW
|
// Then check that each time we call render, only SAMPLES_PER_ROW
|
||||||
// number of samples are rendered
|
// number of samples are rendered
|
||||||
synthState->SamplesPerRow = SAMPLES_PER_ROW;
|
|
||||||
for (int i = 0; i < 16; i++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
// Simulate "small buffers" i.e. render a buffer with 1 sample
|
// Simulate "small buffers" i.e. render a buffer with 1 sample
|
||||||
// check that buffer full
|
// check that buffer full
|
||||||
remainingOut = su_render_samples(synthState, 1, &buffer[2 * (su_max_samples - remaining)]);
|
samples = 1;
|
||||||
if (remainingOut != -1)
|
time = INT32_MAX;
|
||||||
|
su_render_time(synthState, &buffer[totalrendered*2], &samples, &time);
|
||||||
|
totalrendered += samples;
|
||||||
|
if (samples != 1)
|
||||||
{
|
{
|
||||||
printf("su_render_samples should have return -1, as it should have believed buffer is full");
|
printf("su_render should have return 1, as it should have believed buffer is full");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
if (synthState->RowTick != 1)
|
if (time != 1)
|
||||||
{
|
{
|
||||||
printf("su_render_samples RowTick should be at 1 after rendering 1 tick of a row");
|
printf("su_render should have advanced the time also by one");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
remaining--; // we rendered just one sample
|
samples = SAMPLES_PER_ROW - 1;
|
||||||
remainingOut = su_render_samples(synthState, remaining, &buffer[2 * (su_max_samples - remaining)]);
|
time = INT32_MAX;
|
||||||
if (remainingOut != remaining - SAMPLES_PER_ROW + 1)
|
su_render_time(synthState, &buffer[totalrendered * 2], &samples, &time);
|
||||||
|
totalrendered += samples;
|
||||||
|
if (samples != SAMPLES_PER_ROW - 1)
|
||||||
{
|
{
|
||||||
printf("su_render_samples did not render SAMPLES_PER_ROW, despite rowLen being SAMPLES_PER_ROW");
|
printf("su_render should have return SAMPLES_PER_ROW - 1, as it should have believed buffer is full");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
if (synthState->RowTick != 0)
|
if (time != SAMPLES_PER_ROW - 1)
|
||||||
{
|
{
|
||||||
printf("The row should be have been reseted");
|
printf("su_render should have advanced the time also by SAMPLES_PER_ROW - 1");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
remaining = remainingOut;
|
|
||||||
if (i == 8)
|
if (i == 8)
|
||||||
synthState->Synth.Voices[0].Release++;
|
synthState->Synth.Voices[0].Release++;
|
||||||
}
|
}
|
||||||
if (remaining != 0) {
|
if (totalrendered != su_max_samples)
|
||||||
printf("The buffer should be full and row finished");
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
// Finally, now that there is no more buffer remaining, should return -1
|
|
||||||
if (su_render_samples(synthState, remaining, &buffer[2 * (su_max_samples - remaining)]) != -1)
|
|
||||||
{
|
{
|
||||||
printf("su_render_samples should have ran out of buffer and thus return -1");
|
printf("su_render should have rendered a total of su_max_samples");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
#else // 64-bit
|
#else // 64-bit
|
||||||
#define CALLCONV // the asm will use honor honor correct x64 ABI on all 64-bit platforms
|
#define CALLCONV // the asm will use honor honor correct x64 ABI on all 64-bit platforms
|
||||||
#endif
|
#endif
|
||||||
extern void CALLCONV su_render(void *);
|
extern void CALLCONV su_render_song(void *);
|
||||||
|
|
||||||
#ifdef INCLUDE_GMDLS
|
#ifdef INCLUDE_GMDLS
|
||||||
extern void CALLCONV su_load_gmdls(void);
|
extern void CALLCONV su_load_gmdls(void);
|
||||||
@ -67,7 +67,7 @@ int main(int argc, char* argv[]) {
|
|||||||
su_load_gmdls();
|
su_load_gmdls();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
su_render(buf);
|
su_render_song(buf);
|
||||||
|
|
||||||
snprintf(filename, sizeof filename, "%s%s%s", expected_output_folder, test_name, ".raw");
|
snprintf(filename, sizeof filename, "%s%s%s", expected_output_folder, test_name, ".raw");
|
||||||
|
|
||||||
@ -117,7 +117,10 @@ int main(int argc, char* argv[]) {
|
|||||||
printf("4klang rendered different wave than expected\n");
|
printf("4klang rendered different wave than expected\n");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
max_diff = fmax(diff, max_diff);
|
|
||||||
|
if (diff > max_diff) {
|
||||||
|
max_diff = diff;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (max_diff > 1e-6) {
|
if (max_diff > 1e-6) {
|
||||||
|
Loading…
Reference in New Issue
Block a user