175 lines
4.8 KiB
Go
175 lines
4.8 KiB
Go
|
// Copyright 2021 The Oto Authors
|
||
|
//
|
||
|
// 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.
|
||
|
|
||
|
package oto
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"runtime"
|
||
|
"unsafe"
|
||
|
|
||
|
"golang.org/x/sys/windows"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
winmm = windows.NewLazySystemDLL("winmm")
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
procWaveOutOpen = winmm.NewProc("waveOutOpen")
|
||
|
procWaveOutClose = winmm.NewProc("waveOutClose")
|
||
|
procWaveOutPrepareHeader = winmm.NewProc("waveOutPrepareHeader")
|
||
|
procWaveOutUnprepareHeader = winmm.NewProc("waveOutUnprepareHeader")
|
||
|
procWaveOutWrite = winmm.NewProc("waveOutWrite")
|
||
|
)
|
||
|
|
||
|
type _WAVEHDR struct {
|
||
|
lpData uintptr
|
||
|
dwBufferLength uint32
|
||
|
dwBytesRecorded uint32
|
||
|
dwUser uintptr
|
||
|
dwFlags uint32
|
||
|
dwLoops uint32
|
||
|
lpNext uintptr
|
||
|
reserved uintptr
|
||
|
}
|
||
|
|
||
|
type _WAVEFORMATEX struct {
|
||
|
wFormatTag uint16
|
||
|
nChannels uint16
|
||
|
nSamplesPerSec uint32
|
||
|
nAvgBytesPerSec uint32
|
||
|
nBlockAlign uint16
|
||
|
wBitsPerSample uint16
|
||
|
cbSize uint16
|
||
|
}
|
||
|
|
||
|
const (
|
||
|
_WAVE_FORMAT_IEEE_FLOAT = 3
|
||
|
_WHDR_INQUEUE = 16
|
||
|
)
|
||
|
|
||
|
type _MMRESULT uint
|
||
|
|
||
|
const (
|
||
|
_MMSYSERR_NOERROR _MMRESULT = 0
|
||
|
_MMSYSERR_ERROR _MMRESULT = 1
|
||
|
_MMSYSERR_BADDEVICEID _MMRESULT = 2
|
||
|
_MMSYSERR_ALLOCATED _MMRESULT = 4
|
||
|
_MMSYSERR_INVALIDHANDLE _MMRESULT = 5
|
||
|
_MMSYSERR_NODRIVER _MMRESULT = 6
|
||
|
_MMSYSERR_NOMEM _MMRESULT = 7
|
||
|
_WAVERR_BADFORMAT _MMRESULT = 32
|
||
|
_WAVERR_STILLPLAYING _MMRESULT = 33
|
||
|
_WAVERR_UNPREPARED _MMRESULT = 34
|
||
|
_WAVERR_SYNC _MMRESULT = 35
|
||
|
)
|
||
|
|
||
|
func (m _MMRESULT) Error() string {
|
||
|
switch m {
|
||
|
case _MMSYSERR_NOERROR:
|
||
|
return "MMSYSERR_NOERROR"
|
||
|
case _MMSYSERR_ERROR:
|
||
|
return "MMSYSERR_ERROR"
|
||
|
case _MMSYSERR_BADDEVICEID:
|
||
|
return "MMSYSERR_BADDEVICEID"
|
||
|
case _MMSYSERR_ALLOCATED:
|
||
|
return "MMSYSERR_ALLOCATED"
|
||
|
case _MMSYSERR_INVALIDHANDLE:
|
||
|
return "MMSYSERR_INVALIDHANDLE"
|
||
|
case _MMSYSERR_NODRIVER:
|
||
|
return "MMSYSERR_NODRIVER"
|
||
|
case _MMSYSERR_NOMEM:
|
||
|
return "MMSYSERR_NOMEM"
|
||
|
case _WAVERR_BADFORMAT:
|
||
|
return "WAVERR_BADFORMAT"
|
||
|
case _WAVERR_STILLPLAYING:
|
||
|
return "WAVERR_STILLPLAYING"
|
||
|
case _WAVERR_UNPREPARED:
|
||
|
return "WAVERR_UNPREPARED"
|
||
|
case _WAVERR_SYNC:
|
||
|
return "WAVERR_SYNC"
|
||
|
}
|
||
|
return fmt.Sprintf("MMRESULT (%d)", m)
|
||
|
}
|
||
|
|
||
|
func waveOutOpen(f *_WAVEFORMATEX, callback uintptr) (uintptr, error) {
|
||
|
const (
|
||
|
waveMapper = 0xffffffff
|
||
|
callbackFunction = 0x30000
|
||
|
)
|
||
|
var w uintptr
|
||
|
var fdwOpen uintptr
|
||
|
if callback != 0 {
|
||
|
fdwOpen |= callbackFunction
|
||
|
}
|
||
|
r, _, e := procWaveOutOpen.Call(uintptr(unsafe.Pointer(&w)), waveMapper, uintptr(unsafe.Pointer(f)),
|
||
|
callback, 0, fdwOpen)
|
||
|
runtime.KeepAlive(f)
|
||
|
if _MMRESULT(r) != _MMSYSERR_NOERROR {
|
||
|
if e != nil && e != windows.ERROR_SUCCESS {
|
||
|
return 0, fmt.Errorf("oto: waveOutOpen failed: %w", e)
|
||
|
}
|
||
|
return 0, fmt.Errorf("oto: waveOutOpen failed: %w", _MMRESULT(r))
|
||
|
}
|
||
|
return w, nil
|
||
|
}
|
||
|
|
||
|
func waveOutClose(hwo uintptr) error {
|
||
|
r, _, e := procWaveOutClose.Call(hwo)
|
||
|
if _MMRESULT(r) != _MMSYSERR_NOERROR {
|
||
|
if e != nil && e != windows.ERROR_SUCCESS {
|
||
|
return fmt.Errorf("oto: waveOutClose failed: %w", e)
|
||
|
}
|
||
|
return fmt.Errorf("oto: waveOutClose failed: %w", _MMRESULT(r))
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func waveOutPrepareHeader(hwo uintptr, pwh *_WAVEHDR) error {
|
||
|
r, _, e := procWaveOutPrepareHeader.Call(hwo, uintptr(unsafe.Pointer(pwh)), unsafe.Sizeof(_WAVEHDR{}))
|
||
|
runtime.KeepAlive(pwh)
|
||
|
if _MMRESULT(r) != _MMSYSERR_NOERROR {
|
||
|
if e != nil && e != windows.ERROR_SUCCESS {
|
||
|
return fmt.Errorf("oto: waveOutPrepareHeader failed: %w", e)
|
||
|
}
|
||
|
return fmt.Errorf("oto: waveOutPrepareHeader failed: %w", _MMRESULT(r))
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func waveOutUnprepareHeader(hwo uintptr, pwh *_WAVEHDR) error {
|
||
|
r, _, e := procWaveOutUnprepareHeader.Call(hwo, uintptr(unsafe.Pointer(pwh)), unsafe.Sizeof(_WAVEHDR{}))
|
||
|
runtime.KeepAlive(pwh)
|
||
|
if _MMRESULT(r) != _MMSYSERR_NOERROR {
|
||
|
if e != nil && e != windows.ERROR_SUCCESS {
|
||
|
return fmt.Errorf("oto: waveOutUnprepareHeader failed: %w", e)
|
||
|
}
|
||
|
return fmt.Errorf("oto: waveOutUnprepareHeader failed: %w", _MMRESULT(r))
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func waveOutWrite(hwo uintptr, pwh *_WAVEHDR) error {
|
||
|
r, _, e := procWaveOutWrite.Call(hwo, uintptr(unsafe.Pointer(pwh)), unsafe.Sizeof(_WAVEHDR{}))
|
||
|
runtime.KeepAlive(pwh)
|
||
|
if _MMRESULT(r) != _MMSYSERR_NOERROR {
|
||
|
if e != nil && e != windows.ERROR_SUCCESS {
|
||
|
return fmt.Errorf("oto: waveOutWrite failed: %w", e)
|
||
|
}
|
||
|
return fmt.Errorf("oto: waveOutWrite failed: %w", _MMRESULT(r))
|
||
|
}
|
||
|
return nil
|
||
|
}
|