code.witches.io/go/sdl2@v0.1.1/audio.go (about)

     1  package sdl
     2  
     3  // #include <SDL2/SDL_audio.h>
     4  // void callback(void *userdata, Uint8 *stream, int len);
     5  import "C"
     6  import (
     7  	"io"
     8  	"reflect"
     9  	"sync"
    10  	"unsafe"
    11  )
    12  
    13  type AudioCallback func(userData unsafe.Pointer, stream *byte, length int32)
    14  
    15  type AudioSpec struct {
    16  	Frequency int32
    17  	Format    AudioFormat
    18  	Channels  uint8
    19  	Silence   uint8
    20  	Samples   uint16
    21  	Padding   uint16
    22  	Size      uint32
    23  	Callback  AudioCallback
    24  	UserData  unsafe.Pointer
    25  }
    26  
    27  type audioSpec struct {
    28  	Frequency int32
    29  	Format    AudioFormat
    30  	Channels  uint8
    31  	Silence   uint8
    32  	Samples   uint16
    33  	Padding   uint16
    34  	Size      uint32
    35  	Callback  unsafe.Pointer
    36  	UserData  uintptr
    37  }
    38  
    39  type AudioFormat uint16
    40  
    41  const (
    42  	AudioU8     AudioFormat = 0x0008
    43  	AudioS8     AudioFormat = 0x8008
    44  	AudioU16LSB AudioFormat = 0x0010
    45  	AudioS16LSB AudioFormat = 0x8010
    46  	AudioU16MSB AudioFormat = 0x1010
    47  	AudioS16MSB AudioFormat = 0x9010
    48  	AudioU16                = AudioU16LSB
    49  	AudioS16                = AudioS16LSB
    50  	AudioS32LSB AudioFormat = 0x8020
    51  	AudioS32MSB AudioFormat = 0x9020
    52  	AudioS32                = AudioS32LSB
    53  	AudioF32LSB AudioFormat = 0x8120
    54  	AudioF32MSB AudioFormat = 0x9120
    55  	AudioF32                = AudioF32LSB
    56  )
    57  
    58  func GetNumAudioDevices(isCapture bool) int {
    59  	var capture int
    60  	if isCapture {
    61  		capture = 1
    62  	}
    63  	return int(C.SDL_GetNumAudioDevices(C.int(capture)))
    64  }
    65  
    66  func GetAudioDeviceName(device int, isCapture bool) (string, error) {
    67  	var capture int
    68  	if isCapture {
    69  		capture = 1
    70  	}
    71  	ptr := C.SDL_GetAudioDeviceName(C.int(device), C.int(capture))
    72  	if ptr == nil {
    73  		return "", GetError()
    74  	}
    75  	return C.GoString(ptr), nil
    76  }
    77  
    78  func GetNumAudioDrivers() int {
    79  	return int(C.SDL_GetNumAudioDrivers())
    80  }
    81  
    82  func GetAudioDriver(index int) (string, error) {
    83  	nativePtr := C.SDL_GetAudioDriver(C.int(index))
    84  	if nativePtr == nil {
    85  		return "", GetError()
    86  	}
    87  	return C.GoString(nativePtr), nil
    88  }
    89  
    90  type AudioDeviceID int
    91  
    92  var callbackMutex sync.Mutex
    93  var callbackToken uintptr
    94  var callbackFuncs = make(map[uintptr]callback)
    95  
    96  type callback struct {
    97  	Callback AudioCallback
    98  	UserData unsafe.Pointer
    99  }
   100  
   101  //export audioSpecCallback
   102  func audioSpecCallback(userData unsafe.Pointer, stream *uint8, len int32) {
   103  	callbackMutex.Lock()
   104  	f := callbackFuncs[uintptr(userData)]
   105  	callbackMutex.Unlock()
   106  	if f.Callback == nil {
   107  		panic("unknown callback func")
   108  	}
   109  	f.Callback(f.UserData, stream, len)
   110  }
   111  
   112  func OpenAudioDevice(device string, isCapture bool, desired, obtained *AudioSpec, allowedChanges int) (AudioDeviceID, error) {
   113  	nativePtr := C.CString(device)
   114  	defer C.free(unsafe.Pointer(nativePtr))
   115  	var capture int
   116  	if isCapture {
   117  		capture = 1
   118  	}
   119  	_desired := audioSpec{
   120  		Frequency: desired.Frequency,
   121  		Format:    desired.Format,
   122  		Channels:  desired.Channels,
   123  		Silence:   desired.Silence,
   124  		Samples:   desired.Samples,
   125  		Padding:   desired.Padding,
   126  		Size:      desired.Size,
   127  	}
   128  	var i uintptr
   129  	if desired.Callback != nil {
   130  		callbackMutex.Lock()
   131  		callbackToken++
   132  		i = callbackToken
   133  		callbackFuncs[i] = struct {
   134  			Callback AudioCallback
   135  			UserData unsafe.Pointer
   136  		}{
   137  			Callback: desired.Callback,
   138  			UserData: desired.UserData,
   139  		}
   140  		callbackMutex.Unlock()
   141  		_desired.Callback = C.callback
   142  		_desired.UserData = i
   143  	}
   144  	var _obtained audioSpec
   145  	result := AudioDeviceID(C.SDL_OpenAudioDevice(
   146  		nativePtr,
   147  		C.int(capture),
   148  		(*C.SDL_AudioSpec)(unsafe.Pointer(&_desired)),
   149  		(*C.SDL_AudioSpec)(unsafe.Pointer(&_obtained)),
   150  		C.int(allowedChanges),
   151  	))
   152  	if result == 0 {
   153  		return 0, GetError()
   154  	}
   155  	*obtained = AudioSpec{
   156  		Frequency: _obtained.Frequency,
   157  		Format:    _obtained.Format,
   158  		Channels:  _obtained.Channels,
   159  		Silence:   _obtained.Silence,
   160  		Samples:   _obtained.Samples,
   161  		Padding:   _obtained.Padding,
   162  		Size:      _obtained.Size,
   163  	}
   164  	if _obtained.Callback != nil {
   165  		if _obtained.Callback != C.callback {
   166  			panic("invalid callback")
   167  		}
   168  		f, ok := callbackFuncs[_obtained.UserData]
   169  		if !ok {
   170  			panic("unknown callback")
   171  		}
   172  		obtained.Callback = f.Callback
   173  		obtained.UserData = f.UserData
   174  	}
   175  	return result, nil
   176  }
   177  
   178  func CloseAudioDevice(device AudioDeviceID) {
   179  	C.SDL_CloseAudioDevice(C.SDL_AudioDeviceID(device))
   180  }
   181  
   182  func PauseAudioDevice(device AudioDeviceID, pause bool) {
   183  	var pauseOn int
   184  	if pause {
   185  		pauseOn = 1
   186  	}
   187  	C.SDL_PauseAudioDevice(C.SDL_AudioDeviceID(device), C.int(pauseOn))
   188  }
   189  
   190  func QueueAudio(device AudioDeviceID, data unsafe.Pointer, length int) error {
   191  	if C.SDL_QueueAudio(
   192  		C.SDL_AudioDeviceID(device),
   193  		data,
   194  		C.Uint32(length),
   195  	) != 0 {
   196  		return GetError()
   197  	}
   198  	return nil
   199  }
   200  
   201  func LoadWAVRW(ops *RWOps, freeSrc bool, spec *AudioSpec) ([]byte, error) {
   202  	var local reflect.StringHeader
   203  	var free int
   204  	if freeSrc {
   205  		free = 1
   206  	}
   207  	ptr := (*AudioSpec)(unsafe.Pointer(C.SDL_LoadWAV_RW(
   208  		(*C.struct_SDL_RWops)(unsafe.Pointer(ops)),
   209  		C.int(free),
   210  		(*C.struct_SDL_AudioSpec)(unsafe.Pointer(spec)),
   211  		(**C.Uint8)(noescape(unsafe.Pointer(&local.Data))),
   212  		(*C.Uint32)(noescape(unsafe.Pointer(&local.Len))),
   213  	)))
   214  	if ptr == nil {
   215  		return nil, GetError()
   216  	}
   217  	length := uint32(local.Len)
   218  	data := make([]byte, length)
   219  	copy(data, *(*string)(unsafe.Pointer(&local)))
   220  	C.SDL_FreeWAV(*(**C.Uint8)(unsafe.Pointer(&local.Data)))
   221  	return data, nil
   222  }
   223  
   224  type audioStream C.SDL_AudioStream
   225  
   226  type AudioStream interface {
   227  	Put([]byte) error
   228  	Get([]byte) (int, error)
   229  	Available() int
   230  	Flush() error
   231  	Clear()
   232  	Free()
   233  
   234  	io.Writer
   235  	io.Reader
   236  	io.Closer
   237  }
   238  
   239  func (s *audioStream) Put(p []byte) error {
   240  	if C.SDL_AudioStreamPut(
   241  		(*C.SDL_AudioStream)(unsafe.Pointer(s)),
   242  		unsafe.Pointer(&p[0]),
   243  		C.int(len(p)),
   244  	) != 0 {
   245  		return GetError()
   246  	}
   247  	return nil
   248  }
   249  
   250  func (s *audioStream) Get(p []byte) (n int, err error) {
   251  	result := int(C.SDL_AudioStreamGet(
   252  		(*C.SDL_AudioStream)(unsafe.Pointer(s)),
   253  		unsafe.Pointer(&p[0]),
   254  		C.int(len(p)),
   255  	))
   256  	if result == -1 {
   257  		return 0, GetError()
   258  	}
   259  	return result, nil
   260  }
   261  
   262  func (s *audioStream) Available() int {
   263  	return int(C.SDL_AudioStreamAvailable((*C.SDL_AudioStream)(unsafe.Pointer(s))))
   264  }
   265  
   266  func (s *audioStream) Flush() error {
   267  	if C.SDL_AudioStreamFlush((*C.SDL_AudioStream)(unsafe.Pointer(s))) != 0 {
   268  		return GetError()
   269  	}
   270  	return nil
   271  }
   272  
   273  func (s *audioStream) Clear() {
   274  	C.SDL_AudioStreamClear((*C.SDL_AudioStream)(unsafe.Pointer(s)))
   275  }
   276  
   277  func (s *audioStream) Free() {
   278  	C.SDL_FreeAudioStream((*C.SDL_AudioStream)(unsafe.Pointer(s)))
   279  }
   280  
   281  func (s *audioStream) Write(p []byte) (n int, err error) {
   282  	err = s.Put(p)
   283  	return len(p), err
   284  }
   285  
   286  func (s *audioStream) Read(p []byte) (n int, err error) {
   287  	if len(p) == 0 {
   288  		return 0, nil
   289  	}
   290  	if s.Available() == 0 {
   291  		return 0, io.EOF
   292  	}
   293  	return s.Get(p)
   294  }
   295  
   296  func (s *audioStream) Close() error {
   297  	s.Free()
   298  	return nil
   299  }
   300  
   301  func NewAudioStream(srcFormat AudioFormat, srcChannels, srcRate int, dstFormat AudioFormat, dstChannels, dstRate int) (AudioStream, error) {
   302  	ptr := (*audioStream)(unsafe.Pointer(C.SDL_NewAudioStream(
   303  		C.SDL_AudioFormat(srcFormat),
   304  		C.Uint8(srcChannels),
   305  		C.int(srcRate),
   306  		C.SDL_AudioFormat(dstFormat),
   307  		C.Uint8(dstChannels),
   308  		C.int(dstRate),
   309  	)))
   310  	if ptr == nil {
   311  		return nil, GetError()
   312  	}
   313  	return ptr, nil
   314  }