File: audio.py

package info (click to toggle)
psychtoolbox-3 3.0.19.14.dfsg1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 86,796 kB
  • sloc: ansic: 176,245; cpp: 20,103; objc: 5,393; sh: 2,753; python: 1,397; php: 384; makefile: 193; java: 113
file content (289 lines) | stat: -rw-r--r-- 9,815 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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
"""Converts Psychtoolbox formatted functions into prettier python-styling

RIGHT NOW THIS IS A WORK IN PROGRESS. ALTHOUGH THE PsychPortAudio LIB HAS BEEN
TESTED BY MARIO, this additional wrapper is under development. Use with extreme
caution only for development purposes right now.


e.g.::

    from psychtoolbox import PsychPortAudio
    pahandle = PsychPortAudio('Open', [], [], [0], Fs, 2)
    PsychPortAudio('FillBuffer', pahandle, stereowav)
    PsychPortAudio('Start', pahandle)

becomes::

    from psychtoolbox import audio
    stream = audio.Stream([], [], [0], Fs, 2)
    stream.fill_buffer(stereowav)
    stream.start()

(c) 2018 Jon Peirce - Licensed under MIT license.
"""

"""
Function summary from PsychPortAudio for not-yet-implemented functions:

pahandle = PsychPortAudio('OpenSlave', pamaster [, mode][, channels]
    [, selectchannels]);

PsychPortAudio('DeleteBuffer'[, bufferhandle] [, waitmode]);
PsychPortAudio('RefillBuffer', pahandle [, bufferhandle=0], bufferdata
    [, startIndex=0]);
PsychPortAudio('SetLoop', pahandle[, startSample=0][, endSample=max]
    [, UnitIsSeconds=0]);

PsychPortAudio('UseSchedule', pahandle, enableSchedule [, maxSize = 128]);
[success, freeslots] = PsychPortAudio('AddToSchedule', pahandle
    [, bufferHandle=0][, repetitions=1][, startSample=0][, endSample=max]
    [, UnitIsSeconds=0][, specialFlags=0]);
startTime = PsychPortAudio('RescheduleStart', pahandle, when [, waitForStart=0]
    [, repetitions] [, stopTime]);
"""
import atexit
from . import PsychPortAudio

def get_version_info():
    return PsychPortAudio('Version')


def verbosity(level=None):
    """Gets or sets the verbosity level (integer 1-5).

    If no level is provided the current level of verbosity is returned.
    If a new level is provided the old level setting is returned"""
    oldlevel = PsychPortAudio('Verbosity', level)


def get_open_device_count():
    return PsychPortAudio('GetOpenDeviceCount')


def get_devices(device_type=None, device_index=None):
    """Get a list of devices on the current system, optionally of certain types

    :param device_type:
    :param device_index:
    :return:
    """
    return PsychPortAudio('GetDevices', device_type, device_index)


def tune_engine(yield_interval, mutex_enable, lock_to_core1,
                audioserver_autosuspend):
    """Sets values for various advanced tuning parameters for the audio engine

    :param yield_interval:
    :param mutex_enable:
    :param lock_to_core1:
    :param audioserver_autosuspend:
    :return: the 4 values in their final state
    """
    return PsychPortAudio('EngineTunables', yield_interval, mutex_enable,
                          lock_to_core1, audioserver_autosuspend)


