2014-11-16 16:50:37 -05:00
|
|
|
#include "AudioThread.h"
|
|
|
|
#include "CubicSDRDefs.h"
|
|
|
|
#include <vector>
|
|
|
|
|
2014-11-22 22:33:32 -05:00
|
|
|
AudioThread::AudioThread(AudioThreadInputQueue *inputQueue) :
|
2014-12-05 20:13:45 -05:00
|
|
|
inputQueue(inputQueue), terminated(false), audio_queue_ptr(0), underflow_count(0) {
|
2014-11-22 22:33:32 -05:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
AudioThread::~AudioThread() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-12-04 19:44:49 -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;
|
|
|
|
if (status) {
|
2014-12-05 20:13:45 -05:00
|
|
|
std::cout << "Audio buffer underflow.." << (src->underflow_count++) << std::endl;
|
2014-12-04 19:44:49 -05:00
|
|
|
}
|
2014-12-05 20:13:45 -05:00
|
|
|
|
|
|
|
std::queue<std::vector<float> > *audio_queue = src->audio_queue.load();
|
|
|
|
|
|
|
|
#ifdef __APPLE__ // Crude re-sync
|
|
|
|
int wait_for_it = 0;
|
|
|
|
|
|
|
|
if (audio_queue->empty()) {
|
2014-12-05 20:18:19 -05:00
|
|
|
while (wait_for_it++ < 5) { // Can we wait it out?
|
|
|
|
std::this_thread::sleep_for(std::chrono::microseconds(100000));
|
2014-12-05 20:13:45 -05:00
|
|
|
// std::this_thread::yield();
|
|
|
|
if (!audio_queue->empty()) {
|
|
|
|
std::cout << "Buffer recovery.." << std::endl;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (audio_queue->empty()) {
|
2014-12-04 19:44:49 -05:00
|
|
|
for (int i = 0; i < nBufferFrames * 2; i++) {
|
|
|
|
out[i] = 0;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2014-12-05 20:13:45 -05:00
|
|
|
|
|
|
|
wait_for_it = 0;
|
|
|
|
|
|
|
|
std::vector<float> nextBuffer = audio_queue->front();
|
|
|
|
int nextBufferSize = nextBuffer.size();
|
2014-12-04 19:44:49 -05:00
|
|
|
for (int i = 0; i < nBufferFrames * 2; i++) {
|
|
|
|
out[i] = nextBuffer[src->audio_queue_ptr];
|
|
|
|
src->audio_queue_ptr++;
|
2014-12-05 20:13:45 -05:00
|
|
|
if (src->audio_queue_ptr == nextBufferSize) {
|
|
|
|
audio_queue->pop();
|
2014-12-04 19:44:49 -05:00
|
|
|
src->audio_queue_ptr = 0;
|
2014-12-05 20:13:45 -05:00
|
|
|
if (audio_queue->empty()) {
|
|
|
|
|
|
|
|
#ifdef __APPLE__
|
2014-12-05 20:18:19 -05:00
|
|
|
while (wait_for_it++ < 5) { // Can we wait it out?
|
|
|
|
std::this_thread::sleep_for(std::chrono::microseconds(100000));
|
2014-12-05 20:13:45 -05:00
|
|
|
if (!audio_queue->empty()) {
|
|
|
|
std::cout << "Buffer recovery.." << std::endl;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (audio_queue->empty()) {
|
|
|
|
for (int j = i; j < nBufferFrames * 2; j++) {
|
|
|
|
std::cout << "Audio buffer underflow mid request.." << (src->underflow_count++) << std::endl;
|
|
|
|
out[i] = 0;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2014-12-04 19:44:49 -05:00
|
|
|
}
|
2014-12-05 20:13:45 -05:00
|
|
|
nextBuffer = audio_queue->front();
|
2014-12-04 19:44:49 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-22 20:57:06 -05:00
|
|
|
void AudioThread::threadMain() {
|
2014-11-30 17:11:29 -05:00
|
|
|
std::cout << "Audio thread initializing.." << std::endl;
|
|
|
|
|
2014-12-04 19:44:49 -05:00
|
|
|
if (dac.getDeviceCount() < 1) {
|
|
|
|
std::cout << "No audio devices found!" << std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
RtAudio::StreamParameters parameters;
|
|
|
|
parameters.deviceId = dac.getDefaultOutputDevice();
|
|
|
|
parameters.nChannels = 2;
|
|
|
|
parameters.firstChannel = 0;
|
|
|
|
unsigned int sampleRate = AUDIO_FREQUENCY;
|
2014-12-05 20:13:45 -05:00
|
|
|
unsigned int bufferFrames = 0;
|
2014-12-04 19:44:49 -05:00
|
|
|
|
2014-12-05 18:20:28 -05:00
|
|
|
RtAudio::StreamOptions opts;
|
2014-12-05 20:13:45 -05:00
|
|
|
// opts.flags = RTAUDIO_SCHEDULE_REALTIME | RTAUDIO_MINIMIZE_LATENCY;
|
|
|
|
// opts.flags = RTAUDIO_MINIMIZE_LATENCY;
|
2014-12-05 18:20:28 -05:00
|
|
|
opts.streamName = "CubicSDR Audio Output";
|
2014-12-05 20:13:45 -05:00
|
|
|
opts.priority = sched_get_priority_max(SCHED_FIFO);
|
|
|
|
|
|
|
|
audio_queue = new std::queue<std::vector<float> >;
|
2014-12-05 18:20:28 -05:00
|
|
|
|
2014-12-04 19:44:49 -05:00
|
|
|
try {
|
2014-12-05 18:20:28 -05:00
|
|
|
dac.openStream(¶meters, NULL, RTAUDIO_FLOAT32, sampleRate, &bufferFrames, &audioCallback, (void *) this, &opts);
|
2014-12-04 19:44:49 -05:00
|
|
|
dac.startStream();
|
|
|
|
} catch (RtAudioError& e) {
|
|
|
|
e.printMessage();
|
|
|
|
return;
|
|
|
|
}
|
2014-12-04 19:03:02 -05:00
|
|
|
|
2014-12-04 19:44:49 -05:00
|
|
|
while (!terminated) {
|
|
|
|
AudioThreadInput inp;
|
2014-12-05 20:13:45 -05:00
|
|
|
inputQueue->pop(inp);
|
|
|
|
if (inp.data.size()) {
|
|
|
|
audio_queue.load()->push(inp.data);
|
|
|
|
}
|
|
|
|
// std::this_thread::yield();
|
2014-12-04 19:44:49 -05:00
|
|
|
}
|
2014-11-22 22:56:33 -05:00
|
|
|
|
2014-12-04 19:44:49 -05:00
|
|
|
try {
|
|
|
|
// Stop the stream
|
|
|
|
dac.stopStream();
|
|
|
|
} catch (RtAudioError& e) {
|
|
|
|
e.printMessage();
|
|
|
|
}
|
2014-11-30 17:11:29 -05:00
|
|
|
|
2014-12-04 19:44:49 -05:00
|
|
|
if (dac.isStreamOpen()) {
|
|
|
|
dac.closeStream();
|
|
|
|
}
|
2014-11-30 17:11:29 -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;
|
|
|
|
AudioThreadInput endCond; // push an empty input to bump the queue
|
|
|
|
inputQueue->push(endCond);
|
|
|
|
}
|