CubicSDR/src/audio/AudioThread.cpp

425 lines
14 KiB
C++
Raw Normal View History

#include "AudioThread.h"
#include "CubicSDRDefs.h"
#include <vector>
#include <algorithm>
2014-12-11 19:07:21 -05:00
#include "DemodulatorThread.h"
#ifdef __APPLE__
std::map<int, AudioThread *> AudioThread::deviceController;
std::map<int, std::thread *> AudioThread::deviceThread;
#endif
2014-11-22 22:33:32 -05:00
AudioThread::AudioThread(AudioThreadInputQueue *inputQueue, DemodulatorThreadCommandQueue* threadQueueNotify) :
currentInput(NULL), inputQueue(inputQueue), audio_queue_ptr(0), underflow_count(0), terminated(false), active(false), output_device(-1), gain(1.0), threadQueueNotify(
2014-12-24 01:28:33 -05:00
threadQueueNotify) {
#ifdef __APPLE__
boundThreads = new std::vector<AudioThread *>;
#endif
2014-11-22 22:33:32 -05:00
}
AudioThread::~AudioThread() {
#ifdef __APPLE__
delete boundThreads.load();
#endif
}
2014-11-22 22:33:32 -05:00
#ifdef __APPLE__
void AudioThread::bindThread(AudioThread *other) {
if (boundThreads.find(other) == boundThreads.end()) {
boundThreads.load()->push_back(other);
}
}
void AudioThread::removeThread(AudioThread *other) {
std::vector<AudioThread *>::iterator i;
i = std::find(boundThreads.load()->begin(), boundThreads.load()->end(), other);
if (i != boundThreads.load()->end()) {
boundThreads.load()->erase(i);
}
}
void AudioThread::deviceCleanup() {
std::map<int,AudioThread *>::iterator i;
for (i = deviceController.begin(); i != deviceController.end(); i++) {
i->second->terminate();
}
2014-11-22 22:33:32 -05:00
}
static int audioCallback(void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, double streamTime, RtAudioStreamStatus status,
void *userData) {
AudioThread *src = (AudioThread *) userData;
float *out = (float*) outputBuffer;
memset(out, 0, nBufferFrames * 2 * sizeof(float));
if (status) {
std::cout << "Audio buffer underflow.." << (src->underflow_count++) << std::endl;
}
if (!src->boundThreads.load()->empty()) {
src->gain = 1.0 / src->boundThreads.load()->size();
} else {
return 0;
}
for (int j = 0; j < src->boundThreads.load()->size(); j++) {
AudioThread *srcmix = (*(src->boundThreads.load()))[j];
if (srcmix->terminated || !srcmix->inputQueue || srcmix->inputQueue->empty() || !srcmix->isActive()) {
continue;
}
2014-12-23 01:59:03 -05:00
if (!srcmix->currentInput) {
if (srcmix->terminated) {
continue;
}
srcmix->inputQueue->pop(srcmix->currentInput);
srcmix->audio_queue_ptr = 0;
continue;
}
2014-12-24 01:28:33 -05:00
std::lock_guard < std::mutex > lock(srcmix->currentInput->m_mutex);
2014-12-23 01:59:03 -05:00
if (srcmix->currentInput->channels == 0 || !srcmix->currentInput->data.size()) {
if (!srcmix->inputQueue->empty()) {
2014-12-23 01:59:03 -05:00
if (srcmix->currentInput) {
2014-12-24 01:28:33 -05:00
srcmix->currentInput->decRefCount();
2014-12-23 01:59:03 -05:00
srcmix->currentInput = NULL;
}
if (srcmix->terminated) {
continue;
}
srcmix->inputQueue->pop(srcmix->currentInput);
srcmix->audio_queue_ptr = 0;
}
2014-12-23 01:59:03 -05:00
continue;
}
2014-12-23 01:59:03 -05:00
if (srcmix->currentInput->channels == 1) {
for (int i = 0; i < nBufferFrames; i++) {
2014-12-23 01:59:03 -05:00
if (srcmix->audio_queue_ptr >= srcmix->currentInput->data.size()) {
if (srcmix->currentInput) {
2014-12-24 01:28:33 -05:00
srcmix->currentInput->decRefCount();
2014-12-23 01:59:03 -05:00
srcmix->currentInput = NULL;
}
if (srcmix->terminated) {
continue;
}
srcmix->inputQueue->pop(srcmix->currentInput);
srcmix->audio_queue_ptr = 0;
}
2014-12-23 01:59:03 -05:00
if (srcmix->currentInput && srcmix->currentInput->data.size()) {
float v = srcmix->currentInput->data[srcmix->audio_queue_ptr] * src->gain;
out[i * 2] += v;
out[i * 2 + 1] += v;
}
srcmix->audio_queue_ptr++;
}
} else {
2014-12-26 21:55:13 -05:00
for (int i = 0, iMax = srcmix->currentInput->channels * nBufferFrames; i < iMax; i++) {
2014-12-23 01:59:03 -05:00
if (srcmix->audio_queue_ptr >= srcmix->currentInput->data.size()) {
if (srcmix->currentInput) {
2014-12-24 01:28:33 -05:00
srcmix->currentInput->decRefCount();
2014-12-23 01:59:03 -05:00
srcmix->currentInput = NULL;
}
if (srcmix->terminated) {
continue;
}
srcmix->inputQueue->pop(srcmix->currentInput);
srcmix->audio_queue_ptr = 0;
}
2014-12-23 01:59:03 -05:00
if (srcmix->currentInput && srcmix->currentInput->data.size()) {
out[i] = out[i] + srcmix->currentInput->data[srcmix->audio_queue_ptr] * src->gain;
}
srcmix->audio_queue_ptr++;
}
}
}
return 0;
}
#else
static int audioCallback(void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, double streamTime, RtAudioStreamStatus status,
void *userData) {
AudioThread *src = (AudioThread *) userData;
float *out = (float*) outputBuffer;
memset(out, 0, nBufferFrames * 2 * sizeof(float));
if (status) {
std::cout << "Audio buffer underflow.." << (src->underflow_count++) << std::endl;
}
2014-12-24 01:28:33 -05:00
2014-12-23 01:59:03 -05:00
if (!src->currentInput) {
src->inputQueue->pop(src->currentInput);
src->audio_queue_ptr = 0;
return 0;
}
2014-12-24 01:28:33 -05:00
std::lock_guard < std::mutex > lock(src->currentInput->m_mutex);
2014-12-23 01:59:03 -05:00
if (src->currentInput->channels == 0 || !src->currentInput->data.size()) {
if (!src->inputQueue->empty()) {
2014-12-23 01:59:03 -05:00
if (src->currentInput) {
2014-12-24 01:28:33 -05:00
src->currentInput->decRefCount();
2014-12-23 01:59:03 -05:00
src->currentInput = NULL;
}
if (src->terminated) {
return 1;
}
src->inputQueue->pop(src->currentInput);
src->audio_queue_ptr = 0;
}
return 0;
}
2014-12-23 01:59:03 -05:00
if (src->currentInput->channels == 1) {
for (int i = 0; i < nBufferFrames; i++) {
2014-12-23 01:59:03 -05:00
if (src->audio_queue_ptr >= src->currentInput->data.size()) {
if (src->currentInput) {
2014-12-24 01:28:33 -05:00
src->currentInput->decRefCount();
2014-12-23 01:59:03 -05:00
src->currentInput = NULL;
}
if (src->terminated) {
2014-12-23 01:59:03 -05:00
return 1;
}
src->inputQueue->pop(src->currentInput);
src->audio_queue_ptr = 0;
}
2014-12-23 01:59:03 -05:00
if (src->currentInput && src->currentInput->data.size()) {
out[i * 2] = out[i * 2 + 1] = src->currentInput->data[src->audio_queue_ptr] * src->gain;
}
src->audio_queue_ptr++;
}
} else {
2014-12-23 01:59:03 -05:00
for (int i = 0, iMax = src->currentInput->channels * nBufferFrames; i < iMax; i++) {
if (src->audio_queue_ptr >= src->currentInput->data.size()) {
if (src->currentInput) {
src->currentInput->decRefCount();
src->currentInput = NULL;
}
if (src->terminated) {
2014-12-23 01:59:03 -05:00
return 1;
}
src->inputQueue->pop(src->currentInput);
src->audio_queue_ptr = 0;
}
2014-12-23 01:59:03 -05:00
if (src->currentInput && src->currentInput->data.size()) {
out[i] = src->currentInput->data[src->audio_queue_ptr] * src->gain;
}
src->audio_queue_ptr++;
}
}
return 0;
}
#endif
2014-12-31 19:45:01 -05:00
void AudioThread::enumerateDevices(std::vector<RtAudio::DeviceInfo> &devs) {
RtAudio endac;
int numDevices = endac.getDeviceCount();
for (int i = 0; i < numDevices; i++) {
2014-12-31 19:45:01 -05:00
RtAudio::DeviceInfo info = endac.getDeviceInfo(i);
devs.push_back(info);
std::cout << std::endl;
std::cout << "Audio Device #" << i << " " << info.name << std::endl;
std::cout << "\tDefault Output? " << (info.isDefaultOutput ? "Yes" : "No") << std::endl;
std::cout << "\tDefault Input? " << (info.isDefaultOutput ? "Yes" : "No") << std::endl;
std::cout << "\tInput channels: " << info.inputChannels << std::endl;
std::cout << "\tOutput channels: " << info.outputChannels << std::endl;
std::cout << "\tDuplex channels: " << info.duplexChannels << std::endl;
std::cout << "\t" << "Native formats:" << std::endl;
RtAudioFormat nFormats = info.nativeFormats;
if (nFormats & RTAUDIO_SINT8) {
std::cout << "\t\t8-bit signed integer." << std::endl;
}
if (nFormats & RTAUDIO_SINT16) {
std::cout << "\t\t16-bit signed integer." << std::endl;
}
if (nFormats & RTAUDIO_SINT24) {
std::cout << "\t\t24-bit signed integer." << std::endl;
}
if (nFormats & RTAUDIO_SINT32) {
std::cout << "\t\t32-bit signed integer." << std::endl;
}
if (nFormats & RTAUDIO_FLOAT32) {
std::cout << "\t\t32-bit float normalized between plus/minus 1.0." << std::endl;
}
if (nFormats & RTAUDIO_FLOAT64) {
std::cout << "\t\t32-bit float normalized between plus/minus 1.0." << std::endl;
}
std::vector<unsigned int>::iterator srate;
std::cout << "\t" << "Supported sample rates:" << std::endl;
for (srate = info.sampleRates.begin(); srate != info.sampleRates.end(); srate++) {
std::cout << "\t\t" << (*srate) << "hz" << std::endl;
}
std::cout << std::endl;
}
}
void AudioThread::setupDevice(int deviceId) {
parameters.deviceId = deviceId;
parameters.nChannels = 2;
parameters.firstChannel = 0;
unsigned int sampleRate = AUDIO_FREQUENCY;
2014-12-05 21:16:43 -05:00
unsigned int bufferFrames = 256;
RtAudio::StreamOptions opts;
opts.streamName = "CubicSDR Audio Output";
output_device = deviceId;
try {
#ifdef __APPLE__
if (active && deviceController.find(parameters.deviceId) != deviceController.end()) {
deviceController[parameters.deviceId]->removeThread(this);
}
2014-12-18 20:13:29 -05:00
opts.priority = sched_get_priority_max(SCHED_FIFO);
// opts.flags = RTAUDIO_MINIMIZE_LATENCY;
opts.flags = RTAUDIO_SCHEDULE_REALTIME;
if (deviceController.find(parameters.deviceId) == deviceController.end()) {
deviceController[parameters.deviceId] = new AudioThread(NULL, NULL);
deviceController[parameters.deviceId]->bindThread(this);
deviceThread[parameters.deviceId] = new std::thread(&AudioThread::threadMain, deviceController[parameters.deviceId]);
} else if (deviceController[parameters.deviceId] == this) {
dac.openStream(&parameters, NULL, RTAUDIO_FLOAT32, sampleRate, &bufferFrames, &audioCallback, (void *) this, &opts);
dac.startStream();
} else {
deviceController[parameters.deviceId]->bindThread(this);
}
active = true;
#else
if (dac.isStreamOpen()) {
if (dac.isStreamRunning()) {
dac.stopStream();
}
dac.closeStream();
}
dac.openStream(&parameters, NULL, RTAUDIO_FLOAT32, sampleRate, &bufferFrames, &audioCallback, (void *) this, &opts);
dac.startStream();
#endif
} catch (RtAudioError& e) {
e.printMessage();
return;
}
}
int AudioThread::getOutputDevice() {
if (output_device == -1) {
return dac.getDefaultOutputDevice();
}
return output_device;
}
void AudioThread::threadMain() {
#ifdef __APPLE__
pthread_t tID = pthread_self(); // ID of this thread
int priority = sched_get_priority_max( SCHED_RR) - 1;
sched_param prio = {priority}; // scheduling priority of thread
pthread_setschedparam(tID, SCHED_RR, &prio);
#endif
std::cout << "Audio thread initializing.." << std::endl;
if (dac.getDeviceCount() < 1) {
std::cout << "No audio devices found!" << std::endl;
return;
}
setupDevice(dac.getDefaultOutputDevice());
while (!terminated) {
AudioThreadCommand command;
cmdQueue.pop(command);
if (command.cmd == AudioThreadCommand::AUDIO_THREAD_CMD_SET_DEVICE) {
setupDevice(command.int_value);
}
}
#ifdef __APPLE__
if (deviceController[parameters.deviceId] != this) {
deviceController[parameters.deviceId]->removeThread(this);
} else {
try {
dac.stopStream();
dac.closeStream();
} catch (RtAudioError& e) {
e.printMessage();
}
}
#else
try {
// Stop the stream
dac.stopStream();
dac.closeStream();
} catch (RtAudioError& e) {
e.printMessage();
}
if (dac.isStreamOpen()) {
dac.closeStream();
}
#endif
std::cout << "Audio thread done." << std::endl;
2014-12-11 19:07:21 -05:00
if (threadQueueNotify != NULL) {
DemodulatorThreadCommand tCmd(DemodulatorThreadCommand::DEMOD_THREAD_CMD_AUDIO_TERMINATED);
tCmd.context = this;
threadQueueNotify->push(tCmd);
}
}
void AudioThread::terminate() {
terminated = true;
AudioThreadCommand endCond; // push an empty input to bump the queue
cmdQueue.push(endCond);
}
bool AudioThread::isActive() {
return active;
}
void AudioThread::setActive(bool state) {
#ifdef __APPLE__
2014-12-23 01:59:03 -05:00
AudioThreadInput *dummy;
if (state && !active) {
while (!inputQueue->empty()) { // flush queue
inputQueue->pop(dummy);
2014-12-23 01:59:03 -05:00
if (dummy) {
dummy->decRefCount();
}
}
deviceController[parameters.deviceId]->bindThread(this);
} else if (!state && active) {
deviceController[parameters.deviceId]->removeThread(this);
while (!inputQueue->empty()) { // flush queue
inputQueue->pop(dummy);
2014-12-23 01:59:03 -05:00
if (dummy) {
dummy->decRefCount();
}
}
}
#endif
active = state;
}
AudioThreadCommandQueue *AudioThread::getCommandQueue() {
return &cmdQueue;
}