class Stream:
    """
    Creates a Psychtoolbox Stream with the given settings.
    See also http://psychtoolbox.org/docs/PsychPortAudio-Open
    """
    def __init__(self, device_id=[], mode=[], latency_class=[0],
                 freq=48000, channels=2,
                 buffer_size=[], suggested_latency=[], select_channels=[],
                 flags=0):
        # PsychPortAudio('Open', [], [], [0], Fs, 2);
        self.handle = PsychPortAudio('Open', device_id, mode,
                                     latency_class,
                                     freq, channels, buffer_size,
                                     suggested_latency, select_channels,
                                     flags)
        self._closed=False
        atexit.register(self.close)

    def start(self, repetitions=1, when=0, wait_for_start=0,
              stop_time=None, resume=0):
        """Start the stream for a given number of repetitions, potentially with
        a delayed start.

        :param repetitions:
        :param when:
        :param wait_for_start:
        :param stop_time:
        :param resume:
        :return:
        """
        start_time = PsychPortAudio('Start', self.handle, repetitions, when,
                                    wait_for_start, stop_time, resume)
        return start_time

    def stop(self, wait_for_end_playback=0, block_until_stopped=1,
             repetitions=None, stopTime=None):
        """
        :param wait_for_end_playback:
        :param block_until_stopped:
        :param repetitions:
        :param stopTime:
        :return: startTime, endPositionSecs, xruns, estStopTime
        """
        return PsychPortAudio('Stop', self.handle,
                              wait_for_end_playback, block_until_stopped,
                              repetitions, stopTime)

    def close(self):
        """Close the current stream"""
        if getattr(self, '_closed', False): # if we're already closed don't try again
            return
        try:
            PsychPortAudio('Close', self.handle)
            self._closed = True
        except Exception as err:
            if "Invalid audio device handle" in str(err):
                # this most likely means the stream is closed already
                return
            else:
                raise err

    def setVolume(self, masterVolume=None, channelVolumes=None):
        """As well as being able to use the volume_master and volume_channels
        attributes, you can use this function to set both master and channel
        volumes at the same time"""
        PsychPortAudio('Volume', self.handle, masterVolume, channelVolumes)

    @property
    def status(self):
        """The status of this portaudio stream"""
        if self._closed:
            return -1
        return PsychPortAudio('GetStatus', self.handle)

    @property
    def volume(self):
        """A property allowing you to get/set the stream's master volume

        see also:
            volume_channels
        """
        return PsychPortAudio('Volume', self.handle)[0]

    @volume.setter
    def volume(self, volume):
        PsychPortAudio('Volume', self.handle, volume, None)

    @property
    def volume_channels(self):
        """A property allowing you to get/set the volume independently in each
        channel

        see also:
            volume_master
        """
        return PsychPortAudio('Volume', self.handle)[1]

    @volume_channels.setter
    def volume_channels(self, volumes):
        PsychPortAudio('Volume', self.handle, None, volumes)

    @property
    def run_mode(self):
        """A property to get or set the stream's current run_mode"""
        return PsychPortAudio('RunMode', self.handle)

    @run_mode.setter
    def run_mode(self, mode):
        PsychPortAudio('RunMode', self.handle, mode)

    @property
    def latency_bias(self):
        """A property to get/set the stream's latency bias"""
        return PsychPortAudio('LatencyBias', self.handle)

    @latency_bias.setter
    def latency_bias(self, secs):
        PsychPortAudio('LatencyBias', self.handle, secs)

    @property
    def op_mode(self):
        """Equivalent to PsychPortAudio('SetOpMode', op_mode)"""
        return PsychPortAudio('SetOpMode', self.handle)

    @op_mode.setter
    def op_mode(self, op_mode):
        PsychPortAudio('SetOpMode', self.handle, op_mode)

    def get_audio_data(self, secs_allocate=None,
                       min_secs=None, max_secs=None, single_type=1):
        """Get audio data from the port audio stream (e.g. from the mic)

        :param secs_allocate:
        :param min_secs: minimum seconds returned
        :param max_secs: maximum seconds returned
        :param single_type:
        :return: (audiodata, absrecposition, overflow, cstarttime)
        """
        return PsychPortAudio('GetAudioData', self.handle,
                              secs_allocate, min_secs, max_secs,
                              single_type)

    def fill_buffer(self, data):
        """Create a new buffer and fill with audio data to be played when
        stream starts"""
        buffer = Buffer(stream=self, data=data)
        return buffer

    def __del__(self):
        self.close()


class Buffer:
    """A buffer allows us to pre-fill a Stream or a Slave with data ready
    to be played. It can be created and filled in a single operation or can
    be created and then filled in two steps."""
    def __init__(self, stream, data=None):
        self.stream = stream
        if data is None:
            self.handle = PsychPortAudio('CreateBuffer', self.stream.handle,
                                         data)
        else:
            self.handle = PsychPortAudio('FillBuffer', self.stream.handle,
                                         data)

    def fill_buffer(self, data, streaming=0, startIndex='append'):
        """Fill a buffer that has already been created"""
        return PsychPortAudio('FillBuffer', self.stream.handle,
                              data, streaming, startIndex)



class Slave(Stream):
    """In PsychPortAudio, a slave is a virtual stream, that has the same
    properties as Stream but multiple Slaves can be combined in a single
    Stream. In this sense slaves are like tracks that can have their own
    buffers and present a sound each that will be mixed by PsychPortAudio.

    Note that the master Stream set up for the Slave must have been created
    with mode=8
    """
    def __init__(self, stream, mode=[1], data=None,
                 channels=None, select_channels=None,
                 volume=None,
                 ):
        """

        Parameters
        ----------
        stream
        mode
        data
        channels
        select_channels
        """
        self._closed=False
        self.handle = PsychPortAudio('OpenSlave',
                                     stream, mode, channels, select_channels)
        atexit.register(self.close)
        if volume is not None:
            PsychPortAudio('Volume', self.handle, volume)
        if data is not None:
            self.fill_buffer(data)