mirror of
				https://github.com/vsariola/sointu.git
				synced 2025-10-30 23:45:53 -04:00 
			
		
		
		
	Change bridge.go so that there is just SetPatch(...) function, instead of having to SetCommands, SetValues etc.
Now the patch definition in bridge_test.go and test_envelope.asm appear quite similar, so it's clear how they are related.
This commit is contained in:
		
							
								
								
									
										202
									
								
								bridge/bridge.go
									
									
									
									
									
								
							
							
						
						
									
										202
									
								
								bridge/bridge.go
									
									
									
									
									
								
							| @ -1,58 +1,74 @@ | ||||
| package bridge | ||||
|  | ||||
| import "fmt" | ||||
| import "unsafe" | ||||
| import "math" | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"math" | ||||
| ) | ||||
|  | ||||
| // #cgo CFLAGS: -I"${SRCDIR}/../include" | ||||
| // #cgo LDFLAGS: "${SRCDIR}/../build/src/libsointu.a" | ||||
| // #include <sointu.h> | ||||
| import "C" | ||||
| import "errors" | ||||
|  | ||||
| type SynthState = C.SynthState | ||||
| // SynthState contains the entire state of sointu sound engine | ||||
| type SynthState C.SynthState // hide C.Synthstate, explicit cast is still possible if needed | ||||
|  | ||||
| // Opcode is a single byte, representing the virtual machine commands used in Sointu | ||||
| type Opcode byte | ||||
|  | ||||
| var ( // cannot be const as the rhs are not known at compile-time | ||||
|   Add = Opcode(C.su_add_id) | ||||
|   Addp = Opcode(C.su_addp_id) | ||||
|   Pop = Opcode(C.su_pop_id) | ||||
|   Loadnote = Opcode(C.su_loadnote_id) | ||||
|   Mul = Opcode(C.su_mul_id) | ||||
|   Mulp = Opcode(C.su_mulp_id) | ||||
|   Push = Opcode(C.su_push_id) | ||||
|   Xch = Opcode(C.su_xch_id) | ||||
|   Distort = Opcode(C.su_distort_id) | ||||
|   Hold = Opcode(C.su_hold_id) | ||||
|   Crush = Opcode(C.su_crush_id) | ||||
|   Gain = Opcode(C.su_gain_id) | ||||
|   Invgain = Opcode(C.su_invgain_id) | ||||
|   Filter = Opcode(C.su_filter_id) | ||||
|   Clip = Opcode(C.su_clip_id) | ||||
|   Pan = Opcode(C.su_pan_id) | ||||
|   Delay = Opcode(C.su_delay_id) | ||||
|   Compres = Opcode(C.su_compres_id) | ||||
|   Advance = Opcode(C.su_advance_id) | ||||
|   Speed = Opcode(C.su_speed_id) | ||||
|   Out = Opcode(C.su_out_id) | ||||
|   Outaux = Opcode(C.su_outaux_id) | ||||
|   Aux = Opcode(C.su_aux_id) | ||||
|   Send = Opcode(C.su_send_id) | ||||
|   Envelope = Opcode(C.su_envelope_id) | ||||
|   Noise = Opcode(C.su_noise_id) | ||||
|   Oscillat = Opcode(C.su_oscillat_id) | ||||
|   Loadval = Opcode(C.su_loadval_id) | ||||
|   Receive = Opcode(C.su_receive_id) | ||||
|   In = Opcode(C.su_in_id) | ||||
| ) | ||||
|  | ||||
| func (o Opcode) Stereo() Opcode { | ||||
|   return Opcode(byte(o) | 1) // set lowest bit | ||||
| // Unit includes command (filter, oscillator, envelope etc.) and its parameters | ||||
| type Unit struct { | ||||
| 	Command Opcode | ||||
| 	Params  []byte | ||||
| } | ||||
|  | ||||
| // Instrument includes a list of units consisting of the instrument, and the number of polyphonic voices for this instrument | ||||
| type Instrument struct { | ||||
| 	NumVoices int | ||||
| 	Units     []Unit | ||||
| } | ||||
|  | ||||
| var ( // cannot be const as the rhs are not known at compile-time | ||||
| 	Add      = Opcode(C.su_add_id) | ||||
| 	Addp     = Opcode(C.su_addp_id) | ||||
| 	Pop      = Opcode(C.su_pop_id) | ||||
| 	Loadnote = Opcode(C.su_loadnote_id) | ||||
| 	Mul      = Opcode(C.su_mul_id) | ||||
| 	Mulp     = Opcode(C.su_mulp_id) | ||||
| 	Push     = Opcode(C.su_push_id) | ||||
| 	Xch      = Opcode(C.su_xch_id) | ||||
| 	Distort  = Opcode(C.su_distort_id) | ||||
| 	Hold     = Opcode(C.su_hold_id) | ||||
| 	Crush    = Opcode(C.su_crush_id) | ||||
| 	Gain     = Opcode(C.su_gain_id) | ||||
| 	Invgain  = Opcode(C.su_invgain_id) | ||||
| 	Filter   = Opcode(C.su_filter_id) | ||||
| 	Clip     = Opcode(C.su_clip_id) | ||||
| 	Pan      = Opcode(C.su_pan_id) | ||||
| 	Delay    = Opcode(C.su_delay_id) | ||||
| 	Compres  = Opcode(C.su_compres_id) | ||||
| 	Advance  = Opcode(C.su_advance_id) | ||||
| 	Speed    = Opcode(C.su_speed_id) | ||||
| 	Out      = Opcode(C.su_out_id) | ||||
| 	Outaux   = Opcode(C.su_outaux_id) | ||||
| 	Aux      = Opcode(C.su_aux_id) | ||||
| 	Send     = Opcode(C.su_send_id) | ||||
| 	Envelope = Opcode(C.su_envelope_id) | ||||
| 	Noise    = Opcode(C.su_noise_id) | ||||
| 	Oscillat = Opcode(C.su_oscillat_id) | ||||
| 	Loadval  = Opcode(C.su_loadval_id) | ||||
| 	Receive  = Opcode(C.su_receive_id) | ||||
| 	In       = Opcode(C.su_in_id) | ||||
| ) | ||||
|  | ||||
| // Stereo returns the stereo version of any (mono or stereo) opcode | ||||
| func (o Opcode) Stereo() Opcode { | ||||
| 	return Opcode(byte(o) | 1) // set lowest bit | ||||
| } | ||||
|  | ||||
| // Mono returns the mono version of any (mono or stereo) opcode | ||||
| func (o Opcode) Mono() Opcode { | ||||
|   return Opcode(byte(o) & 0xFE) // clear lowest bit | ||||
| 	return Opcode(byte(o) & 0xFE) // clear lowest bit | ||||
| } | ||||
|  | ||||
| // Render tries to fill the buffer with samples rendered by Sointu. | ||||
| @ -66,53 +82,79 @@ func (o Opcode) Mono() Opcode { | ||||
| //	 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) 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((*C.SynthState)(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) { | ||||
|   pk := *((*[2048]C.uchar)(unsafe.Pointer(&c))) | ||||
|   s.Commands = pk | ||||
| func (s *SynthState) SetPatch(patch []Instrument) error { | ||||
| 	totalVoices := 0 | ||||
| 	commands := make([]Opcode, 0) | ||||
| 	values := make([]byte, 0) | ||||
| 	for _, instr := range patch { | ||||
| 		if len(instr.Units) > 63 { | ||||
| 			return errors.New("An instrument can have a maximum of 63 units") | ||||
| 		} | ||||
| 		if instr.NumVoices < 1 { | ||||
| 			return errors.New("Each instrument must have at least 1 voice") | ||||
| 		} | ||||
| 		for _, unit := range instr.Units { | ||||
| 			commands = append(commands, unit.Command) | ||||
| 			values = append(values, unit.Params...) | ||||
| 		} | ||||
| 		commands = append(commands, Advance) | ||||
| 		totalVoices += instr.NumVoices | ||||
| 	} | ||||
| 	if totalVoices > 32 { | ||||
| 		return errors.New("Sointu does not support more than 32 concurrent voices") | ||||
| 	} | ||||
| 	if len(commands) > 2048 { // TODO: 2048 could probably be pulled automatically from cgo | ||||
| 		return errors.New("The patch would result in more than 2048 commands") | ||||
| 	} | ||||
| 	if len(values) > 16384 { // TODO: 16384 could probably be pulled automatically from cgo | ||||
| 		return errors.New("The patch would result in more than 16384 values") | ||||
| 	} | ||||
| 	cs := (*C.SynthState)(s) | ||||
| 	for i := range commands { | ||||
| 		cs.Commands[i] = (C.uchar)(commands[i]) | ||||
| 	} | ||||
| 	for i := range values { | ||||
| 		cs.Values[i] = (C.uchar)(values[i]) | ||||
| 	} | ||||
| 	cs.NumVoices = C.uint(totalVoices) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (s *SynthState) SetValues(c [16384]byte) { | ||||
|   pk := *((*[16384]C.uchar)(unsafe.Pointer(&c))) | ||||
|   s.Values = pk | ||||
| } | ||||
|  | ||||
| func (s *SynthState) Trigger(voice int,note int) { | ||||
|   fmt.Printf("Calling Trigger...\n") | ||||
|   s.Synth.Voices[voice] = C.Voice{} | ||||
|   s.Synth.Voices[voice].Note = C.int(note) | ||||
|   fmt.Printf("Returning from Trigger...\n") | ||||
| func (s *SynthState) Trigger(voice int, note int) { | ||||
| 	cs := (*C.SynthState)(s) | ||||
| 	cs.Synth.Voices[voice] = C.Voice{} | ||||
| 	cs.Synth.Voices[voice].Note = C.int(note) | ||||
| } | ||||
|  | ||||
| func (s *SynthState) Release(voice int) { | ||||
|   fmt.Printf("Calling Release...\n") | ||||
|   s.Synth.Voices[voice].Release = 1 | ||||
|   fmt.Printf("Returning from Release...\n") | ||||
| 	cs := (*C.SynthState)(s) | ||||
| 	cs.Synth.Voices[voice].Release = 1 | ||||
| } | ||||
|  | ||||
| func NewSynthState() *SynthState { | ||||
|   s := new(SynthState) | ||||
|   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 | ||||
| 	s := new(SynthState) | ||||
| 	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 | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user