-
Notifications
You must be signed in to change notification settings - Fork 3
/
synth.go
123 lines (104 loc) · 3.62 KB
/
synth.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package fluidsynth2
// #cgo pkg-config: fluidsynth
// #include <fluidsynth.h>
// #include <stdlib.h>
import "C"
import (
"fmt"
"unsafe"
)
type Synth struct {
ptr *C.fluid_synth_t
}
func NewSynth(settings Settings) Synth {
return Synth{C.new_fluid_synth(settings.ptr)}
}
func (s *Synth) Close() {
C.delete_fluid_synth(s.ptr)
}
func (s *Synth) SFLoad(path string, resetPresets bool) (int, error) {
cpath := C.CString(path)
defer C.free(unsafe.Pointer(cpath))
creset := cbool(resetPresets)
cfont_id := C.fluid_synth_sfload(s.ptr, cpath, creset)
if cfont_id == C.FLUID_FAILED {
return 0, fmt.Errorf("could not load soundfont: %s", path)
}
return int(cfont_id), nil
}
func (s *Synth) SFReload(sfid int) (int, error) {
cfont_id := C.fluid_synth_sfreload(s.ptr, C.int(sfid))
if cfont_id == C.FLUID_FAILED {
return 0, fmt.Errorf("could not reload soundfont with ID: %d", sfid)
}
return int(cfont_id), nil
}
func (s *Synth) SFUnload(sfid int, reset bool) error {
status := C.fluid_synth_sfunload(s.ptr, C.int(sfid), cbool(reset))
if status == C.FLUID_FAILED {
return fmt.Errorf("could not unload soundfont with ID: %d", sfid)
}
return nil
}
func (s *Synth) NoteOn(channel, note, velocity uint8) error {
result := C.fluid_synth_noteon(s.ptr, C.int(channel), C.int(note), C.int(velocity))
if result == C.FLUID_FAILED {
return fmt.Errorf("failed to turn note on: channel=%d, note=%d, velocity=%d", channel, note, velocity)
}
return nil
}
func (s *Synth) NoteOff(channel, note uint8) {
C.fluid_synth_noteoff(s.ptr, C.int(channel), C.int(note))
}
func (s *Synth) ProgramChange(channel, program uint8) {
C.fluid_synth_program_change(s.ptr, C.int(channel), C.int(program))
}
func (s *Synth) GetGain() float32 {
return float32(C.fluid_synth_get_gain(s.ptr))
}
func (s *Synth) SetGain(g float32) {
C.fluid_synth_set_gain(s.ptr, C.float(g))
}
/*
WriteS16 synthesizes signed 16-bit samples. It will fill as much of the provided
slices as it can without overflowing 'left' or 'right'. For interleaved stereo, have both
'left' and 'right' share a backing array and use lstride = rstride = 2. ie:
synth.WriteS16(samples, samples[1:], 2, 2)
*/
func (s *Synth) WriteS16(left, right []int16, lstride, rstride int) error {
nframes := (len(left) + lstride - 1) / lstride
rframes := (len(right) + rstride - 1) / rstride
if rframes < nframes {
nframes = rframes
}
if nframes == 0 {
return fmt.Errorf("no frames to write")
}
C.fluid_synth_write_s16(s.ptr, C.int(nframes), unsafe.Pointer(&left[0]), 0, C.int(lstride), unsafe.Pointer(&right[0]), 0, C.int(rstride))
return nil
}
func (s *Synth) WriteFloat(left, right []float32, lstride, rstride int) error {
nframes := (len(left) + lstride - 1) / lstride
rframes := (len(right) + rstride - 1) / rstride
if rframes < nframes {
nframes = rframes
}
if nframes == 0 {
return fmt.Errorf("no frames to write")
}
C.fluid_synth_write_float(s.ptr, C.int(nframes), unsafe.Pointer(&left[0]), 0, C.int(lstride), unsafe.Pointer(&right[0]), 0, C.int(rstride))
return nil
}
type TuningId struct {
Bank, Program uint8
}
/* ActivateKeyTuning creates/modifies a specific tuning bank/program */
func (s *Synth) ActivateKeyTuning(id TuningId, name string, tuning [128]float64, apply bool) {
n := C.CString(name)
defer C.free(unsafe.Pointer(n))
C.fluid_synth_activate_key_tuning(s.ptr, C.int(id.Bank), C.int(id.Program), n, (*C.double)(&tuning[0]), cbool(apply))
}
/* ActivateTuning switches a midi channel onto the specified tuning bank/program */
func (s *Synth) ActivateTuning(channel uint8, id TuningId, apply bool) {
C.fluid_synth_activate_tuning(s.ptr, C.int(channel), C.int(id.Bank), C.int(id.Program), cbool(apply))
}