mirror of
https://github.com/vsariola/sointu.git
synced 2025-05-28 03:10:24 -04:00
Change the Go API to have two versions: Render(buffer []float32), which always fill the whole buffer, and RenderTime(buffer []float32,int maxtime), which ends either when the buffer is full, or modulated time is reached.
This commit is contained in:
parent
7a9ac3489b
commit
5f4b85b0a4
@ -82,25 +82,55 @@ func (o Opcode) Mono() Opcode {
|
||||
}
|
||||
|
||||
// Render tries to fill the buffer with samples rendered by Sointu.
|
||||
// Use this version if you are not interested in time modulation. Will always
|
||||
// fill the buffer.
|
||||
// Parameters:
|
||||
// buffer float32 slice to fill with rendered samples. Stereo signal, so
|
||||
// should have even length.
|
||||
// Returns a tuple (int, bool, error), consisting of the number samples
|
||||
// rendered (len(buffer)/2 in the case where buffer was filled, less or equal
|
||||
// if row end was reached before buffer was full), and bool indicating if row
|
||||
// has ended
|
||||
func (s *SynthState) Render(buffer []float32) (int, bool, error) {
|
||||
// Returns an error if something went wrong.
|
||||
func (s *SynthState) Render(buffer []float32) error {
|
||||
if len(buffer)%1 == 1 {
|
||||
return -1, false, 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
|
||||
cs := (*C.SynthState)(s)
|
||||
cs.SamplesPerRow = C.uint(math.MaxInt32)
|
||||
cs.RowTick = 0
|
||||
C.su_render_samples((*C.SynthState)(s), C.int(maxSamples), (*C.float)(&buffer[0]))
|
||||
return nil
|
||||
}
|
||||
|
||||
// RenderTime renders until the buffer is full or the modulated time is reached, whichever
|
||||
// happens first.
|
||||
// Parameters:
|
||||
// buffer float32 slice to fill with rendered samples. Stereo signal, so
|
||||
// should have even length.
|
||||
// maxtime how long nominal time to render in samples. Speed unit might modulate time
|
||||
// so the actual number of samples rendered is not the
|
||||
// Returns a tuple (int, int, error), consisting of:
|
||||
// samples number of samples rendered in the buffer
|
||||
// time how much the time advanced
|
||||
// error potential error
|
||||
// In practice, if nsamples = len(buffer)/2, then time <= maxtime. If maxtime was reached
|
||||
// first, then nsamples <= len(buffer)/2 and time >= maxtime. Note that it could happen that
|
||||
// time > maxtime, as it is modulated and the time could advance by 2 or more, so the loop
|
||||
// exit condition would fire when the time is already past maxtime.
|
||||
// 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) {
|
||||
if len(buffer)%1 == 1 {
|
||||
return -1, -1, errors.New("Render writes stereo signals, so buffer should have even length")
|
||||
}
|
||||
maxSamples := len(buffer) / 2
|
||||
cs := (*C.SynthState)(s)
|
||||
cs.SamplesPerRow = C.uint(maxtime) // these two lines are here just because the C-API is not
|
||||
cs.RowTick = 0 // updated. SamplesPerRow should be "maxtime" and passed as a parameter
|
||||
retval := int(C.su_render_samples((*C.SynthState)(s), C.int(maxSamples), (*C.float)(&buffer[0])))
|
||||
if retval < 0 {
|
||||
return maxSamples, false, nil
|
||||
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, true, nil
|
||||
return maxSamples, int(cs.RowTick), nil
|
||||
} else {
|
||||
return maxSamples - retval, true, nil
|
||||
return maxSamples - retval, int(cs.RowTick), nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,10 +189,6 @@ func (s *SynthState) Release(voice int) {
|
||||
cs.Synth.Voices[voice].Release = 1
|
||||
}
|
||||
|
||||
func (s *SynthState) SetSamplesPerRow(spr int) {
|
||||
s.SamplesPerRow = C.uint(spr)
|
||||
}
|
||||
|
||||
func NewSynthState() *SynthState {
|
||||
s := new(SynthState)
|
||||
s.RandSeed = 1
|
||||
|
@ -30,22 +30,15 @@ func TestBridge(t *testing.T) {
|
||||
}},
|
||||
})
|
||||
s.Trigger(0, 64)
|
||||
s.SamplesPerRow = SAMPLES_PER_ROW * 8 // this song is two blocks of 8 rows, release before second block start
|
||||
buffer := make([]float32, 2*su_max_samples)
|
||||
n, rowend, err := s.Render(buffer)
|
||||
if n < su_max_samples/2 {
|
||||
t.Fatalf("render should have filled half of the buffer on first call, %v samples rendered, %v expected", n, su_max_samples/2)
|
||||
}
|
||||
if rowend != true {
|
||||
t.Fatalf("Row end should have been hit (rowend should have been true) on the first call to Render")
|
||||
err := s.Render(buffer[:len(buffer)/2])
|
||||
if err != nil {
|
||||
t.Fatalf("first render gave an error")
|
||||
}
|
||||
s.Release(0)
|
||||
n, rowend, err = s.Render(buffer[(n * 2):])
|
||||
if n < su_max_samples/2 {
|
||||
t.Fatalf("render should have filled second half of the buffer on the second call, %v samples rendered, %v expected", n, su_max_samples/2)
|
||||
}
|
||||
if rowend != true {
|
||||
t.Fatalf("Row end should have been hit (rowend should have been true) on the second call to Render")
|
||||
err = s.Render(buffer[len(buffer)/2:])
|
||||
if err != nil {
|
||||
t.Fatalf("first render gave an error")
|
||||
}
|
||||
_, filename, _, _ := runtime.Caller(0)
|
||||
expectedb, err := ioutil.ReadFile(path.Join(path.Dir(filename), "..", "tests", "expected_output", "test_render_samples.raw"))
|
||||
|
@ -93,7 +93,6 @@ func (s *Song) Render() ([]float32, error) {
|
||||
}
|
||||
synth := bridge.NewSynthState()
|
||||
synth.SetPatch(s.Patch)
|
||||
synth.SetSamplesPerRow(44100 * 60 / (s.BPM * 4))
|
||||
curVoices := make([]int, len(s.Tracks))
|
||||
for i := range curVoices {
|
||||
curVoices[i] = s.FirstTrackVoice(i)
|
||||
@ -104,6 +103,7 @@ func (s *Song) Render() ([]float32, error) {
|
||||
}
|
||||
buffer := make([]float32, samples*2)
|
||||
totaln := 0
|
||||
rowtime := 44100 * 60 / (s.BPM * 4)
|
||||
for row := 0; row < s.TotalRows(); row++ {
|
||||
patternRow := row % s.PatternRows()
|
||||
pattern := row / s.PatternRows()
|
||||
@ -123,8 +123,8 @@ func (s *Song) Render() ([]float32, error) {
|
||||
synth.Trigger(curVoices[t], note)
|
||||
}
|
||||
}
|
||||
n, _, _ := synth.Render(buffer[2*totaln:])
|
||||
totaln += n
|
||||
samples, _, _ := synth.RenderTime(buffer[2*totaln:], rowtime)
|
||||
totaln += samples
|
||||
}
|
||||
return buffer, nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user