OnlineLibrary

Форк
0
196 строк · 3.7 Кб
1
package player
2

3
import (
4
	"bufio"
5
	"fmt"
6
	"io"
7
	"sync"
8
	"sync/atomic"
9
	"time"
10

11
	"OnlineLibrary/internal/minimp3"
12
	"OnlineLibrary/internal/sonic"
13
	"OnlineLibrary/internal/util/syncio"
14
	"OnlineLibrary/internal/waveout"
15
)
16

17
type Fragment struct {
18
	sync.Mutex
19
	paused         bool
20
	stream         *sonic.Stream
21
	dec            *minimp3.Decoder
22
	pcmBytesPerSec int
23
	wpBufSize      int
24
	Bitrate        int
25
	wp             *waveout.WavePlayer
26
	willBeStopped  bool
27
	pos            time.Duration
28
}
29

30
const BufferDuration = time.Millisecond * 400
31

32
func NewFragment(src io.ReadSeeker, devName string) (*Fragment, error) {
33
	dec := minimp3.NewDecoder(src)
34
	// Reading into an empty buffer will fill the internal buffer of the decoder, so you can get the audio data parameters
35
	if _, err := dec.Read(nil); err != nil {
36
		return nil, err
37
	}
38

39
	sampleRate := dec.SampleRate()
40
	channels := dec.Channels()
41
	bitrate := dec.Bitrate()
42
	pcmBytesPerSec := sampleRate * channels * 2
43

44
	if pcmBytesPerSec == 0 || bitrate == 0 {
45
		return nil, fmt.Errorf("invalid mp3")
46
	}
47

48
	wpBufSize := int(time.Duration(pcmBytesPerSec) * BufferDuration / time.Second)
49
	wp, err := waveout.NewWavePlayer(channels, sampleRate, 16, wpBufSize, devName)
50
	if err != nil {
51
		return nil, err
52
	}
53

54
	f := &Fragment{
55
		pcmBytesPerSec: pcmBytesPerSec,
56
		wpBufSize:      wpBufSize,
57
		Bitrate:        bitrate,
58
		stream:         sonic.NewStream(sampleRate, channels),
59
		dec:            dec,
60
		wp:             wp,
61
	}
62

63
	return f, nil
64
}
65

66
func (f *Fragment) play(playing *atomic.Bool, elapsedTimeCallback func(time.Duration)) error {
67
	var p time.Duration
68
	wp := bufio.NewWriterSize(f.wp, f.wpBufSize)
69
	stream := syncio.NewReadWriter(f.stream, f)
70
	dec := syncio.NewReader(f.dec, f)
71

72
	for playing.Load() {
73
		elapsedTimeCallback(f.Position())
74
		_, err := io.CopyN(stream, dec, int64(f.wpBufSize))
75
		if err != nil {
76
			if err != io.EOF {
77
				f.wp.Stop()
78
				return fmt.Errorf("copying from mp3 decoder to sonic stream: %w", err)
79
			}
80
			f.Lock()
81
			f.stream.Flush()
82
			f.Unlock()
83
		}
84
		if _, err := wp.ReadFrom(stream); err != nil {
85
			return fmt.Errorf("copying from sonic stream to wave player: %w", err)
86
		}
87
		f.Lock()
88
		if f.willBeStopped {
89
			p = 0
90
			f.willBeStopped = false
91
		} else {
92
			f.pos += p
93
			p = BufferDuration
94
		}
95
		f.Unlock()
96

97
		if err != nil {
98
			// Here err is always io.EOF
99
			wp.Flush()
100
			break
101
		}
102
	}
103

104
	f.wp.Sync()
105
	f.Lock()
106
	f.pos += p
107
	f.Unlock()
108
	elapsedTimeCallback(0)
109
	return nil
110
}
111

112
func (f *Fragment) setSpeed(speed float64) {
113
	f.Lock()
114
	defer f.Unlock()
115
	f.stream.SetSpeed(speed)
116
}
117

118
func (f *Fragment) setVolume(volume float64) {
119
	f.Lock()
120
	defer f.Unlock()
121
	f.stream.SetVolume(volume)
122
}
123

124
func (f *Fragment) SetPosition(pos time.Duration) error {
125
	f.Lock()
126
	defer f.Unlock()
127

128
	if pos < 0 {
129
		// Negative position means the beginning of the fragment
130
		pos = 0
131
	}
132

133
	if pos == f.pos {
134
		// Requested position has already been set
135
		return nil
136
	}
137

138
	f.wp.Stop()
139
	f.willBeStopped = true
140
	f.stream.Flush()
141
	io.ReadAll(f.stream)
142

143
	offset := int64(pos / (time.Second / time.Duration(f.pcmBytesPerSec)))
144
	_, err := f.dec.Seek(offset, io.SeekStart)
145
	if err != nil {
146
		return err
147
	}
148

149
	f.pos = pos
150
	return nil
151
}
152

153
func (f *Fragment) Position() time.Duration {
154
	f.Lock()
155
	defer f.Unlock()
156
	return f.pos
157
}
158

159
func (f *Fragment) pause(pause bool) bool {
160
	f.Lock()
161
	defer f.Unlock()
162

163
	if f.paused == pause {
164
		return false
165
	}
166
	f.paused = pause
167

168
	f.wp.Pause(f.paused)
169
	return true
170
}
171

172
func (f *Fragment) IsPause() bool {
173
	f.Lock()
174
	defer f.Unlock()
175
	return f.paused
176
}
177

178
func (f *Fragment) SetOutputDevice(devName string) error {
179
	return f.wp.SetOutputDevice(devName)
180
}
181

182
func (f *Fragment) stop() {
183
	f.wp.Stop()
184
}
185

186
func (f *Fragment) Close() error {
187
	f.Lock()
188
	defer f.Unlock()
189
	f.wp.Stop()
190
	err := f.wp.Close()
191
	f.stream.Flush()
192
	io.ReadAll(f.stream)
193
	f.stream = nil
194
	f.dec = nil
195
	return err
196
}
197

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.