File: audio.pyx

package info (click to toggle)
pygame 1.9.6%2Bdfsg-4
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 12,060 kB
  • sloc: ansic: 59,765; python: 31,220; objc: 334; makefile: 57; cpp: 25
file content (233 lines) | stat: -rw-r--r-- 7,510 bytes parent folder | download
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