Merge branch 'RtAudioV6' into develop

This commit is contained in:
Anne de Jong 2024-01-19 12:37:16 +01:00
commit 292a9d5938
3 changed files with 121 additions and 71 deletions

View File

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

View File

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

2
third_party/rtaudio vendored

@ -1 +1 @@
Subproject commit 46b01b5b134f33d8ddc3dab76829d4b1350e0522 Subproject commit b4f04903312e0e0efffbe77655172e0f060dc085