├── README.md ├── example ├── 48hz16bit2ch.wav └── main.go ├── dsound ├── guid.go ├── idirectsoundnotify.go ├── iunknown.go ├── ds.go ├── idirectsound.go ├── ds_test.go ├── dsresult.go ├── waveformatex.go └── idirectsoundbuffer.go ├── .gitignore └── LICENSE /README.md: -------------------------------------------------------------------------------- 1 | directsound-go 2 | ============== 3 | 4 | DirectSound wrapper without cgo 5 | -------------------------------------------------------------------------------- /example/48hz16bit2ch.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oov/directsound-go/HEAD/example/48hz16bit2ch.wav -------------------------------------------------------------------------------- /dsound/guid.go: -------------------------------------------------------------------------------- 1 | package dsound 2 | 3 | type GUID struct { 4 | Data1 uint32 5 | Data2 uint16 6 | Data3 uint16 7 | Data4 [8]byte 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Masanobu YOSHIOKA 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /dsound/idirectsoundnotify.go: -------------------------------------------------------------------------------- 1 | package dsound 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | ) 7 | 8 | type IDirectSoundNotify struct { 9 | v *iDirectSoundNotifyVTable 10 | } 11 | 12 | type iDirectSoundNotifyVTable struct { 13 | iUnknownVTable 14 | SetNotificationPositions comProc 15 | } 16 | 17 | func (dsn *IDirectSoundNotify) QueryInterface(iid *GUID) (*IUnknown, error) { 18 | return (*IUnknown)(unsafe.Pointer(dsn)).QueryInterface(iid) 19 | } 20 | 21 | func (dsn *IDirectSoundNotify) AddRef() uint32 { 22 | return (*IUnknown)(unsafe.Pointer(dsn)).AddRef() 23 | } 24 | 25 | func (dsn *IDirectSoundNotify) Release() uint32 { 26 | return (*IUnknown)(unsafe.Pointer(dsn)).Release() 27 | } 28 | 29 | const ( 30 | DSBNOTIFICATIONS_MAX = 0x00100000 31 | DSBPN_OFFSETSTOP = 0xFFFFFFFF 32 | ) 33 | 34 | type DSBPOSITIONNOTIFY struct { 35 | Offset uint32 36 | EventNotify syscall.Handle 37 | } 38 | 39 | func (dsn *IDirectSoundNotify) SetNotificationPositions(positionNotifies []DSBPOSITIONNOTIFY) error { 40 | return dsResult(dsn.v.SetNotificationPositions.Call( 41 | uintptr(unsafe.Pointer(dsn)), 42 | uintptr(len(positionNotifies)), 43 | uintptr(unsafe.Pointer(&positionNotifies[0])), 44 | )) 45 | } 46 | -------------------------------------------------------------------------------- /dsound/iunknown.go: -------------------------------------------------------------------------------- 1 | package dsound 2 | 3 | import ( 4 | "strconv" 5 | "unsafe" 6 | ) 7 | 8 | type HResult uintptr 9 | 10 | const ( 11 | S_OK = HResult(0x00000000) 12 | E_NOTIMPL = HResult(0x80004001) 13 | E_NOINTERFACE = HResult(0x80004002) 14 | E_POINTER = HResult(0x80004003) 15 | E_ABORT = HResult(0x80004004) 16 | E_FAIL = HResult(0x80004005) 17 | E_UNEXPECTED = HResult(0x8000FFFF) 18 | E_ACCESSDENIED = HResult(0x80070005) 19 | E_HANDLE = HResult(0x80070006) 20 | E_OUTOFMEMORY = HResult(0x8007000E) 21 | E_INVALIDARG = HResult(0x80070057) 22 | ) 23 | 24 | func (hr HResult) Error() string { 25 | switch hr { 26 | case S_OK: 27 | return "Operation successful" 28 | case E_NOTIMPL: 29 | return "Not implemented" 30 | case E_NOINTERFACE: 31 | return "No such interface supported" 32 | case E_POINTER: 33 | return "Pointer that is not valid" 34 | case E_ABORT: 35 | return "Operation aborted" 36 | case E_FAIL: 37 | return "Unspecified failure" 38 | case E_UNEXPECTED: 39 | return "Unexpected failure" 40 | case E_ACCESSDENIED: 41 | return "General access denied error" 42 | case E_HANDLE: 43 | return "Handle that is not valid" 44 | case E_OUTOFMEMORY: 45 | return "Failed to allocate necessary memory" 46 | case E_INVALIDARG: 47 | return "One or more arguments are not valid" 48 | } 49 | return "Unknown HRESULT value: " + strconv.FormatUint(uint64(hr), 10) 50 | } 51 | 52 | type IUnknown struct { 53 | v *iUnknownVTable 54 | } 55 | 56 | type iUnknownVTable struct { 57 | QueryInterface comProc 58 | AddRef comProc 59 | Release comProc 60 | } 61 | 62 | func (unk *IUnknown) QueryInterface(iid *GUID) (*IUnknown, error) { 63 | var intf *IUnknown 64 | r, _, _ := unk.v.QueryInterface.Call( 65 | uintptr(unsafe.Pointer(unk)), 66 | uintptr(unsafe.Pointer(iid)), 67 | uintptr(unsafe.Pointer(&intf)), 68 | ) 69 | if r != 0 { 70 | return nil, HResult(r) 71 | } 72 | return intf, nil 73 | } 74 | 75 | func (unk *IUnknown) AddRef() uint32 { 76 | r, _, _ := unk.v.AddRef.Call( 77 | uintptr(unsafe.Pointer(unk)), 78 | ) 79 | return uint32(r) 80 | } 81 | 82 | func (unk *IUnknown) Release() uint32 { 83 | r, _, _ := unk.v.Release.Call( 84 | uintptr(unsafe.Pointer(unk)), 85 | ) 86 | return uint32(r) 87 | } 88 | -------------------------------------------------------------------------------- /dsound/ds.go: -------------------------------------------------------------------------------- 1 | package dsound 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | ) 7 | 8 | var ( 9 | dsoundDLL = syscall.NewLazyDLL("dsound") 10 | procDirectSoundEnumerate = dsoundDLL.NewProc("DirectSoundEnumerateW") 11 | procDirectSoundCaptureEnumerate = dsoundDLL.NewProc("DirectSoundCaptureEnumerateW") 12 | procDirectSoundCreate = dsoundDLL.NewProc("DirectSoundCreate") 13 | //procDirectSoundCreate8 = dsoundDLL.NewProc("DirectSoundCreate8") 14 | //procDirectSoundCaptureCreate = dsoundDLL.NewProc("DirectSoundCaptureCreate") 15 | //procDirectSoundCaptureCreate8 = dsoundDLL.NewProc("DirectSoundCaptureCreate8") 16 | ) 17 | 18 | func DirectSoundEnumerate(dsEnumCallback func(guid *GUID, description string, module string) bool) error { 19 | return dllDSResult(procDirectSoundEnumerate.Call(syscall.NewCallback(func(guid *GUID, description *[maxInt >> 1]uint16, module *[maxInt >> 1]uint16, context uintptr) int { 20 | b := dsEnumCallback( 21 | guid, 22 | syscall.UTF16ToString(description[:]), 23 | syscall.UTF16ToString(module[:]), 24 | ) 25 | if b { 26 | return 1 27 | } 28 | return 0 29 | }), 0)) 30 | } 31 | 32 | func DirectSoundCaptureEnumerate(dsEnumCallback func(guid *GUID, description string, module string) bool) error { 33 | return dllDSResult(procDirectSoundCaptureEnumerate.Call(syscall.NewCallback(func(guid *GUID, description *[maxInt >> 1]uint16, module *[maxInt >> 1]uint16, context uintptr) int { 34 | b := dsEnumCallback( 35 | guid, 36 | syscall.UTF16ToString(description[:]), 37 | syscall.UTF16ToString(module[:]), 38 | ) 39 | if b { 40 | return 1 41 | } 42 | return 0 43 | }), 0)) 44 | } 45 | 46 | func DirectSoundCreate(guid *GUID) (*IDirectSound, error) { 47 | var ds *IDirectSound 48 | err := dllDSResult(procDirectSoundCreate.Call( 49 | uintptr(unsafe.Pointer(guid)), 50 | uintptr(unsafe.Pointer(&ds)), 51 | 0, 52 | )) 53 | if err != nil { 54 | return nil, err 55 | } 56 | return ds, nil 57 | } 58 | 59 | /* 60 | func DirectSoundCreate8(guid *GUID) (*IDirectSound8, error) { 61 | var ds *IDirectSound8 62 | err := dllDSResult(procDirectSoundCreate8.Call( 63 | uintptr(unsafe.Pointer(guid)), 64 | uintptr(unsafe.Pointer(&ds)), 65 | 0, 66 | )) 67 | if err != nil { 68 | return nil, err 69 | } 70 | return ds, nil 71 | } 72 | 73 | func DirectSoundCaptureCreate(guid *GUID) (*DirectSoundCapture, error) { 74 | return nil, nil 75 | } 76 | 77 | func DirectSoundCaptureCreate8(guid *GUID) (*IDirectSoundCapture8, error) { 78 | return nil, nil 79 | } 80 | 81 | func DirectSoundFullDuplexCreate8(){ 82 | } 83 | 84 | func GetDeviceID(){ 85 | } 86 | */ 87 | -------------------------------------------------------------------------------- /dsound/idirectsound.go: -------------------------------------------------------------------------------- 1 | package dsound 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | ) 7 | 8 | type IDirectSound struct { 9 | v *iDirectSoundVTable 10 | } 11 | 12 | type iDirectSoundVTable struct { 13 | iUnknownVTable 14 | CreateSoundBuffer comProc 15 | GetCaps comProc 16 | DuplicateSoundBuffer comProc 17 | SetCooperativeLevel comProc 18 | Compact comProc 19 | GetSpeakerConfig comProc 20 | SetSpeakerConfig comProc 21 | Initialize comProc 22 | } 23 | 24 | func (ds *IDirectSound) QueryInterface(iid *GUID) (*IUnknown, error) { 25 | return (*IUnknown)(unsafe.Pointer(ds)).QueryInterface(iid) 26 | } 27 | 28 | func (ds *IDirectSound) AddRef() uint32 { 29 | return (*IUnknown)(unsafe.Pointer(ds)).AddRef() 30 | } 31 | 32 | func (ds *IDirectSound) Release() uint32 { 33 | return (*IUnknown)(unsafe.Pointer(ds)).Release() 34 | } 35 | 36 | type SCL uint32 37 | 38 | const ( 39 | DSSCL_NORMAL = SCL(0x00000001) 40 | DSSCL_PRIORITY = SCL(0x00000002) 41 | DSSCL_EXCLUSIVE = SCL(0x00000003) 42 | DSSCL_WRITEPRIMARY = SCL(0x00000004) 43 | ) 44 | 45 | func (ds *IDirectSound) SetCooperativeLevel(window syscall.Handle, level SCL) error { 46 | return dsResult(ds.v.SetCooperativeLevel.Call( 47 | uintptr(unsafe.Pointer(ds)), 48 | uintptr(window), 49 | uintptr(level), 50 | )) 51 | } 52 | 53 | type Caps struct { 54 | size uint32 55 | Flags uint32 56 | MinSecondarySampleRate uint32 57 | MaxSecondarySampleRate uint32 58 | PrimaryBuffers uint32 59 | MaxHwMixingAllBuffers uint32 60 | MaxHwMixingStaticBuffers uint32 61 | MaxHwMixingStreamingBuffers uint32 62 | FreeHwMixingAllBuffers uint32 63 | FreeHwMixingStaticBuffers uint32 64 | FreeHwMixingStreamingBuffers uint32 65 | MaxHw3DAllBuffers uint32 66 | MaxHw3DStaticBuffers uint32 67 | MaxHw3DStreamingBuffers uint32 68 | FreeHw3DAllBuffers uint32 69 | FreeHw3DStaticBuffers uint32 70 | FreeHw3DStreamingBuffers uint32 71 | TotalHwMemBytes uint32 72 | FreeHwMemBytes uint32 73 | MaxContigFreeHwMemBytes uint32 74 | UnlockTransferRateHwBuffers uint32 75 | PlayCpuOverheadSwBuffers uint32 76 | Reserved1 uint32 77 | Reserved2 uint32 78 | } 79 | 80 | func (ds *IDirectSound) GetCaps() (*Caps, error) { 81 | var c Caps 82 | c.size = uint32(unsafe.Sizeof(c)) 83 | err := dsResult(ds.v.GetCaps.Call( 84 | uintptr(unsafe.Pointer(ds)), 85 | uintptr(unsafe.Pointer(&c)), 86 | )) 87 | if err != nil { 88 | return nil, err 89 | } 90 | return &c, nil 91 | } 92 | 93 | type BufferDesc struct { 94 | size uint32 95 | Flags BufferCapsFlag 96 | BufferBytes uint32 97 | Reserved uint32 98 | Format *WaveFormatEx 99 | GUID3DAlgorithm GUID 100 | } 101 | 102 | type BufferCapsFlag uint32 103 | 104 | const ( 105 | DSBCAPS_PRIMARYBUFFER = BufferCapsFlag(0x00000001) 106 | DSBCAPS_STATIC = BufferCapsFlag(0x00000002) 107 | DSBCAPS_LOCHARDWARE = BufferCapsFlag(0x00000004) 108 | DSBCAPS_LOCSOFTWARE = BufferCapsFlag(0x00000008) 109 | DSBCAPS_CTRL3D = BufferCapsFlag(0x00000010) 110 | DSBCAPS_CTRLFREQUENCY = BufferCapsFlag(0x00000020) 111 | DSBCAPS_CTRLPAN = BufferCapsFlag(0x00000040) 112 | DSBCAPS_CTRLVOLUME = BufferCapsFlag(0x00000080) 113 | DSBCAPS_CTRLPOSITIONNOTIFY = BufferCapsFlag(0x00000100) 114 | DSBCAPS_CTRLFX = BufferCapsFlag(0x00000200) 115 | DSBCAPS_STICKYFOCUS = BufferCapsFlag(0x00004000) 116 | DSBCAPS_GLOBALFOCUS = BufferCapsFlag(0x00008000) 117 | DSBCAPS_GETCURRENTPOSITION2 = BufferCapsFlag(0x00010000) 118 | DSBCAPS_MUTE3DATMAXDISTANCE = BufferCapsFlag(0x00020000) 119 | DSBCAPS_LOCDEFER = BufferCapsFlag(0x00040000) 120 | DSBCAPS_TRUEPLAYPOSITION = BufferCapsFlag(0x00080000) 121 | ) 122 | 123 | func (ds *IDirectSound) CreateSoundBuffer(bufferDesc *BufferDesc) (*IDirectSoundBuffer, error) { 124 | var buf *IDirectSoundBuffer 125 | bufferDesc.size = uint32(unsafe.Sizeof(*bufferDesc)) 126 | err := dsResult(ds.v.CreateSoundBuffer.Call( 127 | uintptr(unsafe.Pointer(ds)), 128 | uintptr(unsafe.Pointer(bufferDesc)), 129 | uintptr(unsafe.Pointer(&buf)), 130 | 0, 131 | )) 132 | if err != nil { 133 | return nil, err 134 | } 135 | return buf, nil 136 | } 137 | -------------------------------------------------------------------------------- /dsound/ds_test.go: -------------------------------------------------------------------------------- 1 | package dsound 2 | 3 | import ( 4 | "math/rand" 5 | "syscall" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | var ( 11 | user32 = syscall.MustLoadDLL("user32.dll") 12 | GetDesktopWindow = user32.MustFindProc("GetDesktopWindow") 13 | ) 14 | 15 | func TestDirectSoundEnumerate(t *testing.T) { 16 | err := DirectSoundEnumerate(func(guid *GUID, description string, module string) bool { 17 | t.Log(guid, description, module) 18 | return true 19 | }) 20 | if err != nil { 21 | t.Error(err) 22 | } 23 | } 24 | 25 | func TestDirectSoundCaptureEnumerate(t *testing.T) { 26 | err := DirectSoundCaptureEnumerate(func(guid *GUID, description string, module string) bool { 27 | t.Log(guid, description, module) 28 | return true 29 | }) 30 | if err != nil { 31 | t.Error(err) 32 | } 33 | } 34 | 35 | func initDirectSound() *IDirectSound { 36 | hasDefaultDevice := false 37 | DirectSoundEnumerate(func(guid *GUID, description string, module string) bool { 38 | if guid == nil { 39 | hasDefaultDevice = true 40 | return false 41 | } 42 | return true 43 | }) 44 | if !hasDefaultDevice { 45 | return nil 46 | } 47 | 48 | ds, err := DirectSoundCreate(nil) 49 | if err != nil { 50 | panic(err) 51 | } 52 | 53 | desktopWindow, _, err := GetDesktopWindow.Call() 54 | err = ds.SetCooperativeLevel(syscall.Handle(desktopWindow), DSSCL_PRIORITY) 55 | if err != nil { 56 | panic(err) 57 | } 58 | 59 | return ds 60 | } 61 | 62 | func TestIDirectSound(t *testing.T) { 63 | ds := initDirectSound() 64 | if ds == nil { 65 | t.Skip("No devices.") 66 | } 67 | 68 | defer ds.Release() 69 | 70 | caps, err := ds.GetCaps() 71 | if err != nil { 72 | t.Error(err) 73 | } 74 | t.Log(caps) 75 | } 76 | 77 | func TestIDirectSoundBufferStatic(t *testing.T) { 78 | const SampleRate = 44100 79 | const Bits = 16 80 | const Channels = 2 81 | const BytesPerSec = SampleRate * (Channels * Bits / 8) 82 | 83 | ds := initDirectSound() 84 | if ds == nil { 85 | t.Skip("No devices.") 86 | } 87 | defer ds.Release() 88 | 89 | // primary buffer 90 | 91 | primaryBuf, err := ds.CreateSoundBuffer(&BufferDesc{ 92 | Flags: DSBCAPS_PRIMARYBUFFER, 93 | BufferBytes: 0, 94 | Format: nil, 95 | }) 96 | if err != nil { 97 | t.Error(err) 98 | } 99 | bcaps, err := primaryBuf.GetCaps() 100 | if err != nil { 101 | t.Error(err) 102 | } 103 | t.Log("PrimaryBufferCaps:", bcaps) 104 | 105 | wfmbuf, err := primaryBuf.GetFormatBytes() 106 | if err != nil { 107 | t.Error(err) 108 | } 109 | t.Log("WaveFormat Bytes:", wfmbuf) 110 | 111 | wfex, err := primaryBuf.GetFormatWaveFormatEx() 112 | if err != nil { 113 | t.Error(err) 114 | } 115 | t.Log("WaveFormat WaveFormatEx:", wfex) 116 | 117 | wfext, err := primaryBuf.GetFormatWaveFormatExtensible() 118 | if err != nil { 119 | t.Error(err) 120 | } 121 | t.Log("WaveFormat WaveFormatExtensible:", wfext) 122 | 123 | err = primaryBuf.SetFormatWaveFormatEx(&WaveFormatEx{ 124 | FormatTag: WAVE_FORMAT_PCM, 125 | Channels: Channels, 126 | SamplesPerSec: SampleRate, 127 | BitsPerSample: Bits, 128 | BlockAlign: Channels * Bits / 8, 129 | AvgBytesPerSec: BytesPerSec, 130 | ExtSize: 0, 131 | }) 132 | if err != nil { 133 | t.Error(err) 134 | } 135 | 136 | wfex, err = primaryBuf.GetFormatWaveFormatEx() 137 | if err != nil { 138 | t.Error(err) 139 | } 140 | t.Log("WaveFormat WaveFormatEx:", wfex) 141 | 142 | primaryBuf.Release() 143 | 144 | // secondary buffer 145 | 146 | secondaryBuf, err := ds.CreateSoundBuffer(&BufferDesc{ 147 | Flags: DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCDEFER, 148 | BufferBytes: BytesPerSec, 149 | Format: &WaveFormatEx{ 150 | FormatTag: WAVE_FORMAT_PCM, 151 | Channels: Channels, 152 | SamplesPerSec: SampleRate, 153 | BitsPerSample: Bits, 154 | BlockAlign: Channels * Bits / 8, 155 | AvgBytesPerSec: BytesPerSec, 156 | ExtSize: 0, 157 | }, 158 | }) 159 | if err != nil { 160 | t.Error(err) 161 | } 162 | defer secondaryBuf.Release() 163 | 164 | err = secondaryBuf.SetVolume(0) 165 | if err != nil { 166 | t.Error(err) 167 | } 168 | 169 | vol, err := secondaryBuf.GetVolume() 170 | if err != nil { 171 | t.Error(err) 172 | } 173 | t.Log("Volume:", vol) 174 | 175 | err = secondaryBuf.SetPan(0) 176 | if err != nil { 177 | t.Error(err) 178 | } 179 | 180 | pan, err := secondaryBuf.GetPan() 181 | if err != nil { 182 | t.Error(err) 183 | } 184 | t.Log("Pan:", pan) 185 | 186 | freq, err := secondaryBuf.GetFrequency() 187 | if err != nil { 188 | t.Error(err) 189 | } 190 | t.Log("Frequncy:", freq) 191 | 192 | is1, is2, err := secondaryBuf.LockInt16s(0, BytesPerSec, 0) 193 | if err != nil { 194 | t.Error(err) 195 | } 196 | t.Log("LockInt16s Buf1Len:", len(is1), "Buf2Len:", len(is2)) 197 | 198 | // noise fade-in 199 | p, ld4 := 0.0, float64(len(is1)) 200 | for i := range is1 { 201 | is1[i] = int16((rand.Float64()*10000 - 5000) * (p / ld4)) 202 | p += 1 203 | } 204 | err = secondaryBuf.UnlockInt16s(is1, is2) 205 | if err != nil { 206 | t.Error(err) 207 | } 208 | 209 | err = secondaryBuf.Play(0, DSBPLAY_LOOPING) 210 | if err != nil { 211 | t.Error(err) 212 | } 213 | 214 | status, err := secondaryBuf.GetStatus() 215 | if err != nil { 216 | t.Error(err) 217 | } 218 | t.Log("Status:", status) 219 | 220 | time.Sleep(time.Second) 221 | } 222 | -------------------------------------------------------------------------------- /dsound/dsresult.go: -------------------------------------------------------------------------------- 1 | package dsound 2 | 3 | import ( 4 | "strconv" 5 | "syscall" 6 | ) 7 | 8 | const ( 9 | DS_OK = DSRESULT(0x00000000) 10 | DSERR_ACCESSDENIED = DSRESULT(0x80070005) 11 | DSERR_ALLOCATED = DSRESULT(0x8878000A) 12 | DSERR_ALREADYINITIALIZED = DSRESULT(0x88780082) 13 | DSERR_BADFORMAT = DSRESULT(0x88780064) 14 | DSERR_BUFFERLOST = DSRESULT(0x88780096) 15 | DSERR_CONTROLUNAVAIL = DSRESULT(0x8878001E) 16 | DSERR_GENERIC = DSRESULT(0x80004005) 17 | DSERR_INVALIDCALL = DSRESULT(0x88780032) 18 | DSERR_INVALIDPARAM = DSRESULT(0x80070057) 19 | DSERR_NOAGGREGATION = DSRESULT(0x80040110) 20 | DSERR_NODRIVER = DSRESULT(0x88780078) 21 | DSERR_NOINTERFACE = DSRESULT(0x80000004) 22 | DSERR_OTHERAPPHASPRIO = DSRESULT(0x887800A0) 23 | DSERR_OUTOFMEMORY = DSRESULT(0x8007000E) 24 | DSERR_PRIOLEVELNEEDED = DSRESULT(0x88780046) 25 | DSERR_UNINITIALIZED = DSRESULT(0x887800AA) 26 | DSERR_UNSUPPORTED = DSRESULT(0x80004001) 27 | ) 28 | 29 | type DSRESULT uintptr 30 | 31 | func (r DSRESULT) Error() string { 32 | switch r { 33 | case DS_OK: 34 | return "DS_OK: The operation completed successfully." 35 | case DSERR_ACCESSDENIED: 36 | return "DSERR_ACCESSDENIED: Access is denied." 37 | case DSERR_ALLOCATED: 38 | return "DSERR_ALLOCATED: The call failed because resources (such as a priority level) were already being used by another caller." 39 | case DSERR_ALREADYINITIALIZED: 40 | return "DSERR_ALREADYINITIALIZED: This object is already initialized" 41 | case DSERR_BADFORMAT: 42 | return "DSERR_BADFORMAT: The specified WAVE format is not supported." 43 | case DSERR_BUFFERLOST: 44 | return "DSERR_BUFFERLOST: The buffer memory has been lost, and must be restored." 45 | case DSERR_CONTROLUNAVAIL: 46 | return "DSERR_CONTROLUNAVAIL: The control (vol,pan,etc.) requested by the caller is not available." 47 | case DSERR_GENERIC: 48 | return "DSERR_GENERIC: An undetermined error occured inside the DirectSound subsystem." 49 | case DSERR_INVALIDCALL: 50 | return "DSERR_INVALIDCALL: This call is not valid for the current state of this object." 51 | case DSERR_INVALIDPARAM: 52 | return "DSERR_INVALIDPARAM: An invalid parameter was passed to the returning function." 53 | case DSERR_NOAGGREGATION: 54 | return "DSERR_NOAGGREGATION: This object does not support aggregation." 55 | case DSERR_NODRIVER: 56 | return "DSERR_NODRIVER: No sound driver is available for use." 57 | case DSERR_NOINTERFACE: 58 | return "DSERR_NOINTERFACE: The requested COM interface is not available." 59 | case DSERR_OTHERAPPHASPRIO: 60 | return "DSERR_OTHERAPPHASPRIO: Another app has a higher priority level, preventing this call from succeeding." 61 | case DSERR_OUTOFMEMORY: 62 | return "DSERR_OUTOFMEMORY: Not enough free memory is available to complete the operation." 63 | case DSERR_PRIOLEVELNEEDED: 64 | return "DSERR_PRIOLEVELNEEDED: The caller does not have the priority level required for the function to succeed." 65 | case DSERR_UNINITIALIZED: 66 | return "DSERR_UNINITIALIZED: This object has not been initialized" 67 | case DSERR_UNSUPPORTED: 68 | return "DSERR_UNSUPPORTED: The function called is not supported at this time." 69 | } 70 | return "0x" + strconv.FormatUint(uint64(r), 16) + ": An unknown error occurred." 71 | } 72 | 73 | func dsResult(r1, r2 uintptr, err syscall.Errno) error { 74 | if r1 != 0 { 75 | return DSRESULT(r1) 76 | } 77 | return nil 78 | } 79 | 80 | func dllDSResult(r1, r2 uintptr, lastErr error) error { 81 | if lastErr.(syscall.Errno) != 0 { 82 | return lastErr 83 | } 84 | if r1 != 0 { 85 | return DSRESULT(r1) 86 | } 87 | return nil 88 | } 89 | 90 | type comProc uintptr 91 | 92 | func (p comProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr syscall.Errno) { 93 | switch len(a) { 94 | case 0: 95 | return syscall.Syscall(uintptr(p), uintptr(len(a)), 0, 0, 0) 96 | case 1: 97 | return syscall.Syscall(uintptr(p), uintptr(len(a)), a[0], 0, 0) 98 | case 2: 99 | return syscall.Syscall(uintptr(p), uintptr(len(a)), a[0], a[1], 0) 100 | case 3: 101 | return syscall.Syscall(uintptr(p), uintptr(len(a)), a[0], a[1], a[2]) 102 | case 4: 103 | return syscall.Syscall6(uintptr(p), uintptr(len(a)), a[0], a[1], a[2], a[3], 0, 0) 104 | case 5: 105 | return syscall.Syscall6(uintptr(p), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], 0) 106 | case 6: 107 | return syscall.Syscall6(uintptr(p), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5]) 108 | case 7: 109 | return syscall.Syscall9(uintptr(p), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], 0, 0) 110 | case 8: 111 | return syscall.Syscall9(uintptr(p), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], 0) 112 | case 9: 113 | return syscall.Syscall9(uintptr(p), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]) 114 | case 10: 115 | return syscall.Syscall12(uintptr(p), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], 0, 0) 116 | case 11: 117 | return syscall.Syscall12(uintptr(p), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], 0) 118 | case 12: 119 | return syscall.Syscall12(uintptr(p), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11]) 120 | case 13: 121 | return syscall.Syscall15(uintptr(p), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], 0, 0) 122 | case 14: 123 | return syscall.Syscall15(uintptr(p), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], 0) 124 | case 15: 125 | return syscall.Syscall15(uintptr(p), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14]) 126 | } 127 | panic("too many arguments") 128 | } 129 | -------------------------------------------------------------------------------- /example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/binary" 5 | "github.com/oov/directsound-go/dsound" 6 | "math" 7 | "math/rand" 8 | "os" 9 | "os/signal" 10 | "syscall" 11 | "unsafe" 12 | ) 13 | 14 | var ( 15 | kernel32 = syscall.MustLoadDLL("kernel32") 16 | CreateEvent = kernel32.MustFindProc("CreateEventW") 17 | WaitForMultipleObjects = kernel32.MustFindProc("WaitForMultipleObjects") 18 | 19 | user32 = syscall.MustLoadDLL("user32") 20 | GetDesktopWindow = user32.MustFindProc("GetDesktopWindow") 21 | ) 22 | 23 | const ( 24 | WAIT_OBJECT_0 = 0x00000000 25 | WAIT_ABANDONED = 0x00000080 26 | WAIT_TIMEOUT = 0x00000102 27 | ) 28 | 29 | type Note float64 30 | 31 | func randomNoteGenerator(octave int, Notes []Note) <-chan Note { 32 | ch := make(chan Note) 33 | go func() { 34 | for { 35 | ch <- Notes[rand.Intn(len(Notes))] + 12.0*Note(rand.Intn(octave)) 36 | } 37 | }() 38 | return ch 39 | } 40 | 41 | func sineArpeggiator(sampleRate float64, noteLen float64, noteGenerator <-chan Note) <-chan float64 { 42 | ch := make(chan float64) 43 | go func() { 44 | for { 45 | step := 2.0 * math.Pi * 440.0 * math.Pow(2.0, float64(<-noteGenerator)/12.0) / sampleRate 46 | for p, ln := 0.0, step*sampleRate*noteLen; p < ln; p += step { 47 | ch <- math.Sin(p) * (ln - p) / ln 48 | } 49 | } 50 | }() 51 | return ch 52 | } 53 | 54 | func infiniteFileReader() <-chan float64 { 55 | f, err := os.Open("48hz16bit2ch.wav") 56 | if err != nil { 57 | panic(err) 58 | } 59 | 60 | b := make([]byte, 4) 61 | _, err = f.ReadAt(b, 40) 62 | if err != nil { 63 | panic(err) 64 | } 65 | 66 | sz := binary.LittleEndian.Uint32(b) 67 | 68 | ch := make(chan float64) 69 | go func() { 70 | var err error 71 | size := sz 72 | for { 73 | _, err = f.Seek(44, os.SEEK_SET) 74 | if err != nil { 75 | panic(err) 76 | } 77 | for i := uint32(0); i < size; i += 4 { 78 | _, err = f.Read(b) 79 | if err != nil { 80 | panic(err) 81 | } 82 | ch <- float64(int16(binary.LittleEndian.Uint16(b[:2]))) / 32768 83 | ch <- float64(int16(binary.LittleEndian.Uint16(b[2:]))) / 32768 84 | } 85 | } 86 | }() 87 | return ch 88 | } 89 | 90 | func fillBuffer(dsb *dsound.IDirectSoundBuffer, blockPos int, blockSize uint32, base, foreL, foreR <-chan float64) { 91 | buf1, buf2, err := dsb.LockInt16s(uint32(blockPos)*blockSize, blockSize, 0) 92 | if err != nil { 93 | panic(err) 94 | } 95 | 96 | defer dsb.UnlockInt16s(buf1, buf2) 97 | 98 | var w float64 99 | for i := 0; i < len(buf1); i += 2 { 100 | w = (<-base + <-foreL) * 0.5 101 | buf1[i] = int16(w * 32767) 102 | w = (<-base + <-foreR) * 0.5 103 | buf1[i+1] = int16(w * 32767) 104 | } 105 | for i := 0; i < len(buf2); i += 2 { 106 | w = (<-base + <-foreL) * 0.5 107 | buf2[i] = int16(w * 32767) 108 | w = (<-base + <-foreR) * 0.5 109 | buf2[i+1] = int16(w * 32767) 110 | } 111 | } 112 | 113 | const ( 114 | SampleRate = 48000 115 | Bits = 16 116 | Channels = 2 117 | BlockAlign = Channels * Bits / 8 118 | BytesPerSec = SampleRate * BlockAlign 119 | NumBlock = 8 120 | BlockSize = (SampleRate / NumBlock) * BlockAlign 121 | ) 122 | 123 | func main() { 124 | ds, err := dsound.DirectSoundCreate(nil) 125 | if err != nil { 126 | panic(err) 127 | } 128 | 129 | desktopWindow, _, err := GetDesktopWindow.Call() 130 | err = ds.SetCooperativeLevel(syscall.Handle(desktopWindow), dsound.DSSCL_PRIORITY) 131 | if err != nil { 132 | panic(err) 133 | } 134 | 135 | defer ds.Release() 136 | 137 | // primary buffer 138 | 139 | primaryBuf, err := ds.CreateSoundBuffer(&dsound.BufferDesc{ 140 | Flags: dsound.DSBCAPS_PRIMARYBUFFER, 141 | BufferBytes: 0, 142 | Format: nil, 143 | }) 144 | if err != nil { 145 | panic(err) 146 | } 147 | 148 | err = primaryBuf.SetFormatWaveFormatEx(&dsound.WaveFormatEx{ 149 | FormatTag: dsound.WAVE_FORMAT_PCM, 150 | Channels: Channels, 151 | SamplesPerSec: SampleRate, 152 | BitsPerSample: Bits, 153 | BlockAlign: BlockAlign, 154 | AvgBytesPerSec: BytesPerSec, 155 | ExtSize: 0, 156 | }) 157 | if err != nil { 158 | panic(err) 159 | } 160 | 161 | primaryBuf.Release() 162 | 163 | // secondary buffer 164 | 165 | secondaryBuf, err := ds.CreateSoundBuffer(&dsound.BufferDesc{ 166 | Flags: dsound.DSBCAPS_GLOBALFOCUS | dsound.DSBCAPS_GETCURRENTPOSITION2 | dsound.DSBCAPS_CTRLPOSITIONNOTIFY, 167 | BufferBytes: BlockSize * NumBlock, 168 | Format: &dsound.WaveFormatEx{ 169 | FormatTag: dsound.WAVE_FORMAT_PCM, 170 | Channels: Channels, 171 | SamplesPerSec: SampleRate, 172 | BitsPerSample: Bits, 173 | BlockAlign: Channels * Bits / 8, 174 | AvgBytesPerSec: BytesPerSec, 175 | ExtSize: 0, 176 | }, 177 | }) 178 | if err != nil { 179 | panic(err) 180 | } 181 | defer secondaryBuf.Release() 182 | 183 | wave := infiniteFileReader() 184 | arp := sineArpeggiator( 185 | SampleRate, 186 | 1.0/4.0, 187 | randomNoteGenerator(2, []Note{3, 5, 7, 10}), 188 | ) 189 | arp2 := sineArpeggiator( 190 | SampleRate, 191 | 1.0/8.0, 192 | randomNoteGenerator(2, []Note{0, 7, 12}), 193 | ) 194 | 195 | // fill buffer without last block 196 | 197 | for blockPos := 0; blockPos < NumBlock-1; blockPos++ { 198 | fillBuffer(secondaryBuf, blockPos, BlockSize, wave, arp, arp2) 199 | } 200 | 201 | // setup notify event 202 | 203 | notifies := make([]dsound.DSBPOSITIONNOTIFY, NumBlock) 204 | events := make([]syscall.Handle, 0) 205 | for i := range notifies { 206 | h, _, _ := CreateEvent.Call(0, 0, 0, 0) 207 | notifies[i].EventNotify = syscall.Handle(h) 208 | notifies[i].Offset = uint32(i * BlockSize) 209 | events = append(events, syscall.Handle(h)) 210 | } 211 | 212 | notif, err := secondaryBuf.QueryInterfaceIDirectSoundNotify() 213 | if err != nil { 214 | panic(err) 215 | } 216 | defer notif.Release() 217 | 218 | err = notif.SetNotificationPositions(notifies) 219 | if err != nil { 220 | panic(err) 221 | } 222 | 223 | err = secondaryBuf.Play(0, dsound.DSBPLAY_LOOPING) 224 | if err != nil { 225 | panic(err) 226 | } 227 | 228 | go func() { 229 | outer: 230 | for { 231 | r, _, _ := WaitForMultipleObjects.Call( 232 | uintptr(uint32(len(events))), 233 | uintptr(unsafe.Pointer(&events[0])), 234 | 0, 235 | 0xFFFFFFFF, 236 | ) 237 | switch { 238 | case WAIT_OBJECT_0 <= r && r < WAIT_OBJECT_0+uintptr(len(events)): 239 | idx := int(r - WAIT_OBJECT_0) 240 | blockPos := (idx - 1 + NumBlock) % NumBlock 241 | fillBuffer(secondaryBuf, blockPos, BlockSize, wave, arp, arp2) 242 | 243 | case WAIT_ABANDONED <= r && r < WAIT_ABANDONED+uintptr(len(events)): 244 | break outer 245 | 246 | case r == WAIT_TIMEOUT: 247 | break outer 248 | } 249 | } 250 | }() 251 | 252 | sig := make(chan os.Signal, 1) 253 | signal.Notify(sig, os.Interrupt) 254 | for _ = range sig { 255 | return 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /dsound/waveformatex.go: -------------------------------------------------------------------------------- 1 | package dsound 2 | 3 | type WaveFormatEx struct { 4 | FormatTag WaveFormatTag 5 | Channels uint16 6 | SamplesPerSec uint32 7 | AvgBytesPerSec uint32 8 | BlockAlign uint16 9 | BitsPerSample uint16 10 | ExtSize uint16 11 | } 12 | 13 | type WaveFormatExtensible struct { 14 | Format WaveFormatEx 15 | Samples uint16 // union { ValidBitsPerSample or SamplesPerBlock or Reserved } 16 | ChannelMask WFESpeaker 17 | SubFormat GUID 18 | } 19 | 20 | type WaveFormatTag uint16 21 | 22 | const ( 23 | WAVE_FORMAT_UNKNOWN = WaveFormatTag(0x0000) // Microsoft Corporation 24 | WAVE_FORMAT_PCM = WaveFormatTag(0x0001) // Microsoft PCM format 25 | WAVE_FORMAT_MS_ADPCM = WaveFormatTag(0x0002) // Microsoft ADPCM 26 | WAVE_FORMAT_IEEE_FLOAT = WaveFormatTag(0x0003) // Micrososft 32 bit float format 27 | WAVE_FORMAT_VSELP = WaveFormatTag(0x0004) // Compaq Computer Corporation 28 | WAVE_FORMAT_IBM_CVSD = WaveFormatTag(0x0005) // IBM Corporation 29 | WAVE_FORMAT_ALAW = WaveFormatTag(0x0006) // Microsoft Corporation 30 | WAVE_FORMAT_MULAW = WaveFormatTag(0x0007) // Microsoft Corporation 31 | WAVE_FORMAT_OKI_ADPCM = WaveFormatTag(0x0010) // OKI 32 | WAVE_FORMAT_IMA_ADPCM = WaveFormatTag(0x0011) // Intel Corporation 33 | WAVE_FORMAT_MEDIASPACE_ADPCM = WaveFormatTag(0x0012) // Videologic 34 | WAVE_FORMAT_SIERRA_ADPCM = WaveFormatTag(0x0013) // Sierra Semiconductor Corp 35 | WAVE_FORMAT_G723_ADPCM = WaveFormatTag(0x0014) // Antex Electronics Corporation 36 | WAVE_FORMAT_DIGISTD = WaveFormatTag(0x0015) // DSP Solutions, Inc. 37 | WAVE_FORMAT_DIGIFIX = WaveFormatTag(0x0016) // DSP Solutions, Inc. 38 | WAVE_FORMAT_DIALOGIC_OKI_ADPCM = WaveFormatTag(0x0017) // Dialogic Corporation 39 | WAVE_FORMAT_MEDIAVISION_ADPCM = WaveFormatTag(0x0018) // Media Vision, Inc. 40 | WAVE_FORMAT_CU_CODEC = WaveFormatTag(0x0019) // Hewlett-Packard Company 41 | WAVE_FORMAT_YAMAHA_ADPCM = WaveFormatTag(0x0020) // Yamaha Corporation of America 42 | WAVE_FORMAT_SONARC = WaveFormatTag(0x0021) // Speech Compression 43 | WAVE_FORMAT_DSPGROUP_TRUESPEECH = WaveFormatTag(0x0022) // DSP Group, Inc 44 | WAVE_FORMAT_ECHOSC1 = WaveFormatTag(0x0023) // Echo Speech Corporation 45 | WAVE_FORMAT_AUDIOFILE_AF36 = WaveFormatTag(0x0024) // Audiofile, Inc. 46 | WAVE_FORMAT_APTX = WaveFormatTag(0x0025) // Audio Processing Technology 47 | WAVE_FORMAT_AUDIOFILE_AF10 = WaveFormatTag(0x0026) // Audiofile, Inc. 48 | WAVE_FORMAT_PROSODY_1612 = WaveFormatTag(0x0027) // Aculab plc 49 | WAVE_FORMAT_LRC = WaveFormatTag(0x0028) // Merging Technologies S.A. 50 | WAVE_FORMAT_DOLBY_AC2 = WaveFormatTag(0x0030) // Dolby Laboratories 51 | WAVE_FORMAT_GSM610 = WaveFormatTag(0x0031) // Microsoft Corporation 52 | WAVE_FORMAT_MSNAUDIO = WaveFormatTag(0x0032) // Microsoft Corporation 53 | WAVE_FORMAT_ANTEX_ADPCME = WaveFormatTag(0x0033) // Antex Electronics Corporation 54 | WAVE_FORMAT_CONTROL_RES_VQLPC = WaveFormatTag(0x0034) // Control Resources Limited 55 | WAVE_FORMAT_DIGIREAL = WaveFormatTag(0x0035) // DSP Solutions, Inc. 56 | WAVE_FORMAT_DIGIADPCM = WaveFormatTag(0x0036) // DSP Solutions, Inc. 57 | WAVE_FORMAT_CONTROL_RES_CR10 = WaveFormatTag(0x0037) // Control Resources Limited 58 | WAVE_FORMAT_NMS_VBXADPCM = WaveFormatTag(0x0038) // Natural MicroSystems 59 | WAVE_FORMAT_ROLAND_RDAC = WaveFormatTag(0x0039) // Roland 60 | WAVE_FORMAT_ECHOSC3 = WaveFormatTag(0x003A) // Echo Speech Corporation 61 | WAVE_FORMAT_ROCKWELL_ADPCM = WaveFormatTag(0x003B) // Rockwell International 62 | WAVE_FORMAT_ROCKWELL_DIGITALK = WaveFormatTag(0x003C) // Rockwell International 63 | WAVE_FORMAT_XEBEC = WaveFormatTag(0x003D) // Xebec Multimedia Solutions Limited 64 | WAVE_FORMAT_G721_ADPCM = WaveFormatTag(0x0040) // Antex Electronics Corporation 65 | WAVE_FORMAT_G728_CELP = WaveFormatTag(0x0041) // Antex Electronics Corporation 66 | WAVE_FORMAT_MSG723 = WaveFormatTag(0x0042) // Microsoft Corporation 67 | WAVE_FORMAT_MPEG = WaveFormatTag(0x0050) // Microsoft Corporation 68 | WAVE_FORMAT_RT24 = WaveFormatTag(0x0052) // InSoft Inc. 69 | WAVE_FORMAT_PAC = WaveFormatTag(0x0053) // InSoft Inc. 70 | WAVE_FORMAT_MPEGLAYER3 = WaveFormatTag(0x0055) // MPEG 3 Layer 1 71 | WAVE_FORMAT_LUCENT_G723 = WaveFormatTag(0x0059) // Lucent Technologies 72 | WAVE_FORMAT_CIRRUS = WaveFormatTag(0x0060) // Cirrus Logic 73 | WAVE_FORMAT_ESPCM = WaveFormatTag(0x0061) // ESS Technology 74 | WAVE_FORMAT_VOXWARE = WaveFormatTag(0x0062) // Voxware Inc 75 | WAVE_FORMAT_CANOPUS_ATRAC = WaveFormatTag(0x0063) // Canopus, Co., Ltd. 76 | WAVE_FORMAT_G726_ADPCM = WaveFormatTag(0x0064) // APICOM 77 | WAVE_FORMAT_G722_ADPCM = WaveFormatTag(0x0065) // APICOM 78 | WAVE_FORMAT_DSAT = WaveFormatTag(0x0066) // Microsoft Corporation 79 | WAVE_FORMAT_DSAT_DISPLAY = WaveFormatTag(0x0067) // Microsoft Corporation 80 | WAVE_FORMAT_VOXWARE_BYTE_ALIGNED = WaveFormatTag(0x0069) // Voxware Inc. 81 | WAVE_FORMAT_VOXWARE_AC8 = WaveFormatTag(0x0070) // Voxware Inc. 82 | WAVE_FORMAT_VOXWARE_AC10 = WaveFormatTag(0x0071) // Voxware Inc. 83 | WAVE_FORMAT_VOXWARE_AC16 = WaveFormatTag(0x0072) // Voxware Inc. 84 | WAVE_FORMAT_VOXWARE_AC20 = WaveFormatTag(0x0073) // Voxware Inc. 85 | WAVE_FORMAT_VOXWARE_RT24 = WaveFormatTag(0x0074) // Voxware Inc. 86 | WAVE_FORMAT_VOXWARE_RT29 = WaveFormatTag(0x0075) // Voxware Inc. 87 | WAVE_FORMAT_VOXWARE_RT29HW = WaveFormatTag(0x0076) // Voxware Inc. 88 | WAVE_FORMAT_VOXWARE_VR12 = WaveFormatTag(0x0077) // Voxware Inc. 89 | WAVE_FORMAT_VOXWARE_VR18 = WaveFormatTag(0x0078) // Voxware Inc. 90 | WAVE_FORMAT_VOXWARE_TQ40 = WaveFormatTag(0x0079) // Voxware Inc. 91 | WAVE_FORMAT_SOFTSOUND = WaveFormatTag(0x0080) // Softsound, Ltd. 92 | WAVE_FORMAT_VOXARE_TQ60 = WaveFormatTag(0x0081) // Voxware Inc. 93 | WAVE_FORMAT_MSRT24 = WaveFormatTag(0x0082) // Microsoft Corporation 94 | WAVE_FORMAT_G729A = WaveFormatTag(0x0083) // AT&T Laboratories 95 | WAVE_FORMAT_MVI_MV12 = WaveFormatTag(0x0084) // Motion Pixels 96 | WAVE_FORMAT_DF_G726 = WaveFormatTag(0x0085) // DataFusion Systems (Pty) (Ltd) 97 | WAVE_FORMAT_DF_GSM610 = WaveFormatTag(0x0086) // DataFusion Systems (Pty) (Ltd) 98 | WAVE_FORMAT_ONLIVE = WaveFormatTag(0x0089) // OnLive! Technologies, Inc. 99 | WAVE_FORMAT_SBC24 = WaveFormatTag(0x0091) // Siemens Business Communications Systems 100 | WAVE_FORMAT_DOLBY_AC3_SPDIF = WaveFormatTag(0x0092) // Sonic Foundry 101 | WAVE_FORMAT_ZYXEL_ADPCM = WaveFormatTag(0x0097) // ZyXEL Communications, Inc. 102 | WAVE_FORMAT_PHILIPS_LPCBB = WaveFormatTag(0x0098) // Philips Speech Processing 103 | WAVE_FORMAT_PACKED = WaveFormatTag(0x0099) // Studer Professional Audio AG 104 | WAVE_FORMAT_RHETOREX_ADPCM = WaveFormatTag(0x0100) // Rhetorex, Inc. 105 | IBM_FORMAT_MULAW = WaveFormatTag(0x0101) // IBM mu-law format 106 | IBM_FORMAT_ALAW = WaveFormatTag(0x0102) // IBM a-law format 107 | IBM_FORMAT_ADPCM = WaveFormatTag(0x0103) // IBM AVC Adaptive Differential PCM format 108 | WAVE_FORMAT_VIVO_G723 = WaveFormatTag(0x0111) // Vivo Software 109 | WAVE_FORMAT_VIVO_SIREN = WaveFormatTag(0x0112) // Vivo Software 110 | WAVE_FORMAT_DIGITAL_G723 = WaveFormatTag(0x0123) // Digital Equipment Corporation 111 | WAVE_FORMAT_CREATIVE_ADPCM = WaveFormatTag(0x0200) // Creative Labs, Inc 112 | WAVE_FORMAT_CREATIVE_FASTSPEECH8 = WaveFormatTag(0x0202) // Creative Labs, Inc 113 | WAVE_FORMAT_CREATIVE_FASTSPEECH10 = WaveFormatTag(0x0203) // Creative Labs, Inc 114 | WAVE_FORMAT_QUARTERDECK = WaveFormatTag(0x0220) // Quarterdeck Corporation 115 | WAVE_FORMAT_FM_TOWNS_SND = WaveFormatTag(0x0300) // Fujitsu Corporation 116 | WAVE_FORMAT_BZV_DIGITAL = WaveFormatTag(0x0400) // Brooktree Corporation 117 | WAVE_FORMAT_VME_VMPCM = WaveFormatTag(0x0680) // AT&T Labs, Inc. 118 | WAVE_FORMAT_OLIGSM = WaveFormatTag(0x1000) // Ing C. Olivetti & C., S.p.A. 119 | WAVE_FORMAT_OLIADPCM = WaveFormatTag(0x1001) // Ing C. Olivetti & C., S.p.A. 120 | WAVE_FORMAT_OLICELP = WaveFormatTag(0x1002) // Ing C. Olivetti & C., S.p.A. 121 | WAVE_FORMAT_OLISBC = WaveFormatTag(0x1003) // Ing C. Olivetti & C., S.p.A. 122 | WAVE_FORMAT_OLIOPR = WaveFormatTag(0x1004) // Ing C. Olivetti & C., S.p.A. 123 | WAVE_FORMAT_LH_CODEC = WaveFormatTag(0x1100) // Lernout & Hauspie 124 | WAVE_FORMAT_NORRIS = WaveFormatTag(0x1400) // Norris Communications, Inc. 125 | WAVE_FORMAT_SOUNDSPACE_MUSICOMPRESS = WaveFormatTag(0x1500) // AT&T Labs, Inc. 126 | WAVE_FORMAT_DVM = WaveFormatTag(0x2000) // FAST Multimedia AG 127 | WAVE_FORMAT_INTERWAV_VSC112 = WaveFormatTag(0x7150) // ????? 128 | WAVE_FORMAT_EXTENSIBLE = WaveFormatTag(0xFFFE) // 129 | ) 130 | 131 | type WFESpeaker uint32 132 | 133 | const ( 134 | SPEAKER_FRONT_LEFT = WFESpeaker(0x00000001) 135 | SPEAKER_FRONT_RIGHT = WFESpeaker(0x00000002) 136 | SPEAKER_FRONT_CENTER = WFESpeaker(0x00000004) 137 | SPEAKER_LOW_FREQUENCY = WFESpeaker(0x00000008) 138 | SPEAKER_BACK_LEFT = WFESpeaker(0x00000010) 139 | SPEAKER_BACK_RIGHT = WFESpeaker(0x00000020) 140 | SPEAKER_FRONT_LEFT_OF_CENTER = WFESpeaker(0x00000040) 141 | SPEAKER_FRONT_RIGHT_OF_CENTER = WFESpeaker(0x00000080) 142 | SPEAKER_BACK_CENTER = WFESpeaker(0x00000100) 143 | SPEAKER_SIDE_LEFT = WFESpeaker(0x00000200) 144 | SPEAKER_SIDE_RIGHT = WFESpeaker(0x00000400) 145 | SPEAKER_TOP_CENTER = WFESpeaker(0x00000800) 146 | SPEAKER_TOP_FRONT_LEFT = WFESpeaker(0x00001000) 147 | SPEAKER_TOP_FRONT_CENTER = WFESpeaker(0x00002000) 148 | SPEAKER_TOP_FRONT_RIGHT = WFESpeaker(0x00004000) 149 | SPEAKER_TOP_BACK_LEFT = WFESpeaker(0x00008000) 150 | SPEAKER_TOP_BACK_CENTER = WFESpeaker(0x00010000) 151 | SPEAKER_TOP_BACK_RIGHT = WFESpeaker(0x00020000) 152 | ) 153 | -------------------------------------------------------------------------------- /dsound/idirectsoundbuffer.go: -------------------------------------------------------------------------------- 1 | package dsound 2 | 3 | import ( 4 | "unsafe" 5 | ) 6 | 7 | const maxInt = int(^uint32(0) >> 1) 8 | 9 | type IDirectSoundBuffer struct { 10 | v *iDirectSoundBufferVTable 11 | } 12 | 13 | type iDirectSoundBufferVTable struct { 14 | iUnknownVTable 15 | GetCaps comProc 16 | GetCurrentPosition comProc 17 | GetFormat comProc 18 | GetVolume comProc 19 | GetPan comProc 20 | GetFrequency comProc 21 | GetStatus comProc 22 | Initialize comProc 23 | Lock comProc 24 | Play comProc 25 | SetCurrentPosition comProc 26 | SetFormat comProc 27 | SetVolume comProc 28 | SetPan comProc 29 | SetFrequency comProc 30 | Stop comProc 31 | Unlock comProc 32 | Restore comProc 33 | } 34 | 35 | func (dsb *IDirectSoundBuffer) QueryInterface(iid *GUID) (*IUnknown, error) { 36 | return (*IUnknown)(unsafe.Pointer(dsb)).QueryInterface(iid) 37 | } 38 | 39 | func (dsb *IDirectSoundBuffer) QueryInterfaceIDirectSoundNotify() (*IDirectSoundNotify, error) { 40 | IID_IDirectSoundNotify := &GUID{0xb0210783, 0x89cd, 0x11d0, [...]byte{0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16}} 41 | unk, err := dsb.QueryInterface(IID_IDirectSoundNotify) 42 | if err != nil { 43 | return nil, err 44 | } 45 | return (*IDirectSoundNotify)(unsafe.Pointer(unk)), nil 46 | } 47 | 48 | func (dsb *IDirectSoundBuffer) AddRef() uint32 { 49 | return (*IUnknown)(unsafe.Pointer(dsb)).AddRef() 50 | } 51 | 52 | func (dsb *IDirectSoundBuffer) Release() uint32 { 53 | return (*IUnknown)(unsafe.Pointer(dsb)).Release() 54 | } 55 | 56 | type BufferCaps struct { 57 | size uint32 58 | Flags uint32 59 | BufferBytes uint32 60 | UnlockTransferRate uint32 61 | PlayCpuOverhead uint32 62 | } 63 | 64 | func (dsb *IDirectSoundBuffer) GetCaps() (*BufferCaps, error) { 65 | var c BufferCaps 66 | c.size = uint32(unsafe.Sizeof(c)) 67 | err := dsResult(dsb.v.GetCaps.Call( 68 | uintptr(unsafe.Pointer(dsb)), 69 | uintptr(unsafe.Pointer(&c)), 70 | )) 71 | if err != nil { 72 | return nil, err 73 | } 74 | return &c, nil 75 | } 76 | 77 | func (dsb *IDirectSoundBuffer) GetCurrentPosition() (currentPlayCursor, currentWriteCursor uint32, err error) { 78 | err = dsResult(dsb.v.GetCurrentPosition.Call( 79 | uintptr(unsafe.Pointer(dsb)), 80 | uintptr(unsafe.Pointer(¤tPlayCursor)), 81 | uintptr(unsafe.Pointer(¤tWriteCursor)), 82 | )) 83 | return 84 | } 85 | 86 | func (dsb *IDirectSoundBuffer) GetFormatBytes() ([]byte, error) { 87 | var sz uint32 88 | err := dsResult(dsb.v.GetFormat.Call( 89 | uintptr(unsafe.Pointer(dsb)), 90 | 0, 91 | 0, 92 | uintptr(unsafe.Pointer(&sz)), 93 | )) 94 | if err != nil { 95 | return nil, err 96 | } 97 | 98 | buf := make([]byte, sz) 99 | err = dsResult(dsb.v.GetFormat.Call( 100 | uintptr(unsafe.Pointer(dsb)), 101 | uintptr(unsafe.Pointer(&buf[0])), 102 | uintptr(sz), 103 | 0, 104 | )) 105 | if err != nil { 106 | return nil, err 107 | } 108 | 109 | return buf, nil 110 | } 111 | 112 | func (dsb *IDirectSoundBuffer) GetFormatWaveFormatEx() (*WaveFormatEx, error) { 113 | var wfex WaveFormatEx 114 | err := dsResult(dsb.v.GetFormat.Call( 115 | uintptr(unsafe.Pointer(dsb)), 116 | uintptr(unsafe.Pointer(&wfex)), 117 | uintptr(unsafe.Sizeof(wfex)), 118 | 0, 119 | )) 120 | if err != nil { 121 | return nil, err 122 | } 123 | 124 | return &wfex, nil 125 | } 126 | 127 | func (dsb *IDirectSoundBuffer) GetFormatWaveFormatExtensible() (*WaveFormatExtensible, error) { 128 | var wfext WaveFormatExtensible 129 | err := dsResult(dsb.v.GetFormat.Call( 130 | uintptr(unsafe.Pointer(dsb)), 131 | uintptr(unsafe.Pointer(&wfext)), 132 | uintptr(unsafe.Sizeof(wfext)), 133 | 0, 134 | )) 135 | if err != nil { 136 | return nil, err 137 | } 138 | 139 | return &wfext, nil 140 | } 141 | 142 | func (dsb *IDirectSoundBuffer) GetVolume() (int32, error) { 143 | var vol int32 144 | err := dsResult(dsb.v.GetVolume.Call( 145 | uintptr(unsafe.Pointer(dsb)), 146 | uintptr(unsafe.Pointer(&vol)), 147 | )) 148 | if err != nil { 149 | return 0, err 150 | } 151 | return vol, nil 152 | } 153 | 154 | func (dsb *IDirectSoundBuffer) GetPan() (int32, error) { 155 | var pan int32 156 | err := dsResult(dsb.v.GetPan.Call( 157 | uintptr(unsafe.Pointer(dsb)), 158 | uintptr(unsafe.Pointer(&pan)), 159 | )) 160 | if err != nil { 161 | return 0, err 162 | } 163 | return pan, nil 164 | } 165 | 166 | func (dsb *IDirectSoundBuffer) GetFrequency() (uint32, error) { 167 | var freq uint32 168 | err := dsResult(dsb.v.GetFrequency.Call( 169 | uintptr(unsafe.Pointer(dsb)), 170 | uintptr(unsafe.Pointer(&freq)), 171 | )) 172 | if err != nil { 173 | return 0, err 174 | } 175 | return freq, nil 176 | } 177 | 178 | type BufferStatus uint32 179 | 180 | const ( 181 | DSBSTATUS_PLAYING = BufferStatus(0x00000001) 182 | DSBSTATUS_BUFFERLOST = BufferStatus(0x00000002) 183 | DSBSTATUS_LOOPING = BufferStatus(0x00000004) 184 | DSBSTATUS_LOCHARDWARE = BufferStatus(0x00000008) 185 | DSBSTATUS_LOCSOFTWARE = BufferStatus(0x00000010) 186 | DSBSTATUS_TERMINATED = BufferStatus(0x00000020) 187 | ) 188 | 189 | func (dsb *IDirectSoundBuffer) GetStatus() (BufferStatus, error) { 190 | var s BufferStatus 191 | err := dsResult(dsb.v.GetStatus.Call( 192 | uintptr(unsafe.Pointer(dsb)), 193 | uintptr(unsafe.Pointer(&s)), 194 | )) 195 | if err != nil { 196 | return 0, err 197 | } 198 | return s, nil 199 | } 200 | 201 | type BufferLockFlag uint32 202 | 203 | const ( 204 | DSBLOCK_FROMWRITECURSOR = BufferLockFlag(0x00000001) 205 | DSBLOCK_ENTIREBUFFER = BufferLockFlag(0x00000002) 206 | ) 207 | 208 | func (dsb *IDirectSoundBuffer) Lock(offset uint32, bytes uint32, flags BufferLockFlag) (ptr1 uintptr, bytes1 uint32, ptr2 uintptr, bytes2 uint32, err error) { 209 | err = dsResult(dsb.v.Lock.Call( 210 | uintptr(unsafe.Pointer(dsb)), 211 | uintptr(offset), 212 | uintptr(bytes), 213 | uintptr(unsafe.Pointer(&ptr1)), 214 | uintptr(unsafe.Pointer(&bytes1)), 215 | uintptr(unsafe.Pointer(&ptr2)), 216 | uintptr(unsafe.Pointer(&bytes2)), 217 | uintptr(flags), 218 | )) 219 | return 220 | } 221 | 222 | func (dsb *IDirectSoundBuffer) LockBytes(offset uint32, bytes uint32, flags BufferLockFlag) ([]byte, []byte, error) { 223 | var ptr1, ptr2 *[maxInt]byte 224 | var bytes1, bytes2 uint32 225 | err := dsResult(dsb.v.Lock.Call( 226 | uintptr(unsafe.Pointer(dsb)), 227 | uintptr(offset), 228 | uintptr(bytes), 229 | uintptr(unsafe.Pointer(&ptr1)), 230 | uintptr(unsafe.Pointer(&bytes1)), 231 | uintptr(unsafe.Pointer(&ptr2)), 232 | uintptr(unsafe.Pointer(&bytes2)), 233 | uintptr(flags), 234 | )) 235 | if err != nil { 236 | return nil, nil, err 237 | } 238 | 239 | var buf1, buf2 []byte 240 | if ptr1 != nil && bytes1 > 0 { 241 | buf1 = ptr1[:bytes1] 242 | } 243 | if ptr2 != nil && bytes2 > 0 { 244 | buf2 = ptr2[:bytes2] 245 | } 246 | return buf1, buf2, nil 247 | } 248 | 249 | func (dsb *IDirectSoundBuffer) LockInt16s(offset uint32, bytes uint32, flags BufferLockFlag) ([]int16, []int16, error) { 250 | var ptr1, ptr2 *[maxInt>>1]int16 251 | var bytes1, bytes2 uint32 252 | err := dsResult(dsb.v.Lock.Call( 253 | uintptr(unsafe.Pointer(dsb)), 254 | uintptr(offset), 255 | uintptr(bytes), 256 | uintptr(unsafe.Pointer(&ptr1)), 257 | uintptr(unsafe.Pointer(&bytes1)), 258 | uintptr(unsafe.Pointer(&ptr2)), 259 | uintptr(unsafe.Pointer(&bytes2)), 260 | uintptr(flags), 261 | )) 262 | if err != nil { 263 | return nil, nil, err 264 | } 265 | 266 | var buf1, buf2 []int16 267 | if ptr1 != nil && bytes1 > 0 { 268 | buf1 = ptr1[:bytes1>>1] 269 | } 270 | if ptr2 != nil && bytes2 > 0 { 271 | buf2 = ptr2[:bytes2>>1] 272 | } 273 | return buf1, buf2, nil 274 | } 275 | 276 | type BufferPlayFlag uint32 277 | 278 | const ( 279 | DSBPLAY_LOOPING = BufferPlayFlag(0x000000001) 280 | DSBPLAY_LOCHARDWARE = BufferPlayFlag(0x000000002) 281 | DSBPLAY_LOCSOFTWARE = BufferPlayFlag(0x000000004) 282 | DSBPLAY_TERMINATEBY_TIME = BufferPlayFlag(0x000000008) 283 | DSBPLAY_TERMINATEBY_DISTANCE = BufferPlayFlag(0x000000010) 284 | DSBPLAY_TERMINATEBY_PRIORITY = BufferPlayFlag(0x000000020) 285 | ) 286 | 287 | func (dsb *IDirectSoundBuffer) Play(priority uint32, flags BufferPlayFlag) error { 288 | return dsResult(dsb.v.Play.Call( 289 | uintptr(unsafe.Pointer(dsb)), 290 | 0, 291 | uintptr(priority), 292 | uintptr(flags), 293 | )) 294 | } 295 | 296 | func (dsb *IDirectSoundBuffer) SetCurrentPosition(newPosition uint32) error { 297 | return dsResult(dsb.v.SetCurrentPosition.Call( 298 | uintptr(unsafe.Pointer(dsb)), 299 | uintptr(newPosition), 300 | )) 301 | } 302 | 303 | func (dsb *IDirectSoundBuffer) SetFormatBytes(bytes []byte) error { 304 | return dsResult(dsb.v.SetFormat.Call( 305 | uintptr(unsafe.Pointer(dsb)), 306 | uintptr(unsafe.Pointer(&bytes[0])), 307 | )) 308 | } 309 | 310 | func (dsb *IDirectSoundBuffer) SetFormatWaveFormatEx(wfex *WaveFormatEx) error { 311 | return dsResult(dsb.v.SetFormat.Call( 312 | uintptr(unsafe.Pointer(dsb)), 313 | uintptr(unsafe.Pointer(wfex)), 314 | )) 315 | } 316 | 317 | func (dsb *IDirectSoundBuffer) SetFormatWaveFormatExtensible(wfext *WaveFormatExtensible) error { 318 | return dsResult(dsb.v.SetFormat.Call( 319 | uintptr(unsafe.Pointer(dsb)), 320 | uintptr(unsafe.Pointer(wfext)), 321 | )) 322 | } 323 | 324 | func (dsb *IDirectSoundBuffer) SetVolume(volume int32) error { 325 | return dsResult(dsb.v.SetVolume.Call( 326 | uintptr(unsafe.Pointer(dsb)), 327 | uintptr(volume), 328 | )) 329 | } 330 | 331 | func (dsb *IDirectSoundBuffer) SetPan(pan int32) error { 332 | return dsResult(dsb.v.SetPan.Call( 333 | uintptr(unsafe.Pointer(dsb)), 334 | uintptr(pan), 335 | )) 336 | } 337 | 338 | func (dsb *IDirectSoundBuffer) SetFrequency(freq uint32) error { 339 | return dsResult(dsb.v.SetFrequency.Call( 340 | uintptr(unsafe.Pointer(dsb)), 341 | uintptr(freq), 342 | )) 343 | } 344 | 345 | func (dsb *IDirectSoundBuffer) Stop() error { 346 | return dsResult(dsb.v.Stop.Call( 347 | uintptr(unsafe.Pointer(dsb)), 348 | )) 349 | } 350 | 351 | func (dsb *IDirectSoundBuffer) Unlock(ptr1 uintptr, bytes1 uint32, ptr2 uintptr, bytes2 uint32) error { 352 | return dsResult(dsb.v.Unlock.Call( 353 | uintptr(unsafe.Pointer(dsb)), 354 | ptr1, 355 | uintptr(bytes1), 356 | ptr2, 357 | uintptr(bytes2), 358 | )) 359 | } 360 | 361 | func (dsb *IDirectSoundBuffer) UnlockBytes(buf1 []byte, buf2 []byte) error { 362 | var ptr1, ptr2 uintptr 363 | var bytes1, bytes2 uint32 364 | if buf1 != nil { 365 | ptr1 = uintptr(unsafe.Pointer(&buf1[0])) 366 | bytes1 = uint32(len(buf1)) 367 | } 368 | if buf2 != nil { 369 | ptr2 = uintptr(unsafe.Pointer(&buf2[0])) 370 | bytes2 = uint32(len(buf2)) 371 | } 372 | return dsb.Unlock(ptr1, bytes1, ptr2, bytes2) 373 | } 374 | 375 | func (dsb *IDirectSoundBuffer) UnlockInt16s(buf1 []int16, buf2 []int16) error { 376 | var ptr1, ptr2 uintptr 377 | var bytes1, bytes2 uint32 378 | if buf1 != nil { 379 | ptr1 = uintptr(unsafe.Pointer(&buf1[0])) 380 | bytes1 = uint32(len(buf1) << 1) 381 | } 382 | if buf2 != nil { 383 | ptr2 = uintptr(unsafe.Pointer(&buf2[0])) 384 | bytes2 = uint32(len(buf2) << 1) 385 | } 386 | return dsb.Unlock(ptr1, bytes1, ptr2, bytes2) 387 | } 388 | 389 | func (dsb *IDirectSoundBuffer) Restore() error { 390 | return dsResult(dsb.v.Restore.Call( 391 | uintptr(unsafe.Pointer(dsb)), 392 | )) 393 | } 394 | --------------------------------------------------------------------------------