Merge branch 'RtAudioV6' into develop
This commit is contained in:
commit
292a9d5938
@ -34,7 +34,7 @@ if(LASP_HAS_RTAUDIO)
|
||||
target_link_libraries(lasp_device_lib rtaudio)
|
||||
endif()
|
||||
if(LASP_HAS_PORTAUDIO)
|
||||
target_link_libraries(lasp_device_lib portaudio)
|
||||
target_link_libraries(lasp_device_lib PortAudio)
|
||||
if(WIN32)
|
||||
else()
|
||||
target_link_libraries(lasp_device_lib asound)
|
||||
|
@ -17,37 +17,46 @@ using rte = std::runtime_error;
|
||||
using std::vector;
|
||||
using lck = std::scoped_lock<std::mutex>;
|
||||
|
||||
class RtAudioDeviceInfo : public DeviceInfo {
|
||||
const unsigned RTAUDIO_MAX_CHANNELS = 8;
|
||||
|
||||
class RtAudioDeviceInfo : public DeviceInfo
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Specific for the device (Sub-API). Important for the RtAudio
|
||||
* backend, as RtAudio is able to handle different API's.
|
||||
*/
|
||||
int _api_devindex;
|
||||
virtual std::unique_ptr<DeviceInfo> clone() const override {
|
||||
int ID; // Copy of RtAudio::DeviceInfo::ID
|
||||
virtual std::unique_ptr<DeviceInfo> clone() const override
|
||||
{
|
||||
return std::make_unique<DeviceInfo>(*this);
|
||||
}
|
||||
};
|
||||
|
||||
void fillRtAudioDeviceInfo(DeviceInfoList &devinfolist) {
|
||||
void fillRtAudioDeviceInfo(DeviceInfoList &devinfolist)
|
||||
{
|
||||
DEBUGTRACE_ENTER;
|
||||
|
||||
vector<RtAudio::Api> apis;
|
||||
RtAudio::getCompiledApi(apis);
|
||||
|
||||
for (auto api : apis) {
|
||||
for (auto api : apis)
|
||||
{
|
||||
RtAudio rtaudio(api);
|
||||
us count = rtaudio.getDeviceCount();
|
||||
for (us devno = 0; devno < count; devno++) {
|
||||
const us count = rtaudio.getDeviceCount();
|
||||
|
||||
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"
|
||||
RtAudioDeviceInfo d;
|
||||
switch (api) {
|
||||
switch (api)
|
||||
{
|
||||
case RtAudio::LINUX_ALSA:
|
||||
d.api = rtaudioAlsaApi;
|
||||
break;
|
||||
@ -70,43 +79,49 @@ void fillRtAudioDeviceInfo(DeviceInfoList &devinfolist) {
|
||||
}
|
||||
|
||||
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
|
||||
/// kHz value, which is our preffered rate,
|
||||
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];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (rate_int == 48000) {
|
||||
if (rate_int == 48000)
|
||||
{
|
||||
d.prefSampleRateIndex = j;
|
||||
rate_48k_found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
d.noutchannels = devinfo.outputChannels;
|
||||
d.ninchannels = devinfo.inputChannels;
|
||||
d.noutchannels = std::min(devinfo.outputChannels, RTAUDIO_MAX_CHANNELS);
|
||||
d.ninchannels = std::min(devinfo.inputChannels, RTAUDIO_MAX_CHANNELS);
|
||||
|
||||
d.availableInputRanges = {1.0};
|
||||
d.availableOutputRanges = {1.0};
|
||||
|
||||
RtAudioFormat formats = devinfo.nativeFormats;
|
||||
if (formats & RTAUDIO_SINT8) {
|
||||
if (formats & RTAUDIO_SINT8)
|
||||
{
|
||||
d.availableDataTypes.push_back(
|
||||
DataTypeDescriptor::DataType::dtype_int8);
|
||||
}
|
||||
if (formats & RTAUDIO_SINT16) {
|
||||
if (formats & RTAUDIO_SINT16)
|
||||
{
|
||||
d.availableDataTypes.push_back(
|
||||
DataTypeDescriptor::DataType::dtype_int16);
|
||||
}
|
||||
@ -114,15 +129,18 @@ void fillRtAudioDeviceInfo(DeviceInfoList &devinfolist) {
|
||||
/* d.availableDataTypes.push_back(DataTypeDescriptor::DataType::dtype_int24);
|
||||
*/
|
||||
/* } */
|
||||
if (formats & RTAUDIO_SINT32) {
|
||||
if (formats & RTAUDIO_SINT32)
|
||||
{
|
||||
d.availableDataTypes.push_back(
|
||||
DataTypeDescriptor::DataType::dtype_fl32);
|
||||
}
|
||||
if (formats & RTAUDIO_FLOAT64) {
|
||||
if (formats & RTAUDIO_FLOAT64)
|
||||
{
|
||||
d.availableDataTypes.push_back(
|
||||
DataTypeDescriptor::DataType::dtype_fl64);
|
||||
}
|
||||
if (d.availableDataTypes.size() == 0) {
|
||||
if (d.availableDataTypes.size() == 0)
|
||||
{
|
||||
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,
|
||||
RtAudioStreamStatus status, void *userData);
|
||||
|
||||
static void myerrorcallback(RtAudioError::Type, const string &errorText);
|
||||
|
||||
class RtAudioDaq : public Daq {
|
||||
class RtAudioDaq : public Daq
|
||||
{
|
||||
|
||||
RtAudio rtaudio;
|
||||
const us nFramesPerBlock;
|
||||
@ -182,10 +199,11 @@ public:
|
||||
/// 0. For now, our fix is to shift out the channels we want, and let
|
||||
/// RtAudio pass on all channels.
|
||||
inParams->firstChannel = 0;
|
||||
inParams->nChannels = devinfo_gen.ninchannels;
|
||||
inParams->deviceId = devinfo._api_devindex;
|
||||
|
||||
} else {
|
||||
inParams->nChannels = devinfo.ninchannels;
|
||||
inParams->deviceId = devinfo.ID;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
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
|
||||
/// RtAudio pass on all channels.
|
||||
outParams->firstChannel = 0;
|
||||
outParams->nChannels = devinfo_gen.noutchannels;
|
||||
outParams->deviceId = devinfo._api_devindex;
|
||||
outParams->nChannels = devinfo.noutchannels;
|
||||
outParams->deviceId = devinfo.ID;
|
||||
}
|
||||
|
||||
RtAudio::StreamOptions streamoptions;
|
||||
@ -207,7 +225,8 @@ public:
|
||||
RtAudioFormat format;
|
||||
using Dtype = DataTypeDescriptor::DataType;
|
||||
const Dtype dtype = dataType();
|
||||
switch (dtype) {
|
||||
switch (dtype)
|
||||
{
|
||||
case Dtype::dtype_fl32:
|
||||
DEBUGTRACE_PRINT("Datatype float32");
|
||||
format = RTAUDIO_FLOAT32;
|
||||
@ -238,36 +257,46 @@ public:
|
||||
unsigned int nFramesPerBlock_copy = nFramesPerBlock;
|
||||
|
||||
// Final step: open the stream.
|
||||
rtaudio.openStream(outParams.get(), inParams.get(), format,
|
||||
static_cast<us>(samplerate()), &nFramesPerBlock_copy,
|
||||
mycallback, (void *)this, &streamoptions,
|
||||
&myerrorcallback);
|
||||
RtAudioErrorType err = rtaudio.openStream(outParams.get(), inParams.get(), format,
|
||||
static_cast<us>(samplerate()), &nFramesPerBlock_copy,
|
||||
mycallback, (void *)this, &streamoptions);
|
||||
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 "
|
||||
"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,
|
||||
OutDaqCallback outCallback) override final {
|
||||
OutDaqCallback outCallback) override final
|
||||
{
|
||||
|
||||
DEBUGTRACE_ENTER;
|
||||
|
||||
assert(!monitorOutput);
|
||||
|
||||
if (getStreamStatus().runningOK()) {
|
||||
if (getStreamStatus().runningOK())
|
||||
{
|
||||
throw rte("Stream already running");
|
||||
}
|
||||
|
||||
// Logical XOR
|
||||
if (inCallback && outCallback) {
|
||||
if (inCallback && outCallback)
|
||||
{
|
||||
throw rte("Either input or output stream possible for RtAudio. "
|
||||
"Stream duplex mode not provided.");
|
||||
}
|
||||
|
||||
if (neninchannels() > 0) {
|
||||
if (!inCallback) {
|
||||
if (neninchannels() > 0)
|
||||
{
|
||||
if (!inCallback)
|
||||
{
|
||||
throw rte(
|
||||
|
||||
"Input callback given, but stream does not provide input data");
|
||||
@ -275,8 +304,10 @@ public:
|
||||
|
||||
_incallback = inCallback;
|
||||
}
|
||||
if (nenoutchannels() > 0) {
|
||||
if (!outCallback) {
|
||||
if (nenoutchannels() > 0)
|
||||
{
|
||||
if (!outCallback)
|
||||
{
|
||||
throw rte(
|
||||
"Output callback given, but stream does not provide output data");
|
||||
}
|
||||
@ -284,7 +315,11 @@ public:
|
||||
}
|
||||
|
||||
// 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.
|
||||
StreamStatus status;
|
||||
@ -294,10 +329,15 @@ public:
|
||||
|
||||
StreamStatus getStreamStatus() const override final { return _streamStatus; }
|
||||
|
||||
void stop() override final {
|
||||
void stop() override final
|
||||
{
|
||||
DEBUGTRACE_ENTER;
|
||||
if (getStreamStatus().runningOK()) {
|
||||
rtaudio.stopStream();
|
||||
if (getStreamStatus().runningOK())
|
||||
{
|
||||
const auto err = rtaudio.stopStream();
|
||||
if(err != RTAUDIO_NO_ERROR) {
|
||||
std::cerr << "Error occured while stopping the stream: " << rtaudio.getErrorText() << endl;
|
||||
}
|
||||
}
|
||||
StreamStatus s = _streamStatus;
|
||||
s.isRunning = false;
|
||||
@ -306,14 +346,16 @@ public:
|
||||
}
|
||||
|
||||
int streamCallback(void *outputBuffer, void *inputBuffer,
|
||||
unsigned int nFrames, RtAudioStreamStatus status) {
|
||||
unsigned int nFrames, RtAudioStreamStatus status)
|
||||
{
|
||||
|
||||
DEBUGTRACE_ENTER;
|
||||
|
||||
using se = StreamStatus::StreamError;
|
||||
|
||||
int rval = 0;
|
||||
auto stopWithError = [&](se e) {
|
||||
auto stopWithError = [&](se e)
|
||||
{
|
||||
DEBUGTRACE_PRINT("stopWithError");
|
||||
StreamStatus stat = _streamStatus;
|
||||
stat.errorType = e;
|
||||
@ -322,7 +364,8 @@ public:
|
||||
rval = 1;
|
||||
};
|
||||
|
||||
switch (status) {
|
||||
switch (status)
|
||||
{
|
||||
case RTAUDIO_INPUT_OVERFLOW:
|
||||
stopWithError(se::inputXRun);
|
||||
return 1;
|
||||
@ -341,14 +384,16 @@ public:
|
||||
const us neninchannels = this->neninchannels();
|
||||
const us nenoutchannels = this->nenoutchannels();
|
||||
const us sw = dtype_descr.sw;
|
||||
if (nFrames != nFramesPerBlock) {
|
||||
if (nFrames != nFramesPerBlock)
|
||||
{
|
||||
cerr << "RtAudio backend error: nFrames does not match block size!"
|
||||
<< endl;
|
||||
stopWithError(se::logicError);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (inputBuffer) {
|
||||
if (inputBuffer)
|
||||
{
|
||||
assert(_incallback);
|
||||
std::vector<byte_t *> ptrs;
|
||||
ptrs.reserve(neninchannels);
|
||||
@ -359,8 +404,10 @@ public:
|
||||
assert(ch_max < ninchannels);
|
||||
|
||||
/// Only pass on the pointers of the channels we want
|
||||
for (us ch = ch_min; ch <= ch_max; ch++) {
|
||||
if (inchannel_config.at(ch).enabled) {
|
||||
for (us ch = ch_min; ch <= ch_max; ch++)
|
||||
{
|
||||
if (inchannel_config.at(ch).enabled)
|
||||
{
|
||||
byte_t *ptr =
|
||||
static_cast<byte_t *>(inputBuffer) + sw * ch * nFramesPerBlock;
|
||||
ptrs.push_back(ptr);
|
||||
@ -369,10 +416,11 @@ public:
|
||||
DaqData d{nFramesPerBlock, neninchannels, dtype};
|
||||
d.copyInFromRaw(ptrs);
|
||||
|
||||
_incallback(d);
|
||||
_incallback(d);
|
||||
}
|
||||
|
||||
if (outputBuffer) {
|
||||
if (outputBuffer)
|
||||
{
|
||||
assert(_outcallback);
|
||||
std::vector<byte_t *> ptrs;
|
||||
ptrs.reserve(nenoutchannels);
|
||||
@ -384,8 +432,10 @@ public:
|
||||
assert(ch_min < noutchannels);
|
||||
assert(ch_max < noutchannels);
|
||||
/// Only pass on the pointers of the channels we want
|
||||
for (us ch = ch_min; ch <= ch_max; ch++) {
|
||||
if (outchannel_config.at(ch).enabled) {
|
||||
for (us ch = ch_min; ch <= ch_max; ch++)
|
||||
{
|
||||
if (outchannel_config.at(ch).enabled)
|
||||
{
|
||||
ptrs.push_back(static_cast<byte_t *>(outputBuffer) +
|
||||
sw * ch * nFramesPerBlock);
|
||||
}
|
||||
@ -395,7 +445,8 @@ public:
|
||||
_outcallback(d);
|
||||
// Copy over the buffer
|
||||
us j = 0;
|
||||
for (auto ptr : ptrs) {
|
||||
for (auto ptr : ptrs)
|
||||
{
|
||||
d.copyToRaw(j, ptr);
|
||||
j++;
|
||||
}
|
||||
@ -411,17 +462,16 @@ public:
|
||||
};
|
||||
|
||||
std::unique_ptr<Daq> createRtAudioDevice(const DeviceInfo &devinfo,
|
||||
const DaqConfiguration &config) {
|
||||
const DaqConfiguration &config)
|
||||
{
|
||||
return std::make_unique<RtAudioDaq>(devinfo, config);
|
||||
}
|
||||
|
||||
void myerrorcallback(RtAudioError::Type, const string &errorText) {
|
||||
cerr << "RtAudio backend stream error: " << errorText << endl;
|
||||
}
|
||||
int mycallback(
|
||||
void *outputBuffer, void *inputBuffer, unsigned int nFrames,
|
||||
__attribute__((unused)) double streamTime, // Not used parameter streamTime
|
||||
RtAudioStreamStatus status, void *userData) {
|
||||
RtAudioStreamStatus status, void *userData)
|
||||
{
|
||||
|
||||
return static_cast<RtAudioDaq *>(userData)->streamCallback(
|
||||
outputBuffer, inputBuffer, nFrames, status);
|
||||
|
2
third_party/rtaudio
vendored
2
third_party/rtaudio
vendored
@ -1 +1 @@
|
||||
Subproject commit 46b01b5b134f33d8ddc3dab76829d4b1350e0522
|
||||
Subproject commit b4f04903312e0e0efffbe77655172e0f060dc085
|
Loading…
Reference in New Issue
Block a user