2014-11-16 16:50:37 -05:00
|
|
|
#include "AudioThread.h"
|
|
|
|
#include "CubicSDRDefs.h"
|
|
|
|
#include <vector>
|
2014-12-18 20:11:25 -05:00
|
|
|
#include <algorithm>
|
2015-03-15 21:02:26 -04:00
|
|
|
#include "CubicSDR.h"
|
2014-12-11 19:07:21 -05:00
|
|
|
#include "DemodulatorThread.h"
|
2015-03-15 21:02:26 -04:00
|
|
|
#include "DemodulatorInstance.h"
|
2015-02-22 01:58:00 -05:00
|
|
|
#include <memory.h>
|
2014-11-16 16:50:37 -05:00
|
|
|
|
2014-12-18 20:11:25 -05:00
|
|
|
std::map<int, AudioThread *> AudioThread::deviceController;
|
2015-03-13 22:25:07 -04:00
|
|
|
std::map<int, int> AudioThread::deviceSampleRate;
|
2014-12-18 20:11:25 -05:00
|
|
|
std::map<int, std::thread *> AudioThread::deviceThread;
|
2014-11-22 22:33:32 -05:00
|
|
|
|
2015-07-30 19:30:46 -04:00
|
|
|
AudioThread::AudioThread() : IOThread(),
|
2015-08-03 23:41:37 -04:00
|
|
|
currentInput(NULL), inputQueue(NULL), nBufferFrames(1024), threadQueueNotify(NULL), sampleRate(0) {
|
2015-07-21 00:59:18 -04:00
|
|
|
|
|
|
|
audioQueuePtr.store(0);
|
|
|
|
underflowCount.store(0);
|
|
|
|
active.store(false);
|
|
|
|
outputDevice.store(-1);
|
2015-08-03 23:41:37 -04:00
|
|
|
gain.store(1.0);
|
2015-07-21 00:59:18 -04:00
|
|
|
|
2015-10-05 19:59:17 -04:00
|
|
|
boundThreads.store(new std::vector<AudioThread *>);
|
2014-11-22 22:33:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
AudioThread::~AudioThread() {
|
2014-12-18 20:11:25 -05:00
|
|
|
delete boundThreads.load();
|
|
|
|
}
|
2014-11-22 22:33:32 -05:00
|
|
|
|
2014-12-18 20:11:25 -05:00
|
|
|
void AudioThread::bindThread(AudioThread *other) {
|
2014-12-31 22:38:59 -05:00
|
|
|
if (std::find(boundThreads.load()->begin(), boundThreads.load()->end(), other) == boundThreads.load()->end()) {
|
2014-12-31 21:31:37 -05:00
|
|
|
boundThreads.load()->push_back(other);
|
|
|
|
}
|
2014-12-18 20:11:25 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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() {
|
2015-03-01 10:30:02 -05:00
|
|
|
std::map<int, AudioThread *>::iterator i;
|
2014-12-18 20:11:25 -05:00
|
|
|
|
|
|
|
for (i = deviceController.begin(); i != deviceController.end(); i++) {
|
|
|
|
i->second->terminate();
|
|
|
|
}
|
2014-11-22 22:33:32 -05:00
|
|
|
}
|
|
|
|
|
2016-01-26 19:45:54 -05:00
|
|
|
static int audioCallback(void *outputBuffer, void * /* inputBuffer */, unsigned int nBufferFrames, double /* streamTime */, RtAudioStreamStatus status,
|
2014-12-04 19:44:49 -05:00
|
|
|
void *userData) {
|
|
|
|
AudioThread *src = (AudioThread *) userData;
|
|
|
|
float *out = (float*) outputBuffer;
|
2014-12-18 20:11:25 -05:00
|
|
|
memset(out, 0, nBufferFrames * 2 * sizeof(float));
|
2015-01-10 21:49:58 -05:00
|
|
|
|
2015-07-29 20:57:02 -04:00
|
|
|
if (src->isTerminated()) {
|
2015-01-10 21:49:58 -05:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2014-12-04 19:44:49 -05:00
|
|
|
if (status) {
|
2015-01-03 17:07:39 -05:00
|
|
|
std::cout << "Audio buffer underflow.." << (src->underflowCount++) << std::endl;
|
2014-12-04 19:44:49 -05:00
|
|
|
}
|
2014-12-05 20:13:45 -05:00
|
|
|
|
2015-02-08 18:59:08 -05:00
|
|
|
if (src->boundThreads.load()->empty()) {
|
2014-12-18 20:11:25 -05:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-02-08 18:59:08 -05:00
|
|
|
float peak = 0.0;
|
|
|
|
|
2016-01-28 15:49:31 -05:00
|
|
|
for (size_t j = 0; j < src->boundThreads.load()->size(); j++) {
|
2014-12-18 20:11:25 -05:00
|
|
|
AudioThread *srcmix = (*(src->boundThreads.load()))[j];
|
2015-07-29 20:57:02 -04:00
|
|
|
if (srcmix->isTerminated() || !srcmix->inputQueue || srcmix->inputQueue->empty() || !srcmix->isActive()) {
|
2014-12-18 20:11:25 -05:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-12-23 01:59:03 -05:00
|
|
|
if (!srcmix->currentInput) {
|
2015-03-15 21:02:26 -04:00
|
|
|
srcmix->audioQueuePtr = 0;
|
2015-07-29 20:57:02 -04:00
|
|
|
if (srcmix->isTerminated() || srcmix->inputQueue->empty()) {
|
2014-12-23 01:59:03 -05:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
srcmix->inputQueue->pop(srcmix->currentInput);
|
2015-07-29 20:57:02 -04:00
|
|
|
if (srcmix->isTerminated()) {
|
2015-01-08 21:12:49 -05:00
|
|
|
continue;
|
|
|
|
}
|
2014-12-23 01:59:03 -05:00
|
|
|
continue;
|
|
|
|
}
|
2014-12-24 01:28:33 -05:00
|
|
|
|
2015-03-15 21:02:26 -04:00
|
|
|
// std::lock_guard < std::mutex > lock(srcmix->currentInput->m_mutex);
|
|
|
|
|
|
|
|
if (srcmix->currentInput->sampleRate != src->getSampleRate()) {
|
|
|
|
while (srcmix->inputQueue->size()) {
|
|
|
|
srcmix->inputQueue->pop(srcmix->currentInput);
|
|
|
|
if (srcmix->currentInput) {
|
|
|
|
if (srcmix->currentInput->sampleRate == src->getSampleRate()) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
srcmix->currentInput->decRefCount();
|
|
|
|
}
|
|
|
|
srcmix->currentInput = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
srcmix->audioQueuePtr = 0;
|
|
|
|
|
|
|
|
if (!srcmix->currentInput) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-24 01:28:33 -05:00
|
|
|
|
2014-12-23 01:59:03 -05:00
|
|
|
if (srcmix->currentInput->channels == 0 || !srcmix->currentInput->data.size()) {
|
2014-12-21 17:37:41 -05:00
|
|
|
if (!srcmix->inputQueue->empty()) {
|
2015-03-15 21:02:26 -04:00
|
|
|
srcmix->audioQueuePtr = 0;
|
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;
|
|
|
|
}
|
2015-07-29 20:57:02 -04:00
|
|
|
if (srcmix->isTerminated() || srcmix->inputQueue->empty()) {
|
2014-12-23 01:59:03 -05:00
|
|
|
continue;
|
2014-12-22 21:12:13 -05:00
|
|
|
}
|
2014-12-18 20:11:25 -05:00
|
|
|
srcmix->inputQueue->pop(srcmix->currentInput);
|
2015-07-29 20:57:02 -04:00
|
|
|
if (srcmix->isTerminated()) {
|
2015-01-08 21:12:49 -05:00
|
|
|
continue;
|
|
|
|
}
|
2014-12-18 20:11:25 -05:00
|
|
|
}
|
2014-12-23 01:59:03 -05:00
|
|
|
continue;
|
2014-12-21 17:37:41 -05:00
|
|
|
}
|
|
|
|
|
2015-03-01 10:30:02 -05:00
|
|
|
float mixPeak = srcmix->currentInput->peak * srcmix->gain;
|
2015-02-08 18:59:08 -05:00
|
|
|
|
2014-12-23 01:59:03 -05:00
|
|
|
if (srcmix->currentInput->channels == 1) {
|
2016-01-28 15:49:31 -05:00
|
|
|
for (unsigned int i = 0; i < nBufferFrames; i++) {
|
2015-01-03 17:07:39 -05:00
|
|
|
if (srcmix->audioQueuePtr >= srcmix->currentInput->data.size()) {
|
2015-03-15 21:02:26 -04:00
|
|
|
srcmix->audioQueuePtr = 0;
|
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;
|
|
|
|
}
|
2015-07-29 20:57:02 -04:00
|
|
|
if (srcmix->isTerminated() || srcmix->inputQueue->empty()) {
|
2015-03-15 21:02:26 -04:00
|
|
|
break;
|
2014-12-22 21:12:13 -05:00
|
|
|
}
|
2014-12-21 17:37:41 -05:00
|
|
|
srcmix->inputQueue->pop(srcmix->currentInput);
|
2015-07-29 20:57:02 -04:00
|
|
|
if (srcmix->isTerminated()) {
|
2015-03-15 21:02:26 -04:00
|
|
|
break;
|
2015-01-08 21:12:49 -05:00
|
|
|
}
|
2015-03-01 10:30:02 -05:00
|
|
|
float srcPeak = srcmix->currentInput->peak * srcmix->gain;
|
|
|
|
if (mixPeak < srcPeak) {
|
|
|
|
mixPeak = srcPeak;
|
|
|
|
}
|
2014-12-21 17:37:41 -05:00
|
|
|
}
|
2014-12-23 01:59:03 -05:00
|
|
|
if (srcmix->currentInput && srcmix->currentInput->data.size()) {
|
2015-02-08 18:59:08 -05:00
|
|
|
float v = srcmix->currentInput->data[srcmix->audioQueuePtr] * srcmix->gain;
|
2014-12-22 21:12:13 -05:00
|
|
|
out[i * 2] += v;
|
|
|
|
out[i * 2 + 1] += v;
|
|
|
|
}
|
2015-01-03 17:07:39 -05:00
|
|
|
srcmix->audioQueuePtr++;
|
2014-12-21 17:37:41 -05:00
|
|
|
}
|
|
|
|
} else {
|
2014-12-26 21:55:13 -05:00
|
|
|
for (int i = 0, iMax = srcmix->currentInput->channels * nBufferFrames; i < iMax; i++) {
|
2015-01-03 17:07:39 -05:00
|
|
|
if (srcmix->audioQueuePtr >= srcmix->currentInput->data.size()) {
|
2015-03-15 21:02:26 -04:00
|
|
|
srcmix->audioQueuePtr = 0;
|
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;
|
|
|
|
}
|
2015-07-29 20:57:02 -04:00
|
|
|
if (srcmix->isTerminated() || srcmix->inputQueue->empty()) {
|
2015-03-15 21:02:26 -04:00
|
|
|
break;
|
2014-12-22 21:12:13 -05:00
|
|
|
}
|
2014-12-21 17:37:41 -05:00
|
|
|
srcmix->inputQueue->pop(srcmix->currentInput);
|
2015-07-29 20:57:02 -04:00
|
|
|
if (srcmix->isTerminated()) {
|
2015-03-15 21:02:26 -04:00
|
|
|
break;
|
2015-01-08 21:12:49 -05:00
|
|
|
}
|
2015-03-01 10:30:02 -05:00
|
|
|
float srcPeak = srcmix->currentInput->peak * srcmix->gain;
|
|
|
|
if (mixPeak < srcPeak) {
|
|
|
|
mixPeak = srcPeak;
|
|
|
|
}
|
2014-12-21 17:37:41 -05:00
|
|
|
}
|
2014-12-23 01:59:03 -05:00
|
|
|
if (srcmix->currentInput && srcmix->currentInput->data.size()) {
|
2015-02-08 18:59:08 -05:00
|
|
|
out[i] = out[i] + srcmix->currentInput->data[srcmix->audioQueuePtr] * srcmix->gain;
|
2014-12-22 21:12:13 -05:00
|
|
|
}
|
2015-01-03 17:07:39 -05:00
|
|
|
srcmix->audioQueuePtr++;
|
2014-12-21 17:37:41 -05:00
|
|
|
}
|
2014-12-04 19:44:49 -05:00
|
|
|
}
|
2014-12-21 17:37:41 -05:00
|
|
|
|
2015-03-01 10:30:02 -05:00
|
|
|
peak += mixPeak;
|
2014-12-18 20:11:25 -05:00
|
|
|
}
|
|
|
|
|
2015-03-01 10:30:02 -05:00
|
|
|
if (peak > 1.0) {
|
2016-01-28 15:49:31 -05:00
|
|
|
for (unsigned int i = 0; i < nBufferFrames * 2; i++) {
|
2015-03-01 10:30:02 -05:00
|
|
|
out[i] /= peak;
|
|
|
|
}
|
|
|
|
}
|
2014-12-18 20:11:25 -05:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-12-31 19:45:01 -05:00
|
|
|
void AudioThread::enumerateDevices(std::vector<RtAudio::DeviceInfo> &devs) {
|
|
|
|
RtAudio endac;
|
|
|
|
|
|
|
|
int numDevices = endac.getDeviceCount();
|
2014-12-18 20:11:25 -05:00
|
|
|
|
|
|
|
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);
|
2014-12-18 20:11:25 -05:00
|
|
|
|
|
|
|
std::cout << std::endl;
|
|
|
|
|
|
|
|
std::cout << "Audio Device #" << i << " " << info.name << std::endl;
|
|
|
|
std::cout << "\tDefault Output? " << (info.isDefaultOutput ? "Yes" : "No") << std::endl;
|
2015-12-29 20:52:49 -05:00
|
|
|
std::cout << "\tDefault Input? " << (info.isDefaultInput ? "Yes" : "No") << std::endl;
|
2014-12-18 20:11:25 -05:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2014-12-04 19:44:49 -05:00
|
|
|
|
2015-03-13 22:25:07 -04:00
|
|
|
void AudioThread::setDeviceSampleRate(int deviceId, int sampleRate) {
|
|
|
|
if (deviceController.find(deviceId) != deviceController.end()) {
|
|
|
|
AudioThreadCommand refreshDevice;
|
|
|
|
refreshDevice.cmd = AudioThreadCommand::AUDIO_THREAD_CMD_SET_SAMPLE_RATE;
|
|
|
|
refreshDevice.int_value = sampleRate;
|
|
|
|
deviceController[deviceId]->getCommandQueue()->push(refreshDevice);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AudioThread::setSampleRate(int sampleRate) {
|
2015-03-15 21:02:26 -04:00
|
|
|
if (deviceController[outputDevice.load()] == this) {
|
|
|
|
deviceSampleRate[outputDevice.load()] = sampleRate;
|
|
|
|
|
|
|
|
dac.stopStream();
|
|
|
|
dac.closeStream();
|
|
|
|
|
2016-01-28 15:49:31 -05:00
|
|
|
for (size_t j = 0; j < boundThreads.load()->size(); j++) {
|
2015-03-15 21:02:26 -04:00
|
|
|
AudioThread *srcmix = (*(boundThreads.load()))[j];
|
|
|
|
srcmix->setSampleRate(sampleRate);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<DemodulatorInstance *>::iterator demod_i;
|
|
|
|
std::vector<DemodulatorInstance *> *demodulators;
|
|
|
|
|
|
|
|
demodulators = &wxGetApp().getDemodMgr().getDemodulators();
|
|
|
|
|
|
|
|
for (demod_i = demodulators->begin(); demod_i != demodulators->end(); demod_i++) {
|
|
|
|
if ((*demod_i)->getOutputDevice() == outputDevice.load()) {
|
|
|
|
(*demod_i)->setAudioSampleRate(sampleRate);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dac.openStream(¶meters, NULL, RTAUDIO_FLOAT32, sampleRate, &nBufferFrames, &audioCallback, (void *) this, &opts);
|
|
|
|
dac.startStream();
|
|
|
|
}
|
|
|
|
|
2015-03-13 22:25:07 -04:00
|
|
|
this->sampleRate = sampleRate;
|
2015-03-15 21:02:26 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
int AudioThread::getSampleRate() {
|
|
|
|
return this->sampleRate;
|
2015-03-13 22:25:07 -04:00
|
|
|
}
|
|
|
|
|
2014-12-31 21:31:37 -05:00
|
|
|
void AudioThread::setupDevice(int deviceId) {
|
|
|
|
parameters.deviceId = deviceId;
|
2014-12-04 19:44:49 -05:00
|
|
|
parameters.nChannels = 2;
|
|
|
|
parameters.firstChannel = 0;
|
|
|
|
|
2014-12-05 18:20:28 -05:00
|
|
|
opts.streamName = "CubicSDR Audio Output";
|
2014-12-05 20:13:45 -05:00
|
|
|
|
2014-12-04 19:44:49 -05:00
|
|
|
try {
|
2015-01-03 17:07:39 -05:00
|
|
|
if (deviceController.find(outputDevice.load()) != deviceController.end()) {
|
|
|
|
deviceController[outputDevice.load()]->removeThread(this);
|
2014-12-31 21:31:37 -05:00
|
|
|
}
|
2015-02-20 00:34:32 -05:00
|
|
|
#ifndef _MSC_VER
|
2014-12-18 20:13:29 -05:00
|
|
|
opts.priority = sched_get_priority_max(SCHED_FIFO);
|
2015-02-20 00:34:32 -05:00
|
|
|
#endif
|
2014-12-18 20:13:29 -05:00
|
|
|
// opts.flags = RTAUDIO_MINIMIZE_LATENCY;
|
|
|
|
opts.flags = RTAUDIO_SCHEDULE_REALTIME;
|
|
|
|
|
2015-03-15 21:02:26 -04:00
|
|
|
if (deviceSampleRate.find(parameters.deviceId) != deviceSampleRate.end()) {
|
|
|
|
sampleRate = deviceSampleRate[parameters.deviceId];
|
|
|
|
} else {
|
2015-03-22 20:47:07 -04:00
|
|
|
std::cout << "Error, device sample rate wasn't initialized?" << std::endl;
|
|
|
|
return;
|
|
|
|
// sampleRate = AudioThread::getDefaultAudioSampleRate();
|
|
|
|
// deviceSampleRate[parameters.deviceId] = sampleRate;
|
2015-03-15 21:02:26 -04:00
|
|
|
}
|
|
|
|
|
2014-12-18 20:11:25 -05:00
|
|
|
if (deviceController.find(parameters.deviceId) == deviceController.end()) {
|
2015-07-30 19:30:46 -04:00
|
|
|
deviceController[parameters.deviceId] = new AudioThread();
|
2015-03-15 21:02:26 -04:00
|
|
|
|
|
|
|
deviceController[parameters.deviceId]->setInitOutputDevice(parameters.deviceId, sampleRate);
|
2014-12-18 20:11:25 -05:00
|
|
|
deviceController[parameters.deviceId]->bindThread(this);
|
2015-03-13 22:25:07 -04:00
|
|
|
|
2014-12-18 20:11:25 -05:00
|
|
|
deviceThread[parameters.deviceId] = new std::thread(&AudioThread::threadMain, deviceController[parameters.deviceId]);
|
|
|
|
} else if (deviceController[parameters.deviceId] == this) {
|
2015-03-13 22:25:07 -04:00
|
|
|
dac.openStream(¶meters, NULL, RTAUDIO_FLOAT32, sampleRate, &nBufferFrames, &audioCallback, (void *) this, &opts);
|
2014-12-18 20:11:25 -05:00
|
|
|
dac.startStream();
|
|
|
|
} else {
|
|
|
|
deviceController[parameters.deviceId]->bindThread(this);
|
|
|
|
}
|
2015-02-05 20:01:21 -05:00
|
|
|
active = true;
|
2015-02-04 18:10:42 -05:00
|
|
|
|
2014-12-04 19:44:49 -05:00
|
|
|
} catch (RtAudioError& e) {
|
|
|
|
e.printMessage();
|
|
|
|
return;
|
|
|
|
}
|
2015-02-04 18:10:42 -05:00
|
|
|
if (deviceId != -1) {
|
|
|
|
outputDevice = deviceId;
|
|
|
|
}
|
2014-12-31 21:31:37 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
int AudioThread::getOutputDevice() {
|
2015-01-03 17:07:39 -05:00
|
|
|
if (outputDevice == -1) {
|
2014-12-31 21:31:37 -05:00
|
|
|
return dac.getDefaultOutputDevice();
|
|
|
|
}
|
2015-01-03 17:07:39 -05:00
|
|
|
return outputDevice;
|
2014-12-31 21:31:37 -05:00
|
|
|
}
|
|
|
|
|
2015-03-13 22:25:07 -04:00
|
|
|
void AudioThread::setInitOutputDevice(int deviceId, int sampleRate) {
|
2015-01-03 17:07:39 -05:00
|
|
|
outputDevice = deviceId;
|
2015-03-13 22:25:07 -04:00
|
|
|
if (sampleRate == -1) {
|
|
|
|
if (deviceSampleRate.find(parameters.deviceId) != deviceSampleRate.end()) {
|
|
|
|
sampleRate = deviceSampleRate[deviceId];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
deviceSampleRate[deviceId] = sampleRate;
|
|
|
|
}
|
|
|
|
this->sampleRate = sampleRate;
|
2014-12-31 22:38:59 -05:00
|
|
|
}
|
|
|
|
|
2015-07-29 20:57:02 -04:00
|
|
|
void AudioThread::run() {
|
2014-12-31 21:31:37 -05:00
|
|
|
#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;
|
|
|
|
}
|
|
|
|
|
2015-01-09 20:56:43 -05:00
|
|
|
setupDevice((outputDevice.load() == -1) ? (dac.getDefaultOutputDevice()) : outputDevice.load());
|
2014-12-31 22:38:59 -05:00
|
|
|
|
|
|
|
std::cout << "Audio thread started." << std::endl;
|
2014-12-04 19:03:02 -05:00
|
|
|
|
2015-07-30 19:30:46 -04:00
|
|
|
inputQueue = (AudioThreadInputQueue *)getInputQueue("AudioDataInput");
|
|
|
|
threadQueueNotify = (DemodulatorThreadCommandQueue*)getOutputQueue("NotifyQueue");
|
|
|
|
|
2014-12-04 19:44:49 -05:00
|
|
|
while (!terminated) {
|
2014-12-15 20:47:46 -05:00
|
|
|
AudioThreadCommand command;
|
|
|
|
cmdQueue.pop(command);
|
2014-12-31 21:31:37 -05:00
|
|
|
|
|
|
|
if (command.cmd == AudioThreadCommand::AUDIO_THREAD_CMD_SET_DEVICE) {
|
|
|
|
setupDevice(command.int_value);
|
|
|
|
}
|
2015-03-13 22:25:07 -04:00
|
|
|
if (command.cmd == AudioThreadCommand::AUDIO_THREAD_CMD_SET_SAMPLE_RATE) {
|
|
|
|
setSampleRate(command.int_value);
|
|
|
|
}
|
2014-12-04 19:44:49 -05:00
|
|
|
}
|
2014-11-22 22:56:33 -05:00
|
|
|
|
2014-12-18 20:11:25 -05:00
|
|
|
if (deviceController[parameters.deviceId] != this) {
|
|
|
|
deviceController[parameters.deviceId]->removeThread(this);
|
|
|
|
} else {
|
|
|
|
try {
|
2015-01-09 20:56:43 -05:00
|
|
|
if (dac.isStreamOpen()) {
|
|
|
|
if (dac.isStreamRunning()) {
|
|
|
|
dac.stopStream();
|
|
|
|
}
|
|
|
|
dac.closeStream();
|
|
|
|
}
|
2014-12-18 20:11:25 -05:00
|
|
|
} catch (RtAudioError& e) {
|
|
|
|
e.printMessage();
|
|
|
|
}
|
|
|
|
}
|
2014-11-30 17:11:29 -05:00
|
|
|
|
2014-12-18 20:11:25 -05:00
|
|
|
if (threadQueueNotify != NULL) {
|
|
|
|
DemodulatorThreadCommand tCmd(DemodulatorThreadCommand::DEMOD_THREAD_CMD_AUDIO_TERMINATED);
|
|
|
|
tCmd.context = this;
|
|
|
|
threadQueueNotify->push(tCmd);
|
|
|
|
}
|
2015-01-11 20:26:51 -05:00
|
|
|
std::cout << "Audio thread done." << std::endl;
|
2014-11-16 16:50:37 -05:00
|
|
|
}
|
|
|
|
|
2014-11-23 19:39:27 -05:00
|
|
|
void AudioThread::terminate() {
|
|
|
|
terminated = true;
|
2014-12-15 20:47:46 -05:00
|
|
|
AudioThreadCommand endCond; // push an empty input to bump the queue
|
|
|
|
cmdQueue.push(endCond);
|
2014-11-23 19:39:27 -05:00
|
|
|
}
|
2014-12-18 20:11:25 -05:00
|
|
|
|
|
|
|
bool AudioThread::isActive() {
|
|
|
|
return active;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AudioThread::setActive(bool state) {
|
2015-02-04 18:10:42 -05:00
|
|
|
|
2014-12-23 01:59:03 -05:00
|
|
|
AudioThreadInput *dummy;
|
2015-10-07 15:18:29 -04:00
|
|
|
if (state && !active && inputQueue) {
|
2014-12-18 20:11:25 -05:00
|
|
|
while (!inputQueue->empty()) { // flush queue
|
|
|
|
inputQueue->pop(dummy);
|
2014-12-23 01:59:03 -05:00
|
|
|
if (dummy) {
|
2014-12-28 05:13:46 -05:00
|
|
|
dummy->decRefCount();
|
2014-12-22 19:43:56 -05:00
|
|
|
}
|
2014-12-18 20:11:25 -05:00
|
|
|
}
|
2014-12-22 21:12:13 -05:00
|
|
|
deviceController[parameters.deviceId]->bindThread(this);
|
2014-12-18 20:11:25 -05:00
|
|
|
} else if (!state && active) {
|
|
|
|
deviceController[parameters.deviceId]->removeThread(this);
|
2015-10-07 15:18:29 -04:00
|
|
|
if(inputQueue) {
|
|
|
|
while (!inputQueue->empty()) { // flush queue
|
|
|
|
inputQueue->pop(dummy);
|
|
|
|
if (dummy) {
|
|
|
|
dummy->decRefCount();
|
|
|
|
}
|
2014-12-22 19:43:56 -05:00
|
|
|
}
|
2014-12-18 20:11:25 -05:00
|
|
|
}
|
|
|
|
}
|
2015-02-05 20:01:21 -05:00
|
|
|
active = state;
|
2014-12-18 20:11:25 -05:00
|
|
|
}
|
2014-12-31 21:31:37 -05:00
|
|
|
|
|
|
|
AudioThreadCommandQueue *AudioThread::getCommandQueue() {
|
|
|
|
return &cmdQueue;
|
|
|
|
}
|
2015-01-10 20:33:30 -05:00
|
|
|
|
|
|
|
void AudioThread::setGain(float gain_in) {
|
|
|
|
if (gain < 0.0) {
|
|
|
|
gain = 0.0;
|
|
|
|
}
|
|
|
|
if (gain > 2.0) {
|
|
|
|
gain = 2.0;
|
|
|
|
}
|
|
|
|
gain = gain_in;
|
|
|
|
}
|
|
|
|
|
|
|
|
float AudioThread::getGain() {
|
|
|
|
return gain;
|
|
|
|
}
|