Output data for device now directly written to buffer. Bugfix in interleaved/deinterleaved creation of Numpy array from data
This commit is contained in:
parent
705f77858d
commit
dcb861a6ef
@ -26,7 +26,7 @@ cdef extern from "RtAudio.h" nogil:
|
|||||||
THREAD_ERROR
|
THREAD_ERROR
|
||||||
|
|
||||||
ctypedef unsigned long RtAudioStreamFlags
|
ctypedef unsigned long RtAudioStreamFlags
|
||||||
RtAudioStreamFlags RT_AUDIO_NONINTERLEAVED
|
RtAudioStreamFlags RTAUDIO_NONINTERLEAVED
|
||||||
RtAudioStreamFlags RTAUDIO_MINIMIZE_LATENCY
|
RtAudioStreamFlags RTAUDIO_MINIMIZE_LATENCY
|
||||||
RtAudioStreamFlags RTAUDIO_HOG_DEVICE
|
RtAudioStreamFlags RTAUDIO_HOG_DEVICE
|
||||||
RtAudioStreamFlags RTAUDIO_ALSA_USE_DEFAULT
|
RtAudioStreamFlags RTAUDIO_ALSA_USE_DEFAULT
|
||||||
@ -144,10 +144,12 @@ cdef object fromBufferToNPYNoCopy(
|
|||||||
void* buf,
|
void* buf,
|
||||||
size_t nchannels,
|
size_t nchannels,
|
||||||
size_t nframes):
|
size_t nframes):
|
||||||
cdef cnp.npy_intp[2] dims = [nchannels, nframes]
|
cdef cnp.npy_intp[2] dims = [nframes, nchannels]
|
||||||
|
|
||||||
|
# Interleaved data is C-style contiguous. Therefore, we can directly use
|
||||||
|
# SimpleNewFromData()
|
||||||
array = cnp.PyArray_SimpleNewFromData(2, &dims[0], buffer_format_type,
|
array = cnp.PyArray_SimpleNewFromData(2, &dims[0], buffer_format_type,
|
||||||
buf).transpose()
|
buf)
|
||||||
|
|
||||||
return array
|
return array
|
||||||
|
|
||||||
@ -160,8 +162,8 @@ cdef void fromNPYToBuffer(cnp.ndarray arr,
|
|||||||
memcpy(buf, arr.data, arr.size*arr.itemsize)
|
memcpy(buf, arr.data, arr.size*arr.itemsize)
|
||||||
|
|
||||||
|
|
||||||
cdef int audioCallback(void* outputBuffer,
|
cdef int audioCallback(void* outputbuffer,
|
||||||
void* inputBuffer,
|
void* inputbuffer,
|
||||||
unsigned int nFrames,
|
unsigned int nFrames,
|
||||||
double streamTime,
|
double streamTime,
|
||||||
RtAudioStreamStatus status,
|
RtAudioStreamStatus status,
|
||||||
@ -185,41 +187,42 @@ cdef int audioCallback(void* outputBuffer,
|
|||||||
|
|
||||||
# Obtain stream information
|
# Obtain stream information
|
||||||
npy_input = None
|
npy_input = None
|
||||||
|
npy_output = None
|
||||||
if stream.hasInput:
|
if stream.hasInput:
|
||||||
try:
|
try:
|
||||||
assert inputBuffer != NULL
|
assert inputbuffer != NULL
|
||||||
npy_format = _formats_rtkey[stream.sampleformat][2]
|
npy_format = _formats_rtkey[stream.sampleformat][2]
|
||||||
npy_input = fromBufferToNPYNoCopy(
|
npy_input = fromBufferToNPYNoCopy(
|
||||||
npy_format,
|
npy_format,
|
||||||
inputBuffer,
|
inputbuffer,
|
||||||
stream.inputParams.nChannels,
|
stream.inputParams.nChannels,
|
||||||
nFrames)
|
nFrames)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print('Exception in Cython callback: ', str(e))
|
print('exception in cython callback: ', str(e))
|
||||||
|
|
||||||
|
if stream.hasOutput:
|
||||||
|
try:
|
||||||
|
assert outputbuffer != NULL
|
||||||
|
npy_format = _formats_rtkey[stream.sampleformat][2]
|
||||||
|
npy_output = fromBufferToNPYNoCopy(
|
||||||
|
npy_format,
|
||||||
|
outputbuffer,
|
||||||
|
stream.outputParams.nChannels,
|
||||||
|
nFrames)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print('exception in cython callback: ', str(e))
|
||||||
try:
|
try:
|
||||||
npy_output, rval = stream.pyCallback(npy_input,
|
rval = stream.pyCallback(npy_input,
|
||||||
nFrames,
|
npy_output,
|
||||||
streamTime)
|
nFrames,
|
||||||
|
streamTime)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print('Exception in Python callback: ', str(e))
|
print('Exception in Python callback: ', str(e))
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
return rval
|
||||||
if stream.hasOutput:
|
|
||||||
if npy_output is None:
|
|
||||||
print('No output buffer given!')
|
|
||||||
return 1
|
|
||||||
IF LASP_DEBUG_CYTHON:
|
|
||||||
try:
|
|
||||||
assert outputBuffer != NULL, "Bug: RtAudio does not give output buffer!"
|
|
||||||
assert npy_output.shape[0] == stream.outputParams.nChannels, "Bug: channel mismatch in output buffer!"
|
|
||||||
assert npy_output.shape[1] == nFrames, "Bug: frame mismatch in output buffer!"
|
|
||||||
assert npy_output.itemsize == stream.sampleSize, "Bug: invalid sample type in output buffer!"
|
|
||||||
except AssertionError as e:
|
|
||||||
print(e)
|
|
||||||
fromNPYToBuffer(npy_output, outputBuffer)
|
|
||||||
return rval
|
|
||||||
|
|
||||||
cdef void errorCallback(RtAudioError.Type _type,const string& errortxt) nogil:
|
cdef void errorCallback(RtAudioError.Type _type,const string& errortxt) nogil:
|
||||||
pass
|
pass
|
||||||
|
@ -67,7 +67,8 @@ class AvStream:
|
|||||||
|
|
||||||
if daqconfig.duplex_mode or avtype == AvType.audio_output:
|
if daqconfig.duplex_mode or avtype == AvType.audio_output:
|
||||||
rtaudio_outputparams = {'deviceid': device.index,
|
rtaudio_outputparams = {'deviceid': device.index,
|
||||||
'nchannels': device.outputchannels,
|
# TODO: Add option to specify the number of output channels to use
|
||||||
|
'nchannels': 1, #device.outputchannels,
|
||||||
'firstchannel': 0}
|
'firstchannel': 0}
|
||||||
self.sampleformat = daqconfig.en_output_sample_format
|
self.sampleformat = daqconfig.en_output_sample_format
|
||||||
self.samplerate = int(daqconfig.en_output_rate)
|
self.samplerate = int(daqconfig.en_output_rate)
|
||||||
@ -191,24 +192,23 @@ class AvStream:
|
|||||||
cap.release()
|
cap.release()
|
||||||
print('stopped videothread')
|
print('stopped videothread')
|
||||||
|
|
||||||
def _audioCallback(self, indata, nframes, streamtime):
|
def _audioCallback(self, indata, outdata, nframes, streamtime):
|
||||||
"""
|
"""
|
||||||
This is called (from a separate thread) for each audio block.
|
This is called (from a separate thread) for each audio block.
|
||||||
"""
|
"""
|
||||||
self._aframectr += 1
|
self._aframectr += 1
|
||||||
output_signal = None
|
|
||||||
with self._callbacklock:
|
with self._callbacklock:
|
||||||
for cb in self._callbacks[AvType.audio_input]:
|
for cb in self._callbacks[AvType.audio_input]:
|
||||||
try:
|
try:
|
||||||
cb(indata, self._aframectr())
|
cb(indata, outdata, self._aframectr())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
for cb in self._callbacks[AvType.audio_output]:
|
for cb in self._callbacks[AvType.audio_output]:
|
||||||
try:
|
try:
|
||||||
output_signal = cb(indata, self._aframectr())
|
cb(indata, outdata, self._aframectr())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
return output_signal, 0 if self._running else 1
|
return 0 if self._running else 1
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self._running <<= False
|
self._running <<= False
|
||||||
|
@ -131,7 +131,7 @@ class Recording:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _aCallback(self, frames, aframe):
|
def _aCallback(self, indata, outdata, aframe):
|
||||||
|
|
||||||
curT = self._aframeno()*self.blocksize/self.samplerate
|
curT = self._aframeno()*self.blocksize/self.samplerate
|
||||||
recstatus = RecordStatus(
|
recstatus = RecordStatus(
|
||||||
@ -158,7 +158,7 @@ class Recording:
|
|||||||
return
|
return
|
||||||
|
|
||||||
self._ad.resize(self._aframeno()+1, axis=0)
|
self._ad.resize(self._aframeno()+1, axis=0)
|
||||||
self._ad[self._aframeno(), :, :] = frames
|
self._ad[self._aframeno(), :, :] = indata
|
||||||
self._aframeno += 1
|
self._aframeno += 1
|
||||||
|
|
||||||
def _vCallback(self, frame, framectr):
|
def _vCallback(self, frame, framectr):
|
||||||
@ -168,10 +168,3 @@ class Recording:
|
|||||||
self._vd[vframeno, :, :] = frame
|
self._vd[vframeno, :, :] = frame
|
||||||
self._vframeno += 1
|
self._vframeno += 1
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
stream = AvStream()
|
|
||||||
rec = Recording('test', stream, 5)
|
|
||||||
with rec(wait=True):
|
|
||||||
sleep
|
|
||||||
rec.start()
|
|
||||||
|
66
scripts/lasp_siggen
Executable file
66
scripts/lasp_siggen
Executable file
@ -0,0 +1,66 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
import argparse
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Play a sine wave'
|
||||||
|
)
|
||||||
|
device_help = 'DAQ Device to play to'
|
||||||
|
parser.add_argument('--device', '-d', help=device_help, type=str,
|
||||||
|
default='Default')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
from lasp.lasp_avstream import AvStream, AvType
|
||||||
|
from lasp.device import DAQConfiguration, RtAudio
|
||||||
|
|
||||||
|
config = DAQConfiguration.loadConfigs()[args.device]
|
||||||
|
|
||||||
|
rtaudio = RtAudio()
|
||||||
|
count = rtaudio.getDeviceCount()
|
||||||
|
devices = [rtaudio.getDeviceInfo(i) for i in range(count)]
|
||||||
|
|
||||||
|
output_devices = {}
|
||||||
|
for device in devices:
|
||||||
|
if device.outputchannels >= 0:
|
||||||
|
output_devices[device.name] = device
|
||||||
|
|
||||||
|
try:
|
||||||
|
output_device = output_devices[config.output_device_name]
|
||||||
|
except KeyError:
|
||||||
|
raise RuntimeError(f'output device {config.output_device_name} not available')
|
||||||
|
|
||||||
|
samplerate = int(config.en_output_rate)
|
||||||
|
stream = AvStream(output_device,
|
||||||
|
AvType.audio_output,
|
||||||
|
config)
|
||||||
|
|
||||||
|
# freq = 440.
|
||||||
|
freq = 1000.
|
||||||
|
omg = 2*np.pi*freq
|
||||||
|
|
||||||
|
|
||||||
|
def mycallback(indata, outdata, blockctr):
|
||||||
|
frames = outdata.shape[0]
|
||||||
|
nchannels = outdata.shape[1]
|
||||||
|
# nchannels = 1
|
||||||
|
streamtime = blockctr*frames/samplerate
|
||||||
|
t = np.linspace(streamtime, streamtime + frames/samplerate,
|
||||||
|
frames)[np.newaxis, :]
|
||||||
|
outp = 0.01*np.sin(omg*t)
|
||||||
|
for i in range(nchannels):
|
||||||
|
outdata[:,i] = ((2**16-1)*outp).astype(np.int16)
|
||||||
|
|
||||||
|
stream.addCallback(mycallback, AvType.audio_output)
|
||||||
|
stream.start()
|
||||||
|
|
||||||
|
input()
|
||||||
|
|
||||||
|
print('Stopping stream...')
|
||||||
|
stream.stop()
|
||||||
|
|
||||||
|
print('Stream stopped')
|
||||||
|
print('Closing stream...')
|
||||||
|
stream.close()
|
||||||
|
print('Stream closed')
|
Loading…
Reference in New Issue
Block a user