/
audio.go
160 lines (133 loc) · 3.44 KB
/
audio.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
149
150
151
152
153
154
155
156
157
158
159
160
// © 2013 the Ui Authors under the MIT license. See AUTHORS for the list of authors.
package ui
/*
#include "ui.h"
extern void audioCallback(void *, Uint8 *, int);
void setCallback(SDL_AudioSpec *s) {
s->callback = audioCallback;
}
*/
import "C"
import (
"errors"
"unsafe"
)
var (
// OpenedSpec is the audio spec describing the format currently opened for playing.
openedSpec C.SDL_AudioSpec
// Sounds caches the data for all played sounds.
sounds = map[string]*audioData{}
// Playing is a slice of all currently-playing sounds.
playing []*Sound
)
func initAudio() {
var want C.SDL_AudioSpec
want.freq = 44100
want.format = C.AUDIO_S16LSB
want.samples = 8096
C.setCallback(&want)
C.SDL_OpenAudio(&want, &openedSpec)
C.SDL_PauseAudio(0)
}
// PlayWAV plays the sound from a wav file and returns a Sound for it.
func PlayWAV(path string, repeat bool) *Sound {
data, ok := sounds[path]
if !ok {
var err error
if data, err = loadWAV(path); err != nil {
panic(err)
}
sounds[path] = data
}
C.SDL_LockAudio()
defer C.SDL_UnlockAudio()
s := &Sound{audioData: data, repeat: repeat}
playing = append(playing, s)
return s
}
// A Sound is a stream of currently playing audio.
type Sound struct {
*audioData
pos uintptr
repeat bool
}
// Stop stops the sound from playing.
func (s *Sound) Stop() {
C.SDL_LockAudio()
defer C.SDL_UnlockAudio()
s.repeat = false
s.pos = 0
}
func (s *Sound) done() bool {
return (!s.repeat && s.pos >= s.len)
}
func (s *Sound) mix(stream *C.Uint8, sz C.int) {
vol := C.int(C.SDL_MIX_MAXVOLUME)
left := uintptr(sz)
dst := uintptr(unsafe.Pointer(stream))
for left > 0 {
data := uintptr(s.data) + uintptr(s.pos)
n := uintptr(left)
if s.len-s.pos < n {
n = s.len - s.pos
}
C.SDL_MixAudio((*C.Uint8)(unsafe.Pointer(dst)), (*C.Uint8)(unsafe.Pointer(data)), C.Uint32(n), vol)
left -= n
s.pos += n
dst += uintptr(n)
if !s.repeat {
break
} else if s.pos >= s.len {
s.pos = 0
}
}
}
// AudioData is sound data converted to the format of the opened audio spec.
type audioData struct {
path string
data unsafe.Pointer
len uintptr
}
// Rb is a string of C.fopen flags (read and binary) for C.SDL_RWFromFile.
var rb = C.CString("rb")
func loadWAV(path string) (*audioData, error) {
cpath := C.CString(path)
defer C.free(unsafe.Pointer(cpath))
var data *C.Uint8
var len C.Uint32
var s C.SDL_AudioSpec // Apparently just a buffer? SDL just zeroes it, fills it, and returns it.
spec := C.SDL_LoadWAV_RW(C.SDL_RWFromFile(cpath, rb), 1, &s, &data, &len)
if spec == nil {
return nil, sdlError()
}
var err error
data, len, err = convert(spec, data, len)
if err != nil {
return nil, err
}
return &audioData{
path: path,
data: unsafe.Pointer(data),
len: uintptr(len),
}, nil
}
// Convert converts audio to the opened audio spec, returning the data and it's length.
func convert(s *C.SDL_AudioSpec, data *C.Uint8, len C.Uint32) (*C.Uint8, C.Uint32, error) {
var cvt C.SDL_AudioCVT
o := openedSpec
switch C.SDL_BuildAudioCVT(&cvt, s.format, s.channels, s.freq, o.format, o.channels, o.freq) {
case -1:
return nil, 0, errors.New("Cannot convert audio")
case 0:
return data, len, nil
}
buf := C.malloc(C.size_t(len) * C.size_t(cvt.len_mult))
cvt.buf = (*C.Uint8)(buf)
cvt.len = C.int(len)
C.memcpy(buf, unsafe.Pointer(data), C.size_t(len))
C.free(unsafe.Pointer(data))
if C.SDL_ConvertAudio(&cvt) < 0 {
return nil, 0, sdlError()
}
return cvt.buf, C.Uint32(cvt.len), nil
}