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)
|