SDRAngel  4.11.5
Developer docs for <a href="https://github.com/f4exb/sdrangel">SDRangel<\a>, an Open Source Qt5 / OpenGL 3.0+ SDR and signal analyzer frontend to various hardware.
audiooutput.cpp
Go to the documentation of this file.
1 // Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
3 // written by Christian Daniel //
4 // //
5 // This program is free software; you can redistribute it and/or modify //
6 // it under the terms of the GNU General Public License as published by //
7 // the Free Software Foundation as version 3 of the License, or //
8 // (at your option) any later version. //
9 // //
10 // This program is distributed in the hope that it will be useful, //
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of //
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
13 // GNU General Public License V3 for more details. //
14 // //
15 // You should have received a copy of the GNU General Public License //
16 // along with this program. If not, see <http://www.gnu.org/licenses/>. //
18 
19 #include <string.h>
20 #include <QAudioFormat>
21 #include <QAudioDeviceInfo>
22 #include <QAudioOutput>
23 #include "audiooutput.h"
24 #include "audiofifo.h"
25 #include "audionetsink.h"
26 
28  m_mutex(QMutex::Recursive),
29  m_audioOutput(0),
30  m_audioNetSink(0),
31  m_copyAudioToUdp(false),
32  m_udpChannelMode(UDPChannelLeft),
33  m_udpChannelCodec(UDPCodecL16),
34  m_audioUsageCount(0),
35  m_onExit(false),
36  m_audioFifos()
37 {
38 }
39 
41 {
42 // stop();
43 //
44 // QMutexLocker mutexLocker(&m_mutex);
45 //
46 // for (std::list<AudioFifo*>::iterator it = m_audioFifos.begin(); it != m_audioFifos.end(); ++it)
47 // {
48 // delete *it;
49 // }
50 //
51 // m_audioFifos.clear();
52 }
53 
54 bool AudioOutput::start(int device, int rate)
55 {
56 
57 // if (m_audioUsageCount == 0)
58 // {
59  QMutexLocker mutexLocker(&m_mutex);
60  QAudioDeviceInfo devInfo;
61 
62  if (device < 0)
63  {
64  devInfo = QAudioDeviceInfo::defaultOutputDevice();
65  qWarning("AudioOutput::start: using system default device %s", qPrintable(devInfo.defaultOutputDevice().deviceName()));
66  }
67  else
68  {
69  QList<QAudioDeviceInfo> devicesInfo = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
70 
71  if (device < devicesInfo.size())
72  {
73  devInfo = devicesInfo[device];
74  qWarning("AudioOutput::start: using audio device #%d: %s", device, qPrintable(devInfo.deviceName()));
75  }
76  else
77  {
78  devInfo = QAudioDeviceInfo::defaultOutputDevice();
79  qWarning("AudioOutput::start: audio device #%d does not exist. Using system default device %s", device, qPrintable(devInfo.defaultOutputDevice().deviceName()));
80  }
81  }
82 
83  //QAudioDeviceInfo devInfo(QAudioDeviceInfo::defaultOutputDevice());
84 
85  m_audioFormat.setSampleRate(rate);
86  m_audioFormat.setChannelCount(2);
87  m_audioFormat.setSampleSize(16);
88  m_audioFormat.setCodec("audio/pcm");
89  m_audioFormat.setByteOrder(QAudioFormat::LittleEndian);
90  m_audioFormat.setSampleType(QAudioFormat::SignedInt);
91 
92  if (!devInfo.isFormatSupported(m_audioFormat))
93  {
94  m_audioFormat = devInfo.nearestFormat(m_audioFormat);
95  std::ostringstream os;
96  os << " sampleRate: " << m_audioFormat.sampleRate()
97  << " channelCount: " << m_audioFormat.channelCount()
98  << " sampleSize: " << m_audioFormat.sampleSize()
99  << " codec: " << m_audioFormat.codec().toStdString()
100  << " byteOrder: " << (m_audioFormat.byteOrder() == QAudioFormat::BigEndian ? "BE" : "LE")
101  << " sampleType: " << (int) m_audioFormat.sampleType();
102  qWarning("AudioOutput::start: format %d Hz 2xS16LE audio/pcm not supported. Using: %s", rate, os.str().c_str());
103  }
104  else
105  {
106  qInfo("AudioOutput::start: audio format OK");
107  }
108 
109  if (m_audioFormat.sampleSize() != 16)
110  {
111  qWarning("AudioOutput::start: Audio device '%s' failed", qPrintable(devInfo.defaultOutputDevice().deviceName()));
112  return false;
113  }
114 
115  m_audioOutput = new QAudioOutput(devInfo, m_audioFormat);
116  m_audioNetSink = new AudioNetSink(0, m_audioFormat.sampleRate(), false);
117 
118  QIODevice::open(QIODevice::ReadOnly);
119 
120  m_audioOutput->start(this);
121 
122  if (m_audioOutput->state() != QAudio::ActiveState)
123  {
124  qWarning("AudioOutput::start: cannot start");
125  }
126 // }
127 //
128 // m_audioUsageCount++;
129 
130  return true;
131 }
132 
134 {
135  qDebug("AudioOutput::stop");
136 
137  QMutexLocker mutexLocker(&m_mutex);
138  m_audioOutput->stop();
139  QIODevice::close();
140  delete m_audioNetSink;
141  m_audioNetSink = 0;
142  delete m_audioOutput;
143 
144 // if (m_audioUsageCount > 0)
145 // {
146 // m_audioUsageCount--;
147 //
148 // if (m_audioUsageCount == 0)
149 // {
150 // QMutexLocker mutexLocker(&m_mutex);
151 // QIODevice::close();
152 //
153 // if (!m_onExit) {
154 // delete m_audioOutput;
155 // }
156 // }
157 // }
158 }
159 
161 {
162  QMutexLocker mutexLocker(&m_mutex);
163 
164  m_audioFifos.push_back(audioFifo);
165 }
166 
168 {
169  QMutexLocker mutexLocker(&m_mutex);
170 
171  m_audioFifos.remove(audioFifo);
172 }
173 
174 /*
175 bool AudioOutput::open(OpenMode mode)
176 {
177  Q_UNUSED(mode);
178  return false;
179 }*/
180 
181 void AudioOutput::setUdpDestination(const QString& address, uint16_t port)
182 {
183  if (m_audioNetSink) {
184  m_audioNetSink->setDestination(address, port);
185  }
186 }
187 
188 void AudioOutput::setUdpCopyToUDP(bool copyToUDP)
189 {
190  m_copyAudioToUdp = copyToUDP;
191 }
192 
193 void AudioOutput::setUdpUseRTP(bool useRTP)
194 {
195  if (m_audioNetSink) {
197  }
198 }
199 
201 {
202  m_udpChannelMode = udpChannelMode;
203 }
204 
205 void AudioOutput::setUdpChannelFormat(UDPChannelCodec udpChannelCodec, bool stereo, int sampleRate)
206 {
207  m_udpChannelCodec = udpChannelCodec;
208 
209  if (m_audioNetSink) {
211  }
212 }
213 
215 {
216  if (m_audioNetSink) {
217  m_audioNetSink->setDecimation(decimation);
218  }
219 }
220 
221 qint64 AudioOutput::readData(char* data, qint64 maxLen)
222 {
223  //qDebug("AudioOutput::readData: %lld", maxLen);
224 
225  // Study this mutex on OSX, for now deadlocks possible
226  // Removed as it may indeed cause lockups and is in fact useless.
227 //#ifndef __APPLE__
228 // QMutexLocker mutexLocker(&m_mutex);
229 //#endif
230 
231  unsigned int samplesPerBuffer = maxLen / 4;
232 
233  if (samplesPerBuffer == 0)
234  {
235  return 0;
236  }
237 
238  if (m_mixBuffer.size() < samplesPerBuffer * 2)
239  {
240  m_mixBuffer.resize(samplesPerBuffer * 2); // allocate 2 qint32 per sample (stereo)
241 
242  if (m_mixBuffer.size() != samplesPerBuffer * 2)
243  {
244  return 0;
245  }
246  }
247 
248  memset(&m_mixBuffer[0], 0x00, 2 * samplesPerBuffer * sizeof(m_mixBuffer[0])); // start with silence
249 
250  // sum up a block from all fifos
251 
252  for (std::list<AudioFifo*>::iterator it = m_audioFifos.begin(); it != m_audioFifos.end(); ++it)
253  {
254  // use outputBuffer as temp - yes, one memcpy could be saved
255  unsigned int samples = (*it)->read((quint8*) data, samplesPerBuffer);
256  const qint16* src = (const qint16*) data;
257  std::vector<qint32>::iterator dst = m_mixBuffer.begin();
258 
259 // if (samples != framesPerBuffer)
260 // {
261 // qDebug("AudioOutput::readData: read %d samples vs %d requested", samples, framesPerBuffer);
262 // }
263 
264  for (unsigned int i = 0; i < samples; i++)
265  {
266  *dst += *src;
267  ++src;
268  ++dst;
269  *dst += *src;
270  ++src;
271  ++dst;
272  }
273  }
274  // convert to int16
275 
276  //std::vector<qint32>::const_iterator src = m_mixBuffer.begin(); // Valgrind optim
277  qint16* dst = (qint16*) data;
278  qint32 sl, sr;
279 
280  for (unsigned int i = 0; i < samplesPerBuffer; i++)
281  {
282  // left channel
283 
284  //s = *src++; // Valgrind optim
285  sl = m_mixBuffer[2*i];
286 
287  if(sl < -32768)
288  {
289  sl = -32768;
290  }
291  else if (sl > 32767)
292  {
293  sl = 32767;
294  }
295 
296  *dst++ = sl;
297 
298  // right channel
299 
300  //s = *src++; // Valgrind optim
301  sr = m_mixBuffer[2*i + 1];
302 
303  if(sr < -32768)
304  {
305  sr = -32768;
306  }
307  else if (sr > 32767)
308  {
309  sr = 32767;
310  }
311 
312  *dst++ = sr;
313 
314  if ((m_copyAudioToUdp) && (m_audioNetSink))
315  {
316  switch (m_udpChannelMode)
317  {
318  case UDPChannelStereo:
319  m_audioNetSink->write(sl, sr);
320  break;
321  case UDPChannelMixed:
322  m_audioNetSink->write((sl+sr)/2);
323  break;
324  case UDPChannelRight:
325  m_audioNetSink->write(sr);
326  break;
327  case UDPChannelLeft:
328  default:
329  m_audioNetSink->write(sl);
330  break;
331  }
332  }
333  }
334 
335  return samplesPerBuffer * 4;
336 }
337 
338 qint64 AudioOutput::writeData(const char* data, qint64 len)
339 {
340  Q_UNUSED(data);
341  Q_UNUSED(len);
342  return 0;
343 }
void setUdpChannelMode(UDPChannelMode udpChannelMode)
bool selectType(SinkType type)
QAudioFormat m_audioFormat
Definition: audiooutput.h:88
bool m_copyAudioToUdp
Definition: audiooutput.h:79
void setDecimation(uint32_t decimation)
int decimation(float Fin, float Fout)
Definition: datvdemod.h:66
QAudioOutput * m_audioOutput
Definition: audiooutput.h:77
std::list< AudioFifo * > m_audioFifos
Definition: audiooutput.h:85
AudioNetSink * m_audioNetSink
Definition: audiooutput.h:78
void setUdpCopyToUDP(bool copyToUDP)
bool start(int device, int rate)
Definition: audiooutput.cpp:54
virtual qint64 readData(char *data, qint64 maxLen)
std::vector< qint32 > m_mixBuffer
Definition: audiooutput.h:86
virtual ~AudioOutput()
Definition: audiooutput.cpp:40
unsigned int uint32_t
Definition: rtptypes_win.h:46
void setUdpDecimation(uint32_t decimation)
void addFifo(AudioFifo *audioFifo)
void setUdpChannelFormat(UDPChannelCodec udpChannelCodec, bool stereo, int sampleRate)
QMutex m_mutex
Definition: audiooutput.h:76
unsigned short uint16_t
Definition: rtptypes_win.h:44
void setDestination(const QString &address, uint16_t port)
int32_t i
Definition: decimators.h:244
void removeFifo(AudioFifo *audioFifo)
virtual qint64 writeData(const char *data, qint64 len)
UDPChannelCodec m_udpChannelCodec
Definition: audiooutput.h:81
void setUdpDestination(const QString &address, uint16_t port)
void write(qint16 sample)
void setUdpUseRTP(bool useRTP)
UDPChannelMode m_udpChannelMode
Definition: audiooutput.h:80
void setParameters(Codec codec, bool stereo, int sampleRate)