-
Notifications
You must be signed in to change notification settings - Fork 0
/
player.go
148 lines (130 loc) · 3.88 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
140
141
142
143
144
145
146
147
148
package main
import (
"sync"
"github.com/faiface/beep/effects"
"github.com/faiface/beep/speaker"
"github.com/faiface/beep"
)
type player struct {
filename string //the filename of the corresponding file in the poolDir
ctrl *beep.Ctrl
vol *effects.Volume
mtx sync.Mutex
//stopCallback func(*player) //this callback gets invoked, if the file is at the end, or stop() was used
loop bool
sampleRate uint //sampleRate in Hz
seaker beep.StreamSeekCloser
onChange func(*player) //change callback with the new values as parameter. WITHOUT loop currently
}
func newPlayer(filename string) (play *player, err error) {
play = &player{filename: filename}
sampled, s, rate, err := newPlayerInternal(filename)
if err != nil {
return
}
play.sampleRate = rate
play.seaker = s
//create a ctrl so the player can pause and resume the playback. Start in paused mode
play.ctrl = &beep.Ctrl{Streamer: sampled, Paused: true}
//create volume control:
play.vol = &effects.Volume{
Streamer: play.ctrl,
Base: 2,
}
//create the "EndStreamer" that is used for the callback at the end of the Stream of the file
playInternal(play)
return
}
//newPlayerInternal does cresate a stremaer with resampler from the filename.
//Does not add this to speaker.Play!
func newPlayerInternal(filename string) (stream beep.Streamer, s beep.StreamSeekCloser, rate uint, err error) {
s, format, err := decode(filename)
if err != nil {
return
}
rate = uint(int(format.SampleRate))
//use Resampler for the speaker and the given file, so we always use the correct sampleRate
stream = beep.Resample(3, format.SampleRate, sampleRate, s)
return
}
//playInternal invokes the speaker.Play method with the player
func playInternal(play *player) {
speaker.Play(beep.Seq(play.vol, beep.Callback(func() {
go func() {
play.rewindInternal()
playInternal(play) // because when we are in this callback, play is not in speaker.Play anymore
if play.loop {
play.ctrl.Paused = false
} else {
play.ctrl.Paused = true
}
}()
})))
}
//callOnChnage is a small helper function for internal use!
func (p *player) callOnChange() {
if p.onChange != nil {
p.onChange(p)
}
}
//pause pauses the running playback. If its already paused, nothing happens
func (p *player) pause() {
p.mtx.Lock()
defer p.mtx.Unlock()
p.ctrl.Paused = true
go p.callOnChange()
}
//resume starts playback of a paused player. If the player is already running, nothing happens.
//Note: this does not start a player that has reached its end again. Use stop() instead.
func (p *player) resume() {
p.mtx.Lock()
defer p.mtx.Unlock()
p.ctrl.Paused = false
go p.callOnChange()
}
//stop pauses a player and resets to the start of the file.
//Note: this is non-blocking, so it returns when the player is not ready to play.
//A call to player.resume() directly after this won't work.
func (p *player) stop() {
//because we are calling internal functions we need no mutex lock
p.pause()
p.rewindInternal()
}
//rewindInternal is an internal function.
func (p *player) rewindInternal() {
p.mtx.Lock()
defer p.mtx.Unlock()
p.seaker.Seek(0)
go p.callOnChange()
}
//drain removes the player from speaker.Play and closes the file
func (p *player) drain() {
p.mtx.Lock()
defer p.mtx.Unlock()
p.ctrl.Streamer = nil //implicit remove from speaker.Play
p.seaker.Close()
}
func (p *player) isPaused() bool {
p.mtx.Lock()
defer p.mtx.Unlock()
return p.ctrl.Paused
}
//setVloume sets the volume of the player. Values greater than 0 will increase
//and values less than 0 will decrease. Range can be: [-7;3]
func (p *player) setVolume(volume float64) {
p.mtx.Lock()
defer p.mtx.Unlock()
p.vol.Volume = volume
go p.callOnChange()
}
func (p *player) volume() float64 {
p.mtx.Lock()
defer p.mtx.Unlock()
return p.vol.Volume
}
func (p *player) currentSample() int {
return p.seaker.Position()
}
func (p *player) maxSample() int {
return p.seaker.Len()
}