-
Notifications
You must be signed in to change notification settings - Fork 3
/
player.go
139 lines (118 loc) · 3.19 KB
/
player.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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package fluidsynth2
// #cgo pkg-config: fluidsynth
// #include <fluidsynth.h>
// #include <stdlib.h>
import "C"
import (
"fmt"
"unsafe"
)
type Player struct {
ptr *C.fluid_player_t
open bool
}
func NewPlayer(synth Synth) Player {
return Player{
ptr: C.new_fluid_player(synth.ptr),
open: true,
}
}
// Close deletes the fluid player
func (p *Player) Close() {
if p.open {
C.delete_fluid_player(p.ptr)
p.open = false
}
}
// Add plays files from disk
func (p *Player) Add(filename string) error {
if !p.open {
return fmt.Errorf("player is closed")
}
cpath := C.CString(filename)
defer C.free(unsafe.Pointer(cpath))
if status := C.fluid_player_add(p.ptr, cpath); status == C.FLUID_FAILED {
return fmt.Errorf("failed to add file to player: %s", filename)
}
return nil
}
// AddMem plays back MIDI data from a byte slice.
func (p *Player) AddMem(data []byte) error {
if !p.open {
return fmt.Errorf("player is closed")
}
if len(data) == 0 {
return fmt.Errorf("empty MIDI data")
}
cb := C.CBytes(data)
defer C.free(unsafe.Pointer(cb))
return fluidStatus(C.fluid_player_add_mem(p.ptr, cb, C.size_t(len(data))))
}
func (p *Player) Play() error {
return fluidStatus(C.fluid_player_play(p.ptr))
}
func (p *Player) Stop() {
C.fluid_player_stop(p.ptr)
}
// SetLoop enables the MIDI player to loop the playlist. -1 means loop infinitely
func (p *Player) SetLoop(loops int) {
C.fluid_player_set_loop(p.ptr, C.int(loops))
}
func (p *Player) Seek(ticks int) error {
return fluidStatus(C.fluid_player_seek(p.ptr, C.int(ticks)))
}
// Join blocks until playback has finished
func (p *Player) Join() {
C.fluid_player_join(p.ptr)
}
// GetBPM returns the beats per minute of the MIDI player
func (p *Player) GetBPM() int {
return int(C.fluid_player_get_bpm(p.ptr))
}
// GetTempo returns the tempo of the MIDI player (in microseconds per quarter note)
func (p *Player) GetTempo() int {
return int(C.fluid_player_get_midi_tempo(p.ptr))
}
type TempoType int
const (
TEMPO_INTERNAL = 0
TEMPO_EXTERNAL_BPM = 1
TEMPO_EXTERNAL_MIDI = 2
)
// SetTempo sets the tempo of the MIDI player (in microseconds per quarter note)
func (p *Player) SetTempo(t TempoType, bpm float64) error {
if !p.open {
return fmt.Errorf("player is closed")
}
if t < TEMPO_INTERNAL || t > TEMPO_EXTERNAL_MIDI {
return fmt.Errorf("invalid tempo type: %d", t)
}
C.fluid_player_set_tempo(p.ptr, C.int(t), C.double(bpm))
return nil
}
// GetCurrentTick returns the number of tempo ticks passed
func (p *Player) GetCurrentTick() int {
return int(C.fluid_player_get_current_tick(p.ptr))
}
// GetTotalTicks returns the total tick count of the sequence
func (p *Player) GetTotalTicks() int {
return int(C.fluid_player_get_total_ticks(p.ptr))
}
// GetStatus returns the current status of the player
func (p *Player) GetStatus() (string, error) {
if !p.open {
return "", fmt.Errorf("player is closed")
}
status := int(C.fluid_player_get_status(p.ptr))
//Codes documented here http://www.fluidsynth.org/api/midi_8h.html#a5ec93766f61465dedbbac9bdb76ced83
switch status {
case 0:
return "READY", nil
case 1:
return "PLAYING", nil
case 2:
return "DONE", nil
default:
return "UNKNOWN", fmt.Errorf("unknown status code: %d", status)
}
}