Compare commits
6 Commits
fd8366c362
...
08010e56dd
Author | SHA1 | Date | |
---|---|---|---|
08010e56dd | |||
292a9d5938 | |||
373dcfb60f | |||
e8408ab53d | |||
0d152f6c14 | |||
46bef007ca |
|
@ -34,7 +34,7 @@ if(LASP_HAS_RTAUDIO)
|
||||||
target_link_libraries(lasp_device_lib rtaudio)
|
target_link_libraries(lasp_device_lib rtaudio)
|
||||||
endif()
|
endif()
|
||||||
if(LASP_HAS_PORTAUDIO)
|
if(LASP_HAS_PORTAUDIO)
|
||||||
target_link_libraries(lasp_device_lib portaudio)
|
target_link_libraries(lasp_device_lib PortAudio)
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
else()
|
else()
|
||||||
target_link_libraries(lasp_device_lib asound)
|
target_link_libraries(lasp_device_lib asound)
|
||||||
|
|
|
@ -17,37 +17,46 @@ using rte = std::runtime_error;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
using lck = std::scoped_lock<std::mutex>;
|
using lck = std::scoped_lock<std::mutex>;
|
||||||
|
|
||||||
class RtAudioDeviceInfo : public DeviceInfo {
|
const unsigned RTAUDIO_MAX_CHANNELS = 8;
|
||||||
|
|
||||||
|
class RtAudioDeviceInfo : public DeviceInfo
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Specific for the device (Sub-API). Important for the RtAudio
|
* @brief Specific for the device (Sub-API). Important for the RtAudio
|
||||||
* backend, as RtAudio is able to handle different API's.
|
* backend, as RtAudio is able to handle different API's.
|
||||||
*/
|
*/
|
||||||
int _api_devindex;
|
int ID; // Copy of RtAudio::DeviceInfo::ID
|
||||||
virtual std::unique_ptr<DeviceInfo> clone() const override {
|
virtual std::unique_ptr<DeviceInfo> clone() const override
|
||||||
|
{
|
||||||
return std::make_unique<DeviceInfo>(*this);
|
return std::make_unique<DeviceInfo>(*this);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void fillRtAudioDeviceInfo(DeviceInfoList &devinfolist) {
|
void fillRtAudioDeviceInfo(DeviceInfoList &devinfolist)
|
||||||
|
{
|
||||||
DEBUGTRACE_ENTER;
|
DEBUGTRACE_ENTER;
|
||||||
|
|
||||||
vector<RtAudio::Api> apis;
|
vector<RtAudio::Api> apis;
|
||||||
RtAudio::getCompiledApi(apis);
|
RtAudio::getCompiledApi(apis);
|
||||||
|
|
||||||
for (auto api : apis) {
|
for (auto api : apis)
|
||||||
|
{
|
||||||
RtAudio rtaudio(api);
|
RtAudio rtaudio(api);
|
||||||
us count = rtaudio.getDeviceCount();
|
const us count = rtaudio.getDeviceCount();
|
||||||
for (us devno = 0; devno < count; devno++) {
|
|
||||||
|
const auto ids = rtaudio.getDeviceIds();
|
||||||
|
|
||||||
|
for (us i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
us id = ids.at(i);
|
||||||
|
|
||||||
|
RtAudio::DeviceInfo devinfo = rtaudio.getDeviceInfo(id);
|
||||||
|
|
||||||
RtAudio::DeviceInfo devinfo = rtaudio.getDeviceInfo(devno);
|
|
||||||
if (!devinfo.probed) {
|
|
||||||
// Device capabilities not successfully probed. Continue to next
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// "Our device info struct"
|
// "Our device info struct"
|
||||||
RtAudioDeviceInfo d;
|
RtAudioDeviceInfo d;
|
||||||
switch (api) {
|
switch (api)
|
||||||
|
{
|
||||||
case RtAudio::LINUX_ALSA:
|
case RtAudio::LINUX_ALSA:
|
||||||
d.api = rtaudioAlsaApi;
|
d.api = rtaudioAlsaApi;
|
||||||
break;
|
break;
|
||||||
|
@ -70,43 +79,49 @@ void fillRtAudioDeviceInfo(DeviceInfoList &devinfolist) {
|
||||||
}
|
}
|
||||||
|
|
||||||
d.device_name = devinfo.name;
|
d.device_name = devinfo.name;
|
||||||
d._api_devindex = devno;
|
d.ID = id;
|
||||||
|
|
||||||
/// When 48k is available we overwrite the default sample rate with the 48
|
/// When 48k is available we overwrite the default sample rate with the 48
|
||||||
/// kHz value, which is our preffered rate,
|
/// kHz value, which is our preffered rate,
|
||||||
bool rate_48k_found = false;
|
bool rate_48k_found = false;
|
||||||
|
|
||||||
for (us j = 0; j < devinfo.sampleRates.size(); j++) {
|
for (us j = 0; j < devinfo.sampleRates.size(); j++)
|
||||||
|
{
|
||||||
|
|
||||||
us rate_int = devinfo.sampleRates[j];
|
us rate_int = devinfo.sampleRates[j];
|
||||||
|
|
||||||
d.availableSampleRates.push_back((double)rate_int);
|
d.availableSampleRates.push_back((double)rate_int);
|
||||||
|
|
||||||
if (!rate_48k_found) {
|
if (!rate_48k_found)
|
||||||
|
{
|
||||||
|
|
||||||
if (devinfo.preferredSampleRate == rate_int) {
|
if (devinfo.preferredSampleRate == rate_int)
|
||||||
|
{
|
||||||
d.prefSampleRateIndex = j;
|
d.prefSampleRateIndex = j;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rate_int == 48000) {
|
if (rate_int == 48000)
|
||||||
|
{
|
||||||
d.prefSampleRateIndex = j;
|
d.prefSampleRateIndex = j;
|
||||||
rate_48k_found = true;
|
rate_48k_found = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
d.noutchannels = devinfo.outputChannels;
|
d.noutchannels = std::min(devinfo.outputChannels, RTAUDIO_MAX_CHANNELS);
|
||||||
d.ninchannels = devinfo.inputChannels;
|
d.ninchannels = std::min(devinfo.inputChannels, RTAUDIO_MAX_CHANNELS);
|
||||||
|
|
||||||
d.availableInputRanges = {1.0};
|
d.availableInputRanges = {1.0};
|
||||||
d.availableOutputRanges = {1.0};
|
d.availableOutputRanges = {1.0};
|
||||||
|
|
||||||
RtAudioFormat formats = devinfo.nativeFormats;
|
RtAudioFormat formats = devinfo.nativeFormats;
|
||||||
if (formats & RTAUDIO_SINT8) {
|
if (formats & RTAUDIO_SINT8)
|
||||||
|
{
|
||||||
d.availableDataTypes.push_back(
|
d.availableDataTypes.push_back(
|
||||||
DataTypeDescriptor::DataType::dtype_int8);
|
DataTypeDescriptor::DataType::dtype_int8);
|
||||||
}
|
}
|
||||||
if (formats & RTAUDIO_SINT16) {
|
if (formats & RTAUDIO_SINT16)
|
||||||
|
{
|
||||||
d.availableDataTypes.push_back(
|
d.availableDataTypes.push_back(
|
||||||
DataTypeDescriptor::DataType::dtype_int16);
|
DataTypeDescriptor::DataType::dtype_int16);
|
||||||
}
|
}
|
||||||
|
@ -114,15 +129,18 @@ void fillRtAudioDeviceInfo(DeviceInfoList &devinfolist) {
|
||||||
/* d.availableDataTypes.push_back(DataTypeDescriptor::DataType::dtype_int24);
|
/* d.availableDataTypes.push_back(DataTypeDescriptor::DataType::dtype_int24);
|
||||||
*/
|
*/
|
||||||
/* } */
|
/* } */
|
||||||
if (formats & RTAUDIO_SINT32) {
|
if (formats & RTAUDIO_SINT32)
|
||||||
|
{
|
||||||
d.availableDataTypes.push_back(
|
d.availableDataTypes.push_back(
|
||||||
DataTypeDescriptor::DataType::dtype_fl32);
|
DataTypeDescriptor::DataType::dtype_fl32);
|
||||||
}
|
}
|
||||||
if (formats & RTAUDIO_FLOAT64) {
|
if (formats & RTAUDIO_FLOAT64)
|
||||||
|
{
|
||||||
d.availableDataTypes.push_back(
|
d.availableDataTypes.push_back(
|
||||||
DataTypeDescriptor::DataType::dtype_fl64);
|
DataTypeDescriptor::DataType::dtype_fl64);
|
||||||
}
|
}
|
||||||
if (d.availableDataTypes.size() == 0) {
|
if (d.availableDataTypes.size() == 0)
|
||||||
|
{
|
||||||
std::cerr << "RtAudio: No data types found in device!" << endl;
|
std::cerr << "RtAudio: No data types found in device!" << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,9 +158,8 @@ static int mycallback(void *outputBuffer, void *inputBuffer,
|
||||||
unsigned int nFrames, double streamTime,
|
unsigned int nFrames, double streamTime,
|
||||||
RtAudioStreamStatus status, void *userData);
|
RtAudioStreamStatus status, void *userData);
|
||||||
|
|
||||||
static void myerrorcallback(RtAudioError::Type, const string &errorText);
|
class RtAudioDaq : public Daq
|
||||||
|
{
|
||||||
class RtAudioDaq : public Daq {
|
|
||||||
|
|
||||||
RtAudio rtaudio;
|
RtAudio rtaudio;
|
||||||
const us nFramesPerBlock;
|
const us nFramesPerBlock;
|
||||||
|
@ -182,10 +199,11 @@ public:
|
||||||
/// 0. For now, our fix is to shift out the channels we want, and let
|
/// 0. For now, our fix is to shift out the channels we want, and let
|
||||||
/// RtAudio pass on all channels.
|
/// RtAudio pass on all channels.
|
||||||
inParams->firstChannel = 0;
|
inParams->firstChannel = 0;
|
||||||
inParams->nChannels = devinfo_gen.ninchannels;
|
inParams->nChannels = devinfo.ninchannels;
|
||||||
inParams->deviceId = devinfo._api_devindex;
|
inParams->deviceId = devinfo.ID;
|
||||||
|
}
|
||||||
} else {
|
else
|
||||||
|
{
|
||||||
|
|
||||||
outParams = std::make_unique<RtAudio::StreamParameters>();
|
outParams = std::make_unique<RtAudio::StreamParameters>();
|
||||||
|
|
||||||
|
@ -193,8 +211,8 @@ public:
|
||||||
/// 0. For now, our fix is to shift out the channels we want, and let
|
/// 0. For now, our fix is to shift out the channels we want, and let
|
||||||
/// RtAudio pass on all channels.
|
/// RtAudio pass on all channels.
|
||||||
outParams->firstChannel = 0;
|
outParams->firstChannel = 0;
|
||||||
outParams->nChannels = devinfo_gen.noutchannels;
|
outParams->nChannels = devinfo.noutchannels;
|
||||||
outParams->deviceId = devinfo._api_devindex;
|
outParams->deviceId = devinfo.ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
RtAudio::StreamOptions streamoptions;
|
RtAudio::StreamOptions streamoptions;
|
||||||
|
@ -207,7 +225,8 @@ public:
|
||||||
RtAudioFormat format;
|
RtAudioFormat format;
|
||||||
using Dtype = DataTypeDescriptor::DataType;
|
using Dtype = DataTypeDescriptor::DataType;
|
||||||
const Dtype dtype = dataType();
|
const Dtype dtype = dataType();
|
||||||
switch (dtype) {
|
switch (dtype)
|
||||||
|
{
|
||||||
case Dtype::dtype_fl32:
|
case Dtype::dtype_fl32:
|
||||||
DEBUGTRACE_PRINT("Datatype float32");
|
DEBUGTRACE_PRINT("Datatype float32");
|
||||||
format = RTAUDIO_FLOAT32;
|
format = RTAUDIO_FLOAT32;
|
||||||
|
@ -238,36 +257,46 @@ public:
|
||||||
unsigned int nFramesPerBlock_copy = nFramesPerBlock;
|
unsigned int nFramesPerBlock_copy = nFramesPerBlock;
|
||||||
|
|
||||||
// Final step: open the stream.
|
// Final step: open the stream.
|
||||||
rtaudio.openStream(outParams.get(), inParams.get(), format,
|
RtAudioErrorType err = rtaudio.openStream(outParams.get(), inParams.get(), format,
|
||||||
static_cast<us>(samplerate()), &nFramesPerBlock_copy,
|
static_cast<us>(samplerate()), &nFramesPerBlock_copy,
|
||||||
mycallback, (void *)this, &streamoptions,
|
mycallback, (void *)this, &streamoptions);
|
||||||
&myerrorcallback);
|
if (err != RTAUDIO_NO_ERROR)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(string("Error opening stream: ") + rtaudio.getErrorText());
|
||||||
|
}
|
||||||
|
|
||||||
if (nFramesPerBlock_copy != nFramesPerBlock) {
|
if (nFramesPerBlock_copy != nFramesPerBlock)
|
||||||
|
{
|
||||||
throw rte(string("Got different number of frames per block back from RtAudio "
|
throw rte(string("Got different number of frames per block back from RtAudio "
|
||||||
"backend: ") + std::to_string(nFramesPerBlock_copy) + ". I do not know what to do.");
|
"backend: ") +
|
||||||
|
std::to_string(nFramesPerBlock_copy) + ". I do not know what to do.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void start(InDaqCallback inCallback,
|
virtual void start(InDaqCallback inCallback,
|
||||||
OutDaqCallback outCallback) override final {
|
OutDaqCallback outCallback) override final
|
||||||
|
{
|
||||||
|
|
||||||
DEBUGTRACE_ENTER;
|
DEBUGTRACE_ENTER;
|
||||||
|
|
||||||
assert(!monitorOutput);
|
assert(!monitorOutput);
|
||||||
|
|
||||||
if (getStreamStatus().runningOK()) {
|
if (getStreamStatus().runningOK())
|
||||||
|
{
|
||||||
throw rte("Stream already running");
|
throw rte("Stream already running");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logical XOR
|
// Logical XOR
|
||||||
if (inCallback && outCallback) {
|
if (inCallback && outCallback)
|
||||||
|
{
|
||||||
throw rte("Either input or output stream possible for RtAudio. "
|
throw rte("Either input or output stream possible for RtAudio. "
|
||||||
"Stream duplex mode not provided.");
|
"Stream duplex mode not provided.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (neninchannels() > 0) {
|
if (neninchannels() > 0)
|
||||||
if (!inCallback) {
|
{
|
||||||
|
if (!inCallback)
|
||||||
|
{
|
||||||
throw rte(
|
throw rte(
|
||||||
|
|
||||||
"Input callback given, but stream does not provide input data");
|
"Input callback given, but stream does not provide input data");
|
||||||
|
@ -275,8 +304,10 @@ public:
|
||||||
|
|
||||||
_incallback = inCallback;
|
_incallback = inCallback;
|
||||||
}
|
}
|
||||||
if (nenoutchannels() > 0) {
|
if (nenoutchannels() > 0)
|
||||||
if (!outCallback) {
|
{
|
||||||
|
if (!outCallback)
|
||||||
|
{
|
||||||
throw rte(
|
throw rte(
|
||||||
"Output callback given, but stream does not provide output data");
|
"Output callback given, but stream does not provide output data");
|
||||||
}
|
}
|
||||||
|
@ -284,7 +315,11 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the stream. Throws on error.
|
// Start the stream. Throws on error.
|
||||||
rtaudio.startStream();
|
const auto err = rtaudio.startStream();
|
||||||
|
if (err != RTAUDIO_NO_ERROR)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(string("Error starting stream: ") + rtaudio.getErrorText());
|
||||||
|
}
|
||||||
|
|
||||||
// If we are here, we are running without errors.
|
// If we are here, we are running without errors.
|
||||||
StreamStatus status;
|
StreamStatus status;
|
||||||
|
@ -294,10 +329,15 @@ public:
|
||||||
|
|
||||||
StreamStatus getStreamStatus() const override final { return _streamStatus; }
|
StreamStatus getStreamStatus() const override final { return _streamStatus; }
|
||||||
|
|
||||||
void stop() override final {
|
void stop() override final
|
||||||
|
{
|
||||||
DEBUGTRACE_ENTER;
|
DEBUGTRACE_ENTER;
|
||||||
if (getStreamStatus().runningOK()) {
|
if (getStreamStatus().runningOK())
|
||||||
rtaudio.stopStream();
|
{
|
||||||
|
const auto err = rtaudio.stopStream();
|
||||||
|
if(err != RTAUDIO_NO_ERROR) {
|
||||||
|
std::cerr << "Error occured while stopping the stream: " << rtaudio.getErrorText() << endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
StreamStatus s = _streamStatus;
|
StreamStatus s = _streamStatus;
|
||||||
s.isRunning = false;
|
s.isRunning = false;
|
||||||
|
@ -306,14 +346,16 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
int streamCallback(void *outputBuffer, void *inputBuffer,
|
int streamCallback(void *outputBuffer, void *inputBuffer,
|
||||||
unsigned int nFrames, RtAudioStreamStatus status) {
|
unsigned int nFrames, RtAudioStreamStatus status)
|
||||||
|
{
|
||||||
|
|
||||||
DEBUGTRACE_ENTER;
|
DEBUGTRACE_ENTER;
|
||||||
|
|
||||||
using se = StreamStatus::StreamError;
|
using se = StreamStatus::StreamError;
|
||||||
|
|
||||||
int rval = 0;
|
int rval = 0;
|
||||||
auto stopWithError = [&](se e) {
|
auto stopWithError = [&](se e)
|
||||||
|
{
|
||||||
DEBUGTRACE_PRINT("stopWithError");
|
DEBUGTRACE_PRINT("stopWithError");
|
||||||
StreamStatus stat = _streamStatus;
|
StreamStatus stat = _streamStatus;
|
||||||
stat.errorType = e;
|
stat.errorType = e;
|
||||||
|
@ -322,7 +364,8 @@ public:
|
||||||
rval = 1;
|
rval = 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (status) {
|
switch (status)
|
||||||
|
{
|
||||||
case RTAUDIO_INPUT_OVERFLOW:
|
case RTAUDIO_INPUT_OVERFLOW:
|
||||||
stopWithError(se::inputXRun);
|
stopWithError(se::inputXRun);
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -341,14 +384,16 @@ public:
|
||||||
const us neninchannels = this->neninchannels();
|
const us neninchannels = this->neninchannels();
|
||||||
const us nenoutchannels = this->nenoutchannels();
|
const us nenoutchannels = this->nenoutchannels();
|
||||||
const us sw = dtype_descr.sw;
|
const us sw = dtype_descr.sw;
|
||||||
if (nFrames != nFramesPerBlock) {
|
if (nFrames != nFramesPerBlock)
|
||||||
|
{
|
||||||
cerr << "RtAudio backend error: nFrames does not match block size!"
|
cerr << "RtAudio backend error: nFrames does not match block size!"
|
||||||
<< endl;
|
<< endl;
|
||||||
stopWithError(se::logicError);
|
stopWithError(se::logicError);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputBuffer) {
|
if (inputBuffer)
|
||||||
|
{
|
||||||
assert(_incallback);
|
assert(_incallback);
|
||||||
std::vector<byte_t *> ptrs;
|
std::vector<byte_t *> ptrs;
|
||||||
ptrs.reserve(neninchannels);
|
ptrs.reserve(neninchannels);
|
||||||
|
@ -359,8 +404,10 @@ public:
|
||||||
assert(ch_max < ninchannels);
|
assert(ch_max < ninchannels);
|
||||||
|
|
||||||
/// Only pass on the pointers of the channels we want
|
/// Only pass on the pointers of the channels we want
|
||||||
for (us ch = ch_min; ch <= ch_max; ch++) {
|
for (us ch = ch_min; ch <= ch_max; ch++)
|
||||||
if (inchannel_config.at(ch).enabled) {
|
{
|
||||||
|
if (inchannel_config.at(ch).enabled)
|
||||||
|
{
|
||||||
byte_t *ptr =
|
byte_t *ptr =
|
||||||
static_cast<byte_t *>(inputBuffer) + sw * ch * nFramesPerBlock;
|
static_cast<byte_t *>(inputBuffer) + sw * ch * nFramesPerBlock;
|
||||||
ptrs.push_back(ptr);
|
ptrs.push_back(ptr);
|
||||||
|
@ -369,10 +416,11 @@ public:
|
||||||
DaqData d{nFramesPerBlock, neninchannels, dtype};
|
DaqData d{nFramesPerBlock, neninchannels, dtype};
|
||||||
d.copyInFromRaw(ptrs);
|
d.copyInFromRaw(ptrs);
|
||||||
|
|
||||||
_incallback(d);
|
_incallback(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outputBuffer) {
|
if (outputBuffer)
|
||||||
|
{
|
||||||
assert(_outcallback);
|
assert(_outcallback);
|
||||||
std::vector<byte_t *> ptrs;
|
std::vector<byte_t *> ptrs;
|
||||||
ptrs.reserve(nenoutchannels);
|
ptrs.reserve(nenoutchannels);
|
||||||
|
@ -384,8 +432,10 @@ public:
|
||||||
assert(ch_min < noutchannels);
|
assert(ch_min < noutchannels);
|
||||||
assert(ch_max < noutchannels);
|
assert(ch_max < noutchannels);
|
||||||
/// Only pass on the pointers of the channels we want
|
/// Only pass on the pointers of the channels we want
|
||||||
for (us ch = ch_min; ch <= ch_max; ch++) {
|
for (us ch = ch_min; ch <= ch_max; ch++)
|
||||||
if (outchannel_config.at(ch).enabled) {
|
{
|
||||||
|
if (outchannel_config.at(ch).enabled)
|
||||||
|
{
|
||||||
ptrs.push_back(static_cast<byte_t *>(outputBuffer) +
|
ptrs.push_back(static_cast<byte_t *>(outputBuffer) +
|
||||||
sw * ch * nFramesPerBlock);
|
sw * ch * nFramesPerBlock);
|
||||||
}
|
}
|
||||||
|
@ -395,7 +445,8 @@ public:
|
||||||
_outcallback(d);
|
_outcallback(d);
|
||||||
// Copy over the buffer
|
// Copy over the buffer
|
||||||
us j = 0;
|
us j = 0;
|
||||||
for (auto ptr : ptrs) {
|
for (auto ptr : ptrs)
|
||||||
|
{
|
||||||
d.copyToRaw(j, ptr);
|
d.copyToRaw(j, ptr);
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
|
@ -411,17 +462,16 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<Daq> createRtAudioDevice(const DeviceInfo &devinfo,
|
std::unique_ptr<Daq> createRtAudioDevice(const DeviceInfo &devinfo,
|
||||||
const DaqConfiguration &config) {
|
const DaqConfiguration &config)
|
||||||
|
{
|
||||||
return std::make_unique<RtAudioDaq>(devinfo, config);
|
return std::make_unique<RtAudioDaq>(devinfo, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
void myerrorcallback(RtAudioError::Type, const string &errorText) {
|
|
||||||
cerr << "RtAudio backend stream error: " << errorText << endl;
|
|
||||||
}
|
|
||||||
int mycallback(
|
int mycallback(
|
||||||
void *outputBuffer, void *inputBuffer, unsigned int nFrames,
|
void *outputBuffer, void *inputBuffer, unsigned int nFrames,
|
||||||
__attribute__((unused)) double streamTime, // Not used parameter streamTime
|
__attribute__((unused)) double streamTime, // Not used parameter streamTime
|
||||||
RtAudioStreamStatus status, void *userData) {
|
RtAudioStreamStatus status, void *userData)
|
||||||
|
{
|
||||||
|
|
||||||
return static_cast<RtAudioDaq *>(userData)->streamCallback(
|
return static_cast<RtAudioDaq *>(userData)->streamCallback(
|
||||||
outputBuffer, inputBuffer, nFrames, status);
|
outputBuffer, inputBuffer, nFrames, status);
|
||||||
|
|
|
@ -15,9 +15,11 @@ using std::endl;
|
||||||
using std::string;
|
using std::string;
|
||||||
using std::to_string;
|
using std::to_string;
|
||||||
|
|
||||||
inline void throwIfError(PaError e) {
|
inline void throwIfError(PaError e)
|
||||||
|
{
|
||||||
DEBUGTRACE_ENTER;
|
DEBUGTRACE_ENTER;
|
||||||
if (e != paNoError) {
|
if (e != paNoError)
|
||||||
|
{
|
||||||
throw rte(string("PortAudio backend error: ") + Pa_GetErrorText(e));
|
throw rte(string("PortAudio backend error: ") + Pa_GetErrorText(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,21 +27,25 @@ inline void throwIfError(PaError e) {
|
||||||
/**
|
/**
|
||||||
* @brief Device info, plus PortAudio stuff
|
* @brief Device info, plus PortAudio stuff
|
||||||
*/
|
*/
|
||||||
class OurPaDeviceInfo : public DeviceInfo {
|
class OurPaDeviceInfo : public DeviceInfo
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Store instance to PaDeviceInfo.
|
* @brief Store instance to PaDeviceInfo.
|
||||||
*/
|
*/
|
||||||
PaDeviceInfo _paDevInfo;
|
PaDeviceInfo _paDevInfo;
|
||||||
|
|
||||||
virtual std::unique_ptr<DeviceInfo> clone() const override final {
|
virtual std::unique_ptr<DeviceInfo> clone() const override final
|
||||||
|
{
|
||||||
return std::make_unique<OurPaDeviceInfo>(*this);
|
return std::make_unique<OurPaDeviceInfo>(*this);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void fillPortAudioDeviceInfo(DeviceInfoList &devinfolist) {
|
void fillPortAudioDeviceInfo(DeviceInfoList &devinfolist)
|
||||||
|
{
|
||||||
DEBUGTRACE_ENTER;
|
DEBUGTRACE_ENTER;
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
|
|
||||||
PaError err = Pa_Initialize();
|
PaError err = Pa_Initialize();
|
||||||
/// PortAudio says that Pa_Terminate() should not be called whenever there
|
/// PortAudio says that Pa_Terminate() should not be called whenever there
|
||||||
|
@ -47,28 +53,33 @@ void fillPortAudioDeviceInfo(DeviceInfoList &devinfolist) {
|
||||||
/// of PortAudio show.
|
/// of PortAudio show.
|
||||||
throwIfError(err);
|
throwIfError(err);
|
||||||
|
|
||||||
auto fin = gsl::finally([&err] {
|
auto fin = gsl::finally([&err]
|
||||||
|
{
|
||||||
DEBUGTRACE_PRINT("Terminating PortAudio instance");
|
DEBUGTRACE_PRINT("Terminating PortAudio instance");
|
||||||
err = Pa_Terminate();
|
err = Pa_Terminate();
|
||||||
if (err != paNoError) {
|
if (err != paNoError) {
|
||||||
cerr << "Error terminating PortAudio. Do not know what to do." << endl;
|
cerr << "Error terminating PortAudio. Do not know what to do." << endl;
|
||||||
}
|
} });
|
||||||
});
|
|
||||||
|
|
||||||
/* const PaDeviceInfo *deviceInfo; */
|
/* const PaDeviceInfo *deviceInfo; */
|
||||||
const int numDevices = Pa_GetDeviceCount();
|
const int numDevices = Pa_GetDeviceCount();
|
||||||
if (numDevices < 0) {
|
if (numDevices < 0)
|
||||||
|
{
|
||||||
throw rte("PortAudio could not find any devices");
|
throw rte("PortAudio could not find any devices");
|
||||||
}
|
}
|
||||||
for (us i = 0; i < (us)numDevices; i++) {
|
for (us i = 0; i < (us)numDevices; i++)
|
||||||
|
{
|
||||||
/* DEBUGTRACE_PRINT(i); */
|
/* DEBUGTRACE_PRINT(i); */
|
||||||
|
|
||||||
const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(i);
|
const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(i);
|
||||||
if (!deviceInfo) {
|
if (!deviceInfo)
|
||||||
|
{
|
||||||
throw rte("No device info struct returned");
|
throw rte("No device info struct returned");
|
||||||
}
|
}
|
||||||
OurPaDeviceInfo d;
|
OurPaDeviceInfo d;
|
||||||
d._paDevInfo = *deviceInfo;
|
d._paDevInfo = *deviceInfo;
|
||||||
|
// We store the name in d.device_name
|
||||||
|
d._paDevInfo.name = nullptr;
|
||||||
d.api = portaudioApi;
|
d.api = portaudioApi;
|
||||||
d.device_name = deviceInfo->name;
|
d.device_name = deviceInfo->name;
|
||||||
|
|
||||||
|
@ -78,7 +89,7 @@ void fillPortAudioDeviceInfo(DeviceInfoList &devinfolist) {
|
||||||
|
|
||||||
d.prefDataTypeIndex = 2;
|
d.prefDataTypeIndex = 2;
|
||||||
|
|
||||||
d.availableSampleRates = {8000.0, 9600.0, 11025.0, 12000.0, 16000.0,
|
d.availableSampleRates = {8000.0, 9600.0, 11025.0, 12000.0, 16000.0,
|
||||||
22050.0, 24000.0, 32000.0, 44100.0, 48000.0,
|
22050.0, 24000.0, 32000.0, 44100.0, 48000.0,
|
||||||
88200.0, 96000.0, 192000.0};
|
88200.0, 96000.0, 192000.0};
|
||||||
d.prefSampleRateIndex = 9;
|
d.prefSampleRateIndex = 9;
|
||||||
|
@ -87,6 +98,9 @@ void fillPortAudioDeviceInfo(DeviceInfoList &devinfolist) {
|
||||||
d.prefFramesPerBlockIndex = 2;
|
d.prefFramesPerBlockIndex = 2;
|
||||||
|
|
||||||
d.availableInputRanges = {1.0};
|
d.availableInputRanges = {1.0};
|
||||||
|
// d.prefInputRangeIndex = 0; // Constructor-defined
|
||||||
|
d.availableOutputRanges = {1.0};
|
||||||
|
// d.prefOutputRangeIndex = 0; // Constructor-defined
|
||||||
|
|
||||||
d.ninchannels = deviceInfo->maxInputChannels;
|
d.ninchannels = deviceInfo->maxInputChannels;
|
||||||
d.noutchannels = deviceInfo->maxOutputChannels;
|
d.noutchannels = deviceInfo->maxOutputChannels;
|
||||||
|
@ -95,7 +109,8 @@ void fillPortAudioDeviceInfo(DeviceInfoList &devinfolist) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (rte &e) {
|
catch (rte &e)
|
||||||
|
{
|
||||||
cerr << "PortAudio backend error: " << e.what() << std::endl;
|
cerr << "PortAudio backend error: " << e.what() << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -120,7 +135,8 @@ static int rawPaCallback(const void *inputBuffer, void *outputBuffer,
|
||||||
const PaStreamCallbackTimeInfo *timeInfo,
|
const PaStreamCallbackTimeInfo *timeInfo,
|
||||||
PaStreamCallbackFlags statusFlags, void *userData);
|
PaStreamCallbackFlags statusFlags, void *userData);
|
||||||
|
|
||||||
class PortAudioDaq : public Daq {
|
class PortAudioDaq : public Daq
|
||||||
|
{
|
||||||
bool _shouldPaTerminate = false;
|
bool _shouldPaTerminate = false;
|
||||||
PaStream *_stream = nullptr;
|
PaStream *_stream = nullptr;
|
||||||
std::atomic<StreamStatus::StreamError> _streamError =
|
std::atomic<StreamStatus::StreamError> _streamError =
|
||||||
|
@ -157,11 +173,13 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<Daq> createPortAudioDevice(const DeviceInfo &devinfo,
|
std::unique_ptr<Daq> createPortAudioDevice(const DeviceInfo &devinfo,
|
||||||
const DaqConfiguration &config) {
|
const DaqConfiguration &config)
|
||||||
|
{
|
||||||
|
|
||||||
const OurPaDeviceInfo *_info =
|
const OurPaDeviceInfo *_info =
|
||||||
dynamic_cast<const OurPaDeviceInfo *>(&devinfo);
|
dynamic_cast<const OurPaDeviceInfo *>(&devinfo);
|
||||||
if (_info == nullptr) {
|
if (_info == nullptr)
|
||||||
|
{
|
||||||
throw rte("BUG: Could not cast DeviceInfo to OurPaDeviceInfo");
|
throw rte("BUG: Could not cast DeviceInfo to OurPaDeviceInfo");
|
||||||
}
|
}
|
||||||
return std::make_unique<PortAudioDaq>(*_info, config);
|
return std::make_unique<PortAudioDaq>(*_info, config);
|
||||||
|
@ -170,14 +188,16 @@ std::unique_ptr<Daq> createPortAudioDevice(const DeviceInfo &devinfo,
|
||||||
static int rawPaCallback(const void *inputBuffer, void *outputBuffer,
|
static int rawPaCallback(const void *inputBuffer, void *outputBuffer,
|
||||||
unsigned long framesPerBuffer,
|
unsigned long framesPerBuffer,
|
||||||
const PaStreamCallbackTimeInfo *timeInfo,
|
const PaStreamCallbackTimeInfo *timeInfo,
|
||||||
PaStreamCallbackFlags statusFlags, void *userData) {
|
PaStreamCallbackFlags statusFlags, void *userData)
|
||||||
|
{
|
||||||
return static_cast<PortAudioDaq *>(userData)->memberPaCallback(
|
return static_cast<PortAudioDaq *>(userData)->memberPaCallback(
|
||||||
inputBuffer, outputBuffer, framesPerBuffer, timeInfo, statusFlags);
|
inputBuffer, outputBuffer, framesPerBuffer, timeInfo, statusFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
PortAudioDaq::PortAudioDaq(const OurPaDeviceInfo &devinfo_gen,
|
PortAudioDaq::PortAudioDaq(const OurPaDeviceInfo &devinfo_gen,
|
||||||
const DaqConfiguration &config)
|
const DaqConfiguration &config)
|
||||||
: Daq(devinfo_gen, config) {
|
: Daq(devinfo_gen, config)
|
||||||
|
{
|
||||||
|
|
||||||
DEBUGTRACE_ENTER;
|
DEBUGTRACE_ENTER;
|
||||||
PaError err = Pa_Initialize();
|
PaError err = Pa_Initialize();
|
||||||
|
@ -193,23 +213,28 @@ PortAudioDaq::PortAudioDaq(const OurPaDeviceInfo &devinfo_gen,
|
||||||
// Going to find the device in the list. If its there, we have to retrieve
|
// Going to find the device in the list. If its there, we have to retrieve
|
||||||
// the index, as this is required in the PaStreamParameters struct
|
// the index, as this is required in the PaStreamParameters struct
|
||||||
int devindex = -1;
|
int devindex = -1;
|
||||||
for (int i = 0; i < Pa_GetDeviceCount(); i++) {
|
for (int i = 0; i < Pa_GetDeviceCount(); i++)
|
||||||
|
{
|
||||||
|
// DEBUGTRACE_PRINT(i);
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
const PaDeviceInfo *info = Pa_GetDeviceInfo(i);
|
const PaDeviceInfo *info = Pa_GetDeviceInfo(i);
|
||||||
if (!info) {
|
if (!info)
|
||||||
|
{
|
||||||
throw rte("No device structure returned from PortAudio");
|
throw rte("No device structure returned from PortAudio");
|
||||||
}
|
}
|
||||||
ok &= string(info->name) == devinfo_gen._paDevInfo.name;
|
ok &= string(info->name) == devinfo_gen.device_name;
|
||||||
ok &= info->hostApi == devinfo_gen._paDevInfo.hostApi;
|
ok &= info->hostApi == devinfo_gen._paDevInfo.hostApi;
|
||||||
ok &= info->maxInputChannels == devinfo_gen._paDevInfo.maxInputChannels;
|
ok &= info->maxInputChannels == devinfo_gen._paDevInfo.maxInputChannels;
|
||||||
ok &= info->maxOutputChannels == devinfo_gen._paDevInfo.maxOutputChannels;
|
ok &= info->maxOutputChannels == devinfo_gen._paDevInfo.maxOutputChannels;
|
||||||
ok &= info->defaultSampleRate == devinfo_gen._paDevInfo.defaultSampleRate;
|
ok &= info->defaultSampleRate == devinfo_gen._paDevInfo.defaultSampleRate;
|
||||||
|
|
||||||
if (ok) {
|
if (ok)
|
||||||
|
{
|
||||||
devindex = i;
|
devindex = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (devindex < 0) {
|
if (devindex < 0)
|
||||||
|
{
|
||||||
throw rte(string("Device not found: ") + string(devinfo_gen.device_name));
|
throw rte(string("Device not found: ") + string(devinfo_gen.device_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,7 +242,8 @@ PortAudioDaq::PortAudioDaq(const OurPaDeviceInfo &devinfo_gen,
|
||||||
const Dtype dtype = dataType();
|
const Dtype dtype = dataType();
|
||||||
// Sample format is bit flag
|
// Sample format is bit flag
|
||||||
PaSampleFormat format = paNonInterleaved;
|
PaSampleFormat format = paNonInterleaved;
|
||||||
switch (dtype) {
|
switch (dtype)
|
||||||
|
{
|
||||||
case Dtype::dtype_fl32:
|
case Dtype::dtype_fl32:
|
||||||
DEBUGTRACE_PRINT("Datatype float32");
|
DEBUGTRACE_PRINT("Datatype float32");
|
||||||
format |= paFloat32;
|
format |= paFloat32;
|
||||||
|
@ -246,18 +272,20 @@ PortAudioDaq::PortAudioDaq(const OurPaDeviceInfo &devinfo_gen,
|
||||||
std::unique_ptr<PaStreamParameters> instreamParams;
|
std::unique_ptr<PaStreamParameters> instreamParams;
|
||||||
std::unique_ptr<PaStreamParameters> outstreamParams;
|
std::unique_ptr<PaStreamParameters> outstreamParams;
|
||||||
|
|
||||||
if (neninchannels() > 0) {
|
if (neninchannels() > 0)
|
||||||
|
{
|
||||||
instreamParams = std::make_unique<PaStreamParameters>(
|
instreamParams = std::make_unique<PaStreamParameters>(
|
||||||
PaStreamParameters({.device = devindex,
|
PaStreamParameters({.device = devindex,
|
||||||
.channelCount = (int)neninchannels(),
|
.channelCount = (int)getHighestEnabledInChannel() + 1,
|
||||||
.sampleFormat = format,
|
.sampleFormat = format,
|
||||||
.suggestedLatency = framesPerBlock() / samplerate(),
|
.suggestedLatency = framesPerBlock() / samplerate(),
|
||||||
.hostApiSpecificStreamInfo = nullptr}));
|
.hostApiSpecificStreamInfo = nullptr}));
|
||||||
}
|
}
|
||||||
if (nenoutchannels() > 0) {
|
if (nenoutchannels() > 0)
|
||||||
|
{
|
||||||
outstreamParams = std::make_unique<PaStreamParameters>(
|
outstreamParams = std::make_unique<PaStreamParameters>(
|
||||||
PaStreamParameters({.device = devindex,
|
PaStreamParameters({.device = devindex,
|
||||||
.channelCount = (int)nenoutchannels(),
|
.channelCount = (int)getHighestEnabledOutChannel() + 1,
|
||||||
.sampleFormat = format,
|
.sampleFormat = format,
|
||||||
.suggestedLatency = framesPerBlock() / samplerate(),
|
.suggestedLatency = framesPerBlock() / samplerate(),
|
||||||
.hostApiSpecificStreamInfo = nullptr}));
|
.hostApiSpecificStreamInfo = nullptr}));
|
||||||
|
@ -278,21 +306,26 @@ PortAudioDaq::PortAudioDaq(const OurPaDeviceInfo &devinfo_gen,
|
||||||
throwIfError(err);
|
throwIfError(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PortAudioDaq::start(InDaqCallback inCallback, OutDaqCallback outCallback) {
|
void PortAudioDaq::start(InDaqCallback inCallback, OutDaqCallback outCallback)
|
||||||
|
{
|
||||||
DEBUGTRACE_ENTER;
|
DEBUGTRACE_ENTER;
|
||||||
assert(_stream);
|
assert(_stream);
|
||||||
if (Pa_IsStreamActive(_stream)) {
|
if (Pa_IsStreamActive(_stream))
|
||||||
|
{
|
||||||
throw rte("Stream is already running");
|
throw rte("Stream is already running");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logical XOR
|
// Logical XOR
|
||||||
if (inCallback && outCallback) {
|
if (inCallback && outCallback)
|
||||||
|
{
|
||||||
throw rte("Either input or output stream possible for RtAudio. "
|
throw rte("Either input or output stream possible for RtAudio. "
|
||||||
"Stream duplex mode not provided.");
|
"Stream duplex mode not provided.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (neninchannels() > 0) {
|
if (neninchannels() > 0)
|
||||||
if (!inCallback) {
|
{
|
||||||
|
if (!inCallback)
|
||||||
|
{
|
||||||
throw rte(
|
throw rte(
|
||||||
|
|
||||||
"Input callback given, but stream does not provide input data");
|
"Input callback given, but stream does not provide input data");
|
||||||
|
@ -300,8 +333,10 @@ void PortAudioDaq::start(InDaqCallback inCallback, OutDaqCallback outCallback) {
|
||||||
|
|
||||||
_incallback = inCallback;
|
_incallback = inCallback;
|
||||||
}
|
}
|
||||||
if (nenoutchannels() > 0) {
|
if (nenoutchannels() > 0)
|
||||||
if (!outCallback) {
|
{
|
||||||
|
if (!outCallback)
|
||||||
|
{
|
||||||
throw rte(
|
throw rte(
|
||||||
"Output callback given, but stream does not provide output data");
|
"Output callback given, but stream does not provide output data");
|
||||||
}
|
}
|
||||||
|
@ -311,46 +346,57 @@ void PortAudioDaq::start(InDaqCallback inCallback, OutDaqCallback outCallback) {
|
||||||
PaError err = Pa_StartStream(_stream);
|
PaError err = Pa_StartStream(_stream);
|
||||||
throwIfError(err);
|
throwIfError(err);
|
||||||
}
|
}
|
||||||
void PortAudioDaq::stop() {
|
void PortAudioDaq::stop()
|
||||||
|
{
|
||||||
DEBUGTRACE_ENTER;
|
DEBUGTRACE_ENTER;
|
||||||
assert(_stream);
|
assert(_stream);
|
||||||
if (Pa_IsStreamStopped(_stream)) {
|
if (Pa_IsStreamStopped(_stream))
|
||||||
|
{
|
||||||
throw rte("Stream is already stopped");
|
throw rte("Stream is already stopped");
|
||||||
}
|
}
|
||||||
PaError err = Pa_StopStream(_stream);
|
PaError err = Pa_StopStream(_stream);
|
||||||
throwIfError(err);
|
throwIfError(err);
|
||||||
}
|
}
|
||||||
Daq::StreamStatus PortAudioDaq::getStreamStatus() const {
|
Daq::StreamStatus PortAudioDaq::getStreamStatus() const
|
||||||
|
{
|
||||||
Daq::StreamStatus status;
|
Daq::StreamStatus status;
|
||||||
// Copy over atomic flag.
|
// Copy over atomic flag.
|
||||||
status.errorType = _streamError;
|
status.errorType = _streamError;
|
||||||
// Check if stream is still running.
|
// Check if stream is still running.
|
||||||
if (_stream) {
|
if (_stream)
|
||||||
if (Pa_IsStreamActive(_stream)) {
|
{
|
||||||
|
if (Pa_IsStreamActive(_stream))
|
||||||
|
{
|
||||||
status.isRunning = true;
|
status.isRunning = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
PortAudioDaq::~PortAudioDaq() {
|
PortAudioDaq::~PortAudioDaq()
|
||||||
|
{
|
||||||
PaError err;
|
PaError err;
|
||||||
if (_stream) {
|
if (_stream)
|
||||||
if (Pa_IsStreamActive(_stream)) {
|
{
|
||||||
|
if (Pa_IsStreamActive(_stream))
|
||||||
|
{
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
err = Pa_CloseStream(_stream);
|
err = Pa_CloseStream(_stream);
|
||||||
_stream = nullptr;
|
_stream = nullptr;
|
||||||
if (err != paNoError) {
|
if (err != paNoError)
|
||||||
|
{
|
||||||
cerr << "Error closing PortAudio stream. Do not know what to do." << endl;
|
cerr << "Error closing PortAudio stream. Do not know what to do." << endl;
|
||||||
}
|
}
|
||||||
assert(_shouldPaTerminate);
|
assert(_shouldPaTerminate);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_shouldPaTerminate) {
|
if (_shouldPaTerminate)
|
||||||
|
{
|
||||||
err = Pa_Terminate();
|
err = Pa_Terminate();
|
||||||
if (err != paNoError) {
|
if (err != paNoError)
|
||||||
|
{
|
||||||
cerr << "Error terminating PortAudio. Do not know what to do." << endl;
|
cerr << "Error terminating PortAudio. Do not know what to do." << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -358,23 +404,28 @@ PortAudioDaq::~PortAudioDaq() {
|
||||||
int PortAudioDaq::memberPaCallback(const void *inputBuffer, void *outputBuffer,
|
int PortAudioDaq::memberPaCallback(const void *inputBuffer, void *outputBuffer,
|
||||||
unsigned long framesPerBuffer,
|
unsigned long framesPerBuffer,
|
||||||
const PaStreamCallbackTimeInfo *timeInfo,
|
const PaStreamCallbackTimeInfo *timeInfo,
|
||||||
PaStreamCallbackFlags statusFlags) {
|
PaStreamCallbackFlags statusFlags)
|
||||||
|
{
|
||||||
|
|
||||||
DEBUGTRACE_ENTER;
|
DEBUGTRACE_ENTER;
|
||||||
typedef Daq::StreamStatus::StreamError se;
|
typedef Daq::StreamStatus::StreamError se;
|
||||||
if (statusFlags & paPrimingOutput) {
|
if (statusFlags & paPrimingOutput)
|
||||||
|
{
|
||||||
// Initial output buffers generated. So nothing with input yet
|
// Initial output buffers generated. So nothing with input yet
|
||||||
return paContinue;
|
return paContinue;
|
||||||
}
|
}
|
||||||
if ((statusFlags & paInputUnderflow) || (statusFlags & paInputOverflow)) {
|
if ((statusFlags & paInputUnderflow) || (statusFlags & paInputOverflow))
|
||||||
|
{
|
||||||
_streamError = se::inputXRun;
|
_streamError = se::inputXRun;
|
||||||
return paAbort;
|
return paAbort;
|
||||||
}
|
}
|
||||||
if ((statusFlags & paOutputUnderflow) || (statusFlags & paOutputOverflow)) {
|
if ((statusFlags & paOutputUnderflow) || (statusFlags & paOutputOverflow))
|
||||||
|
{
|
||||||
_streamError = se::outputXRun;
|
_streamError = se::outputXRun;
|
||||||
return paAbort;
|
return paAbort;
|
||||||
}
|
}
|
||||||
if (framesPerBuffer != framesPerBlock()) {
|
if (framesPerBuffer != framesPerBlock())
|
||||||
|
{
|
||||||
cerr << "Logic error: expected a block size of: " << framesPerBlock()
|
cerr << "Logic error: expected a block size of: " << framesPerBlock()
|
||||||
<< endl;
|
<< endl;
|
||||||
_streamError = se::logicError;
|
_streamError = se::logicError;
|
||||||
|
@ -386,7 +437,8 @@ int PortAudioDaq::memberPaCallback(const void *inputBuffer, void *outputBuffer,
|
||||||
const auto &dtype_descr = dtypeDescr();
|
const auto &dtype_descr = dtypeDescr();
|
||||||
const auto dtype = dataType();
|
const auto dtype = dataType();
|
||||||
const us sw = dtype_descr.sw;
|
const us sw = dtype_descr.sw;
|
||||||
if (inputBuffer) {
|
if (inputBuffer)
|
||||||
|
{
|
||||||
assert(_incallback);
|
assert(_incallback);
|
||||||
std::vector<byte_t *> ptrs;
|
std::vector<byte_t *> ptrs;
|
||||||
ptrs.reserve(neninchannels);
|
ptrs.reserve(neninchannels);
|
||||||
|
@ -398,8 +450,10 @@ int PortAudioDaq::memberPaCallback(const void *inputBuffer, void *outputBuffer,
|
||||||
|
|
||||||
/// Only pass on the pointers of the channels we want. inputBuffer is
|
/// Only pass on the pointers of the channels we want. inputBuffer is
|
||||||
/// noninterleaved, as specified in PortAudioDaq constructor.
|
/// noninterleaved, as specified in PortAudioDaq constructor.
|
||||||
for (us ch = ch_min; ch <= ch_max; ch++) {
|
for (us ch = ch_min; ch <= ch_max; ch++)
|
||||||
if (inchannel_config.at(ch).enabled) {
|
{
|
||||||
|
if (inchannel_config.at(ch).enabled)
|
||||||
|
{
|
||||||
byte_t *ch_ptr =
|
byte_t *ch_ptr =
|
||||||
reinterpret_cast<byte_t **>(const_cast<void *>(inputBuffer))[ch];
|
reinterpret_cast<byte_t **>(const_cast<void *>(inputBuffer))[ch];
|
||||||
ptrs.push_back(ch_ptr);
|
ptrs.push_back(ch_ptr);
|
||||||
|
@ -411,7 +465,8 @@ int PortAudioDaq::memberPaCallback(const void *inputBuffer, void *outputBuffer,
|
||||||
_incallback(d);
|
_incallback(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outputBuffer) {
|
if (outputBuffer)
|
||||||
|
{
|
||||||
assert(_outcallback);
|
assert(_outcallback);
|
||||||
std::vector<byte_t *> ptrs;
|
std::vector<byte_t *> ptrs;
|
||||||
ptrs.reserve(nenoutchannels);
|
ptrs.reserve(nenoutchannels);
|
||||||
|
@ -423,8 +478,10 @@ int PortAudioDaq::memberPaCallback(const void *inputBuffer, void *outputBuffer,
|
||||||
assert(ch_min < noutchannels);
|
assert(ch_min < noutchannels);
|
||||||
assert(ch_max < noutchannels);
|
assert(ch_max < noutchannels);
|
||||||
/// Only pass on the pointers of the channels we want
|
/// Only pass on the pointers of the channels we want
|
||||||
for (us ch = ch_min; ch <= ch_max; ch++) {
|
for (us ch = ch_min; ch <= ch_max; ch++)
|
||||||
if (outchannel_config.at(ch).enabled) {
|
{
|
||||||
|
if (outchannel_config.at(ch).enabled)
|
||||||
|
{
|
||||||
byte_t *ch_ptr = reinterpret_cast<byte_t **>(outputBuffer)[ch];
|
byte_t *ch_ptr = reinterpret_cast<byte_t **>(outputBuffer)[ch];
|
||||||
ptrs.push_back(ch_ptr);
|
ptrs.push_back(ch_ptr);
|
||||||
}
|
}
|
||||||
|
@ -434,7 +491,8 @@ int PortAudioDaq::memberPaCallback(const void *inputBuffer, void *outputBuffer,
|
||||||
_outcallback(d);
|
_outcallback(d);
|
||||||
// Copy over the buffer
|
// Copy over the buffer
|
||||||
us j = 0;
|
us j = 0;
|
||||||
for (auto ptr : ptrs) {
|
for (auto ptr : ptrs)
|
||||||
|
{
|
||||||
d.copyToRaw(j, ptr);
|
d.copyToRaw(j, ptr);
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,23 +22,37 @@ classifiers = [
|
||||||
]
|
]
|
||||||
urls = { "Documentation" = "https://lasp.ascee.nl" }
|
urls = { "Documentation" = "https://lasp.ascee.nl" }
|
||||||
|
|
||||||
dependencies = ["scipy", "numpy", "matplotlib>=3.7.2", "appdirs",
|
dependencies = [
|
||||||
"dataclasses_json", "h5py"]
|
"scipy",
|
||||||
|
"numpy",
|
||||||
|
"matplotlib>=3.7.2",
|
||||||
|
"appdirs",
|
||||||
|
"dataclasses_json",
|
||||||
|
"h5py",
|
||||||
|
]
|
||||||
|
|
||||||
[build-system] # How pip and other frontends should build this project
|
[build-system] # How pip and other frontends should build this project
|
||||||
requires = ["py-build-cmake~=0.1.8", "pybind11" ]
|
requires = ["py-build-cmake~=0.1.8", "pybind11"]
|
||||||
build-backend = "py_build_cmake.build"
|
build-backend = "py_build_cmake.build"
|
||||||
|
|
||||||
[tool.py-build-cmake.module] # Where to find the Python module to package
|
[tool.py-build-cmake.module] # Where to find the Python module to package
|
||||||
directory = "python_src"
|
directory = "python_src"
|
||||||
|
|
||||||
[tool.py-build-cmake.sdist] # What to include in source distributions
|
[tool.py-build-cmake.sdist] # What to include in source distributions
|
||||||
include = ["CMakeLists.txt", "cmake", "cpp_src", "python_src", "img", "scripts",
|
include = [
|
||||||
"third_party"]
|
"CMakeLists.txt",
|
||||||
|
"cmake",
|
||||||
|
"cpp_src",
|
||||||
|
"python_src",
|
||||||
|
"img",
|
||||||
|
"scripts",
|
||||||
|
"third_party",
|
||||||
|
]
|
||||||
|
|
||||||
[tool.py-build-cmake.cmake] # How to build the CMake project
|
[tool.py-build-cmake.cmake] # How to build the CMake project
|
||||||
build_type = "Release"
|
build_type = "Release"
|
||||||
source_path = "."
|
source_path = "."
|
||||||
|
options = { "LASP_HAS_PORTAUDIO" = "ON", "LASP_HAS_RTAUDIO" = "OFF" }
|
||||||
build_args = ["-j12"]
|
build_args = ["-j12"]
|
||||||
install_components = ["python_modules"]
|
install_components = ["python_modules"]
|
||||||
|
|
||||||
|
|
|
@ -91,6 +91,9 @@ class MeasurementType(Enum):
|
||||||
# Measurement serves as impedance tube calibration (long tube case)
|
# Measurement serves as impedance tube calibration (long tube case)
|
||||||
muZCalLong = 1 << 3
|
muZCalLong = 1 << 3
|
||||||
|
|
||||||
|
# Series impedance reference
|
||||||
|
muZSeriesImpedanceRef = 1 << 4
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
match self:
|
match self:
|
||||||
case MeasurementType.NotSpecific: return '-'
|
case MeasurementType.NotSpecific: return '-'
|
||||||
|
@ -98,6 +101,7 @@ class MeasurementType(Enum):
|
||||||
case MeasurementType.CALGeneral: return 'General calibration'
|
case MeasurementType.CALGeneral: return 'General calibration'
|
||||||
case MeasurementType.muZCalShort: return 'ASCEE μZ short length calibration'
|
case MeasurementType.muZCalShort: return 'ASCEE μZ short length calibration'
|
||||||
case MeasurementType.muZCalLong: return 'ASCEE μZ long length calibration'
|
case MeasurementType.muZCalLong: return 'ASCEE μZ long length calibration'
|
||||||
|
case MeasurementType.muZSeriesImpedanceRef: return 'ASCEE μZ series impedance empty reference'
|
||||||
case _: raise ValueError("Not a MeasurementType")
|
case _: raise ValueError("Not a MeasurementType")
|
||||||
|
|
||||||
|
|
||||||
|
|
2
third_party/portaudio
vendored
2
third_party/portaudio
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit cb8d3dcbc6fa74c67f3e236be89b12d5630da141
|
Subproject commit daaf637f6f9fce670031221abfd7dfde92e5cce3
|
Loading…
Reference in New Issue
Block a user