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 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
|
from . import error
# expose constants to python.
AUDIO_U8 = _AUDIO_U8
AUDIO_S8 = _AUDIO_S8
AUDIO_U16LSB = _AUDIO_U16LSB
AUDIO_S16LSB = _AUDIO_S16LSB
AUDIO_U16MSB = _AUDIO_U16MSB
AUDIO_S16MSB = _AUDIO_S16MSB
AUDIO_U16 = _AUDIO_U16
AUDIO_S16 = _AUDIO_S16
AUDIO_S32LSB = _AUDIO_S32LSB
AUDIO_S32MSB = _AUDIO_S32MSB
AUDIO_S32 = _AUDIO_S32
AUDIO_F32LSB = _AUDIO_F32LSB
AUDIO_F32MSB = _AUDIO_F32MSB
AUDIO_F32 = _AUDIO_F32
# So we can get the audio formats as string.
_audio_format_str = {
AUDIO_U8: "AUDIO_U8",
AUDIO_S8: "AUDIO_S8",
AUDIO_U16LSB: "AUDIO_U16LSB",
AUDIO_S16LSB: "AUDIO_S16LSB",
AUDIO_U16MSB: "AUDIO_U16MSB",
AUDIO_S16MSB: "AUDIO_S16MSB",
AUDIO_U16: "AUDIO_U16",
AUDIO_S16: "AUDIO_S16",
AUDIO_S32LSB: "AUDIO_S32LSB",
AUDIO_S32MSB: "AUDIO_S32MSB",
AUDIO_S32: "AUDIO_S32",
AUDIO_F32LSB: "AUDIO_F32LSB",
AUDIO_F32MSB: "AUDIO_F32MSB",
AUDIO_F32: "AUDIO_F32",
}
# for SDL_OpenAudioDevice.
AUDIO_ALLOW_FREQUENCY_CHANGE = _SDL_AUDIO_ALLOW_FREQUENCY_CHANGE
AUDIO_ALLOW_FORMAT_CHANGE = _SDL_AUDIO_ALLOW_FORMAT_CHANGE
AUDIO_ALLOW_CHANNELS_CHANGE = _SDL_AUDIO_ALLOW_CHANNELS_CHANGE
AUDIO_ALLOW_ANY_CHANGE = _SDL_AUDIO_ALLOW_ANY_CHANGE
# https://wiki.libsdl.org/SDL_GetNumAudioDevices
def get_num_audio_devices(iscapture):
""" return the number of audio devices for playback or capture.
:param int iscapture: if 0 return devices available for playback of audio.
If 1 return devices available for capture of audio.
:return: the number of devices available.
:rtype: int
"""
devcount = SDL_GetNumAudioDevices(iscapture);
if devcount == -1:
raise error('Audio system not initialised')
return devcount
# https://wiki.libsdl.org/SDL_GetAudioDeviceName
def get_audio_device_name(index, iscapture):
""" A unique devicename is available for each available audio device.
:param int index: index of the devices from 0 to get_num_audio_devices(iscapture)
:param int iscapture: if 0 return devices available for playback of audio.
If 1 return devices available for capture of audio.
:return: the devicename.
:rtype: bytes
"""
cdef const char * name
name = SDL_GetAudioDeviceName(index, iscapture)
if not name:
raise error()
return name
import traceback
cdef void recording_cb(void* userdata, Uint8* stream, int len) nogil:
""" This is called in a thread made by SDL.
So we need the python GIL to do python stuff.
"""
cdef Uint8 [:] a_memoryview
with gil:
a_memoryview = <Uint8[:len]> stream
try:
# The userdata is the audio device.
# The audio device is needed in some apps
(<object>userdata).callback(<object>userdata, a_memoryview)
except:
traceback.print_exc()
raise
cdef class AudioDevice:
def __cinit__(self):
self._deviceid = 0
self._iscapture = 0
def __dealloc__(self):
if self._deviceid:
SDL_CloseAudioDevice(self._deviceid)
def __init__(self,
devicename,
iscapture,
frequency,
audioformat,
numchannels,
chunksize,
allowed_changes,
callback):
""" An AudioDevice is for sound playback and capture of 'sound cards'.
:param bytes devicename: One of the device names from get_audio_device_name.
If None is passed in, it uses the default audio device.
:param int frequency: Number of samples per second. 44100, 22050, ...
:param int audioformat: AUDIO_F32SYS, AUDIO_F32SYS, AUDIO_U16SYS, AUDIO_S16SYS, ...
:param int numchannels: 2 if stereo, 1 if mono.
:param int chunksize: number of samples buffered.
:param allowed_changes: some drivers don't support all possible requested formats.
So you can tell it which ones yours support.
* AUDIO_ALLOW_FREQUENCY_CHANGE
* AUDIO_ALLOW_FORMAT_CHANGE
* AUDIO_ALLOW_CHANNELS_CHANGE
* AUDIO_ALLOW_ANY_CHANGE
If your application can only handle one specific data format,
pass a zero for allowed_changes and let SDL transparently handle any differences.
:callback: a function which gets called with (audiodevice, memoryview).
memoryview is the audio data.
Use audiodevice.iscapture to see if it is incoming audio or outgoing.
The audiodevice also has the format of the memory.
"""
memset(&self.desired, 0, sizeof(SDL_AudioSpec))
self._iscapture = iscapture
self._callback = callback
self._devicename = devicename
self.desired.freq = frequency;
self.desired.format = audioformat;
self.desired.channels = numchannels;
self.desired.samples = chunksize;
self.desired.callback = <SDL_AudioCallback>recording_cb;
self.desired.userdata = <void*>self
self._deviceid = SDL_OpenAudioDevice(
devicename,
self._iscapture,
&self.desired,
&self.obtained,
allowed_changes
)
if self._deviceid == 0:
raise error()
@property
def iscapture(self):
""" is the AudioDevice for capturing audio?
"""
return self._iscapture
@property
def deviceid(self):
""" deviceid of the audio device relative to the devicename list.
"""
return self._deviceid
@property
def devicename(self):
""" devicename of the audio device from the devicename list.
"""
return self._devicename
@property
def callback(self):
""" called in the sound thread with (audiodevice, memoryview)
"""
return self._callback
@property
def frequency(self):
""" Number of samples per second. 44100, 22050, ...
"""
return self.obtained.freq
@property
def audioformat(self):
""" AUDIO_F32SYS, AUDIO_F32SYS, AUDIO_U16SYS, AUDIO_S16SYS, ...
"""
return self.obtained.format
@property
def numchannels(self):
""" 2 if stereo, 1 if mono.
"""
return self.obtained.channels
@property
def chunksize(self):
""" number of samples buffered.
"""
return self.obtained.samples
def __repr__(self):
ret = "<AudioDevice("
ret += "devicename=%s, " % self.devicename
ret += "iscapture=%s, " % self.iscapture
ret += "frequency=%s, " % self.frequency
ret += "audioformat=%s, " % _audio_format_str[self.audioformat]
ret += "numchannels=%s, " % self.numchannels
ret += "chunksize=%s, " % self.chunksize
ret += ")>"
return ret
def pause(self, int pause_on):
""" Use this to pause and unpause audio playback on this device.
:param int pause_on:
"""
# https://wiki.libsdl.org/SDL_PauseAudioDevice
if self._deviceid:
SDL_PauseAudioDevice(self._deviceid, pause_on)
def close(self):
""" Use this to pause and unpause audio playback on this device.
"""
# https://wiki.libsdl.org/SDL_CloseAudioDevice
if self._deviceid:
SDL_CloseAudioDevice(self._deviceid)
self._deviceid = 0
|