forked from ebitengine/oto
-
Notifications
You must be signed in to change notification settings - Fork 0
/
driver_windows.go
140 lines (126 loc) · 3.38 KB
/
driver_windows.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
// Copyright 2015 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build !js
package oto
import (
"errors"
"runtime"
"unsafe"
)
type header struct {
buffer []byte
waveHdr *wavehdr
}
func newHeader(waveOut uintptr, bufferSize int) (*header, error) {
h := &header{
buffer: make([]byte, bufferSize),
}
h.waveHdr = &wavehdr{
lpData: uintptr(unsafe.Pointer(&h.buffer[0])),
dwBufferLength: uint32(bufferSize),
}
if err := waveOutPrepareHeader(waveOut, h.waveHdr); err != nil {
return nil, err
}
return h, nil
}
func (h *header) Write(waveOut uintptr, data []byte) error {
if len(data) != len(h.buffer) {
return errors.New("oto: len(data) must equal to len(h.buffer)")
}
copy(h.buffer, data)
if err := waveOutWrite(waveOut, h.waveHdr); err != nil {
return err
}
return nil
}
type driver struct {
out uintptr
headers []*header
tmp []byte
bufferSize int
}
func newDriver(sampleRate, channelNum, bitDepthInBytes, bufferSizeInBytes int) (tryWriteCloser, error) {
numBlockAlign := channelNum * bitDepthInBytes
f := &waveformatex{
wFormatTag: waveFormatPCM,
nChannels: uint16(channelNum),
nSamplesPerSec: uint32(sampleRate),
nAvgBytesPerSec: uint32(sampleRate * numBlockAlign),
wBitsPerSample: uint16(bitDepthInBytes * 8),
nBlockAlign: uint16(numBlockAlign),
}
w, err := waveOutOpen(f)
const elementNotFound = 1168
if e, ok := err.(*winmmError); ok && e.errno == elementNotFound {
// No device was found. Return the dummy device.
// TODO: Retry to open the device when possible.
return newDummyDriver(sampleRate, channelNum, bitDepthInBytes), nil
}
if err != nil {
return nil, err
}
const numBufs = 2
p := &driver{
out: w,
headers: make([]*header, numBufs),
bufferSize: bufferSizeInBytes,
}
runtime.SetFinalizer(p, (*driver).Close)
for i := range p.headers {
var err error
p.headers[i], err = newHeader(w, p.bufferSize)
if err != nil {
return nil, err
}
}
return p, nil
}
func (p *driver) TryWrite(data []byte) (int, error) {
n := min(len(data), max(0, p.bufferSize-len(p.tmp)))
p.tmp = append(p.tmp, data[:n]...)
if len(p.tmp) < p.bufferSize {
return n, nil
}
var headerToWrite *header
for _, h := range p.headers {
// TODO: Need to check WHDR_DONE?
if h.waveHdr.dwFlags&whdrInqueue == 0 {
headerToWrite = h
break
}
}
if headerToWrite == nil {
return n, nil
}
if err := headerToWrite.Write(p.out, p.tmp); err != nil {
// This error can happen when e.g. a new HDMI connection is detected (#51).
const errorNotFound = 1168
werr := err.(*winmmError)
if werr.fname == "waveOutWrite" && werr.errno == errorNotFound {
return 0, nil
}
return 0, err
}
p.tmp = nil
return n, nil
}
func (p *driver) Close() error {
runtime.SetFinalizer(p, nil)
// TODO: Call waveOutUnprepareHeader here
if err := waveOutClose(p.out); err != nil {
return err
}
return nil
}