diff --git a/bridge/bridge.go b/bridge/bridge.go index fc4391c..423cf99 100644 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -8,6 +8,7 @@ import "math" // #cgo LDFLAGS: "${SRCDIR}/../build/src/libsointu.a" // #include import "C" +import "errors" type SynthState = C.SynthState @@ -54,11 +55,33 @@ func (o Opcode) Mono() Opcode { return Opcode(byte(o) & 0xFE) // clear lowest bit } -func (s *SynthState) Render(buffer []float32) int { - fmt.Printf("Calling Render...\n") - var ret = C.su_render_samples(s, C.int(len(buffer))/2, (*C.float)(&buffer[0])) - fmt.Printf("Returning from Render...\n") - return int(ret) +// Render tries to fill the buffer with samples rendered by Sointu. +// Parameters: +// buffer float32 slice to fill with rendered samples. Stereo signal, so +// should have even length. +// maxRows maximum number of tracker rows that will be rendered in one +// call. Can be a large number, but keep finite to avoid getting +// stuck trying to render rows in case the synth is buggy and +// produces no sample. +// callback called every time a row advances. Won't get called if you have +// not set SamplesPerRow explicitly. +// Returns the number samples rendered, len(buffer)/2 in the typical case where buffer was filled +func (s *SynthState) Render(buffer []float32,maxRows int,callback func()) (int, error) { + if len(buffer) % 1 == 1 { + return -1, errors.New("Render writes stereo signals, so buffer should have even length") + } + maxSamples := len(buffer) / 2 + remaining := maxSamples + for i := 0; i < maxRows; i++ { + remaining = int(C.su_render_samples(s,C.int(remaining),(*C.float)(&buffer[2*(maxSamples-remaining)]))) + if (remaining >= 0) { // values >= 0 mean that row end was reached + callback() + } + if (remaining <= 0) { // values <= 0 mean that buffer is full, ready to return + break; + } + } + return maxSamples - remaining, nil } func (s *SynthState) SetCommands(c [2048]Opcode) { diff --git a/bridge/bridge_test.go b/bridge/bridge_test.go index e85eb95..8126e27 100644 --- a/bridge/bridge_test.go +++ b/bridge/bridge_test.go @@ -26,31 +26,18 @@ func TestBridge(t *testing.T) { 95, 64, 64, 80, 128, // envelope 2 128} s := bridge.NewSynthState() - // memcpy(synthState->Commands, commands, sizeof(commands)); s.SetCommands(commands) - // memcpy(synthState->Values, values, sizeof(values)); s.SetValues(values) - // synthState->RandSeed = 1; - // initialized in NewSynthState - // synthState->RowLen = INT32_MAX; - // synthState->NumVoices = 1; s.NumVoices = 1 - // synthState->Synth.Voices[0].Note = 64; s.Synth.Voices[0].Note = 64 - // retval = su_render_samples(buffer, su_max_samples / 2, synthState); - buffer := make([]float32, su_max_samples) - remaining := s.Render(buffer) - if remaining > 0 { - t.Fatalf("could not render full buffer, %v bytes remaining, expected <= 0", remaining) + s.SamplesPerRow = SAMPLES_PER_ROW * 8 // this song is two blocks of 8 rows, release during second + buffer := make([]float32, 2*su_max_samples) + n,err := s.Render(buffer,2,func() { + s.Synth.Voices[0].Release = 1 + }) + if n < su_max_samples { + t.Fatalf("could not fill the whole buffer, %v samples rendered, %v expected", n, su_max_samples) } - // synthState->Synth.Voices[0].Release++; - s.Synth.Voices[0].Release++ - sbuffer := make([]float32, su_max_samples) - remaining = s.Render(sbuffer) - if remaining > 0 { - t.Fatalf("could not render second full buffer, %v bytes remaining, expected <= 0", remaining) - } - buffer = append(buffer, sbuffer...) _, filename, _, _ := runtime.Caller(0) expectedb, err := ioutil.ReadFile(path.Join(path.Dir(filename), "..", "tests", "expected_output", "test_render_samples.raw")) if err != nil {