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.
audionetsink.cpp
Go to the documentation of this file.
1 // Copyright (C) 2018 F4EXB //
3 // written by Edouard Griffiths //
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 <algorithm>
20 
21 #include "audionetsink.h"
22 #include "util/rtpsink.h"
23 
24 #include <QDebug>
25 #include <QUdpSocket>
26 
27 const int AudioNetSink::m_udpBlockSize = 512;
28 
29 AudioNetSink::AudioNetSink(QObject *parent) :
30  m_type(SinkUDP),
31  m_codec(CodecL16),
32  m_rtpBufferAudio(0),
33  m_sampleRate(48000),
34  m_stereo(false),
35  m_decimation(1),
36  m_decimationCount(0),
37  m_codecInputSize(960),
38  m_codecInputIndex(0),
39  m_bufferIndex(0),
40  m_port(9998)
41 {
42  std::fill(m_data, m_data+m_dataBlockSize, 0);
43  std::fill(m_opusIn, m_opusIn+m_opusBlockSize, 0);
44  m_codecRatio = (m_sampleRate / m_decimation) / (AudioOpus::m_bitrate / 8); // compressor ratio
45  m_udpSocket = new QUdpSocket(parent);
46 }
47 
48 AudioNetSink::AudioNetSink(QObject *parent, int sampleRate, bool stereo) :
49  m_type(SinkUDP),
52  m_sampleRate(48000),
53  m_stereo(false),
54  m_decimation(1),
56  m_codecInputSize(960),
58  m_bufferIndex(0),
59  m_port(9998)
60 {
61  std::fill(m_data, m_data+m_dataBlockSize, 0);
62  std::fill(m_opusIn, m_opusIn+m_opusBlockSize, 0);
63  m_codecRatio = (m_sampleRate / m_decimation) / (AudioOpus::m_bitrate / 8); // compressor ratio
64  m_udpSocket = new QUdpSocket(parent);
65  m_rtpBufferAudio = new RTPSink(m_udpSocket, sampleRate, stereo);
66 }
67 
69 {
70  if (m_rtpBufferAudio) {
71  delete m_rtpBufferAudio;
72  }
73 
74  m_udpSocket->deleteLater(); // this thread is not the owner thread (was moved)
75 }
76 
78 {
80 }
81 
83 {
84  if (type == SinkUDP)
85  {
86  m_type = SinkUDP;
87  }
88  else // this is SinkRTP
89  {
90  m_type = SinkRTP;
91  }
92 
93  return true;
94 }
95 
96 void AudioNetSink::setDestination(const QString& address, uint16_t port)
97 {
98  m_address.setAddress(const_cast<QString&>(address));
99  m_port = port;
100 
101  if (m_rtpBufferAudio) {
102  m_rtpBufferAudio->setDestination(address, port);
103  }
104 }
105 
106 void AudioNetSink::addDestination(const QString& address, uint16_t port)
107 {
108  if (m_rtpBufferAudio) {
109  m_rtpBufferAudio->addDestination(address, port);
110  }
111 }
112 
113 void AudioNetSink::deleteDestination(const QString& address, uint16_t port)
114 {
115  if (m_rtpBufferAudio) {
116  m_rtpBufferAudio->deleteDestination(address, port);
117  }
118 }
119 
120 void AudioNetSink::setParameters(Codec codec, bool stereo, int sampleRate)
121 {
122  qDebug() << "AudioNetSink::setParameters:"
123  << " codec: " << codec
124  << " stereo: " << stereo
125  << " sampleRate: " << sampleRate;
126 
127  m_codec = codec;
128  m_stereo = stereo;
129  m_sampleRate = sampleRate;
130 
131  setNewCodecData();
132 
133  if (m_rtpBufferAudio)
134  {
135  switch (m_codec)
136  {
137  case CodecPCMA:
140  break;
141  case CodecPCMU:
144  break;
145  case CodecL8:
147  break;
148  case CodecG722:
150  break;
151  case CodecOpus:
153  break;
154  case CodecL16: // actually no codec
155  default:
157  break;
158  }
159  }
160 }
161 
163 {
164  m_decimation = decimation < 1 ? 1 : decimation > 6 ? 6 : decimation;
165  qDebug() << "AudioNetSink::setDecimation: " << m_decimation << " from: " << decimation;
166  setNewCodecData();
167  m_decimationCount = 0;
168 }
169 
171 {
172  if (m_codec == CodecOpus)
173  {
174  m_codecInputSize = m_sampleRate / (m_decimation * 50); // 20ms = 1/50s - size is per channel
175  m_codecInputSize = m_codecInputSize > 960 ? 960 : m_codecInputSize; // hard limit of 48 kS/s
176  m_codecRatio = (m_sampleRate / m_decimation) / (AudioOpus::m_bitrate / 8); // compressor ratio
177  qDebug() << "AudioNetSink::setNewCodecData: CodecOpus:"
178  << " m_codecInputSize: " << m_codecInputSize
179  << " m_codecRatio: " << m_codecRatio
180  << " Fs: " << m_sampleRate/m_decimation
181  << " stereo: " << m_stereo;
182  m_opus.setEncoder(m_sampleRate/m_decimation, m_stereo ? 2 : 1);
183  m_codecInputIndex = 0;
184  m_bufferIndex = 0;
185  }
186 
188 }
189 
191 {
192  int decimatedSampleRate = m_sampleRate / m_decimation;
193 
194  switch (m_codec)
195  {
196  case CodecPCMA:
197  case CodecPCMU:
198  m_audioFilter.setDecimFilters(m_sampleRate, decimatedSampleRate, 3300.0, 300.0);
199  break;
200  case CodecG722:
201  m_audioFilter.setDecimFilters(m_sampleRate, decimatedSampleRate, 7000.0, 50.0);
202  break;
203  case CodecOpus:
204  case CodecL8:
205  case CodecL16:
206  default:
207  m_audioFilter.setDecimFilters(m_sampleRate, decimatedSampleRate, 0.45*decimatedSampleRate, 50.0);
208  break;
209  }
210 }
211 
213 {
214  qint16& sample = isample;
215 
216  if (m_decimation > 1)
217  {
218  float lpSample = m_audioFilter.run(sample / 32768.0f);
219 
220  if (m_decimationCount >= m_decimation - 1)
221  {
222  sample = lpSample * 32768.0f;
223  m_decimationCount = 0;
224  }
225  else
226  {
228  return;
229  }
230  }
231 
232  if (m_type == SinkUDP)
233  {
234  if (m_codec == CodecG722)
235  {
236  if (m_bufferIndex >= 2*m_udpBlockSize)
237  {
238  m_udpSocket->writeDatagram((const char*) m_data, (qint64 ) m_udpBlockSize, m_address, m_port);
239  m_bufferIndex = 0;
240  }
241  }
242  else
243  {
245  {
246  m_udpSocket->writeDatagram((const char*) m_data, (qint64 ) m_udpBlockSize, m_address, m_port);
247  m_bufferIndex = 0;
248  }
249  }
250 
251  switch(m_codec)
252  {
253  case CodecPCMA:
254  case CodecPCMU:
255  {
256  qint8 *p = (qint8*) &m_data[m_bufferIndex];
257  *p = m_audioCompressor.compress8(sample);
258  m_bufferIndex += sizeof(qint8);
259  }
260  break;
261  case CodecL8:
262  {
263  qint8 *p = (qint8*) &m_data[m_bufferIndex];
264  *p = sample / 256;
265  m_bufferIndex += sizeof(qint8);
266  }
267  break;
268  case CodecG722:
269  {
270  qint16 *p = (qint16*) &m_data[m_udpBlockSize + 2*m_bufferIndex];
271  *p = sample;
272  m_bufferIndex += 1;
273 
274  if (m_bufferIndex == 2*m_udpBlockSize) {
275  m_g722.encode((uint8_t *) m_data, (const int16_t*) &m_data[m_udpBlockSize], 2*m_udpBlockSize);
276  }
277  }
278  break;
279  case CodecOpus:
280  {
282  {
283  int nbBytes = m_opus.encode(m_codecInputSize, m_opusIn, (uint8_t *) m_data);
284  nbBytes = nbBytes > m_udpBlockSize ? m_udpBlockSize : nbBytes;
285  m_udpSocket->writeDatagram((const char*) m_data, (qint64 ) nbBytes, m_address, m_port);
286  m_codecInputIndex = 0;
287  }
288 
289  m_opusIn[m_codecInputIndex++] = sample;
290  }
291  break;
292  case CodecL16:
293  default:
294  {
295  qint16 *p = (qint16*) &m_data[m_bufferIndex];
296  *p = sample;
297  m_bufferIndex += sizeof(qint16);
298  }
299  break;
300  }
301  }
302  else if (m_type == SinkRTP)
303  {
304  switch(m_codec)
305  {
306  case CodecPCMA:
307  case CodecPCMU:
308  {
309  qint8 p = m_audioCompressor.compress8(sample);
310  m_rtpBufferAudio->write((uint8_t *) &p);
311  }
312  break;
313  case CodecL8:
314  {
315  qint8 p = sample / 256;
316  m_rtpBufferAudio->write((uint8_t *) &p);
317  }
318  break;
319  case CodecG722:
320  {
321 
323  {
324  m_g722.encode((uint8_t *) m_data, (const int16_t*) &m_data[m_g722BlockSize], 2*m_g722BlockSize);
325  m_bufferIndex = 0;
326  }
327 
328  if (m_bufferIndex % 2 == 0) {
330  }
331 
332  qint16 *p = (qint16*) &m_data[m_g722BlockSize + 2*m_bufferIndex];
333  *p = sample;
334  m_bufferIndex += 1;
335  }
336  break;
337  case CodecOpus:
338  {
340  {
341  int nbBytes = m_opus.encode(m_codecInputSize, m_opusIn, (uint8_t *) m_data);
342  if (nbBytes != AudioOpus::m_bitrate/400) { // 8 bits for 1/50s (20ms)
343  qWarning("AudioNetSink::write: CodecOpus mono: unexpected output frame size: %d bytes", nbBytes);
344  }
345  m_bufferIndex = 0;
346  m_codecInputIndex = 0;
347  }
348 
349  if (m_codecInputIndex % m_codecRatio == 0) {
351  }
352 
353  m_opusIn[m_codecInputIndex++] = sample;
354  }
355  break;
356  case CodecL16:
357  default:
358  m_rtpBufferAudio->write((uint8_t *) &sample);
359  break;
360  }
361  }
362 }
363 
364 void AudioNetSink::write(qint16 ilSample, qint16 irSample)
365 {
366  qint16& lSample = ilSample;
367  qint16& rSample = irSample;
368 
369  if (m_decimation > 1)
370  {
371  float lpLSample = m_audioFilter.runLP(lSample / 32768.0f);
372  float lpRSample = m_audioFilter.runLP(rSample / 32768.0f);
373 
374  if (m_decimationCount >= m_decimation - 1)
375  {
376  lSample = lpLSample * 32768.0f;
377  rSample = lpRSample * 32768.0f;
378  m_decimationCount = 0;
379  }
380  else
381  {
383  return;
384  }
385  }
386 
387  if (m_type == SinkUDP)
388  {
390  {
391  m_udpSocket->writeDatagram((const char*) m_data, (qint64 ) m_udpBlockSize, m_address, m_port);
392  m_bufferIndex = 0;
393  }
394 
395  switch(m_codec)
396  {
397  case CodecPCMA:
398  case CodecPCMU:
399  case CodecG722:
400  break; // mono modes - do nothing
401  case CodecOpus:
402  {
404  {
405  int nbBytes = m_opus.encode(m_codecInputSize, m_opusIn, (uint8_t *) m_data);
406  nbBytes = nbBytes > m_udpBlockSize ? m_udpBlockSize : nbBytes;
407  m_udpSocket->writeDatagram((const char*) m_data, (qint64 ) nbBytes, m_address, m_port);
408  m_codecInputIndex = 0;
409  }
410 
411  m_opusIn[2*m_codecInputIndex] = lSample;
412  m_opusIn[2*m_codecInputIndex+1] = rSample;
414  }
415  break;
416  case CodecL8:
417  {
418  qint8 *p = (qint8*) &m_data[m_bufferIndex];
419  *p = lSample / 256;
420  m_bufferIndex += sizeof(qint8);
421  p = (qint8*) &m_data[m_bufferIndex];
422  *p = rSample / 256;
423  m_bufferIndex += sizeof(qint8);
424  }
425  break;
426  case CodecL16:
427  default:
428  {
429  qint16 *p = (qint16*) &m_data[m_bufferIndex];
430  *p = lSample;
431  m_bufferIndex += sizeof(qint16);
432  p = (qint16*) &m_data[m_bufferIndex];
433  *p = rSample;
434  m_bufferIndex += sizeof(qint16);
435  }
436  break;
437  }
438  }
439  else if (m_type == SinkRTP)
440  {
441  switch(m_codec)
442  {
443  case CodecPCMA:
444  case CodecPCMU:
445  case CodecG722:
446  break; // mono modes - do nothing
447  case CodecOpus:
448  {
450  {
451  int nbBytes = m_opus.encode(m_codecInputSize, m_opusIn, (uint8_t *) m_data);
452  if (nbBytes != AudioOpus::m_bitrate/400) { // 8 bits for 1/50s (20ms)
453  qWarning("AudioNetSink::write: CodecOpus stereo: unexpected output frame size: %d bytes", nbBytes);
454  }
455  m_bufferIndex = 0;
456  m_codecInputIndex = 0;
457  }
458 
459  if (m_codecInputIndex % m_codecRatio == 0) {
461  }
462 
463  m_opusIn[2*m_codecInputIndex] = lSample;
464  m_opusIn[2*m_codecInputIndex+1] = rSample;
466  }
467  break;
468  case CodecL8:
469  {
470  qint8 pl = lSample / 256;
471  qint8 pr = rSample / 256;
472  m_rtpBufferAudio->write((uint8_t *) &pl, (uint8_t *) &pr);
473  }
474  break;
475  case CodecL16:
476  default:
477  m_rtpBufferAudio->write((uint8_t *) &lSample, (uint8_t *) &rSample);
478  break;
479  }
480  }
481 }
482 
483 void AudioNetSink::moveToThread(QThread *thread)
484 {
485  m_udpSocket->moveToThread(thread);
486 }
487 
short int16_t
Definition: rtptypes_win.h:43
bool selectType(SinkType type)
bool isRTPCapable() const
void setDestination(const QString &address, uint16_t port)
Definition: rtpsink.cpp:180
Linear 16 bit samples (no formatting)
Definition: audionetsink.h:47
PCM Mu-law 8 bit samples.
Definition: audionetsink.h:50
RTPSink * m_rtpBufferAudio
Definition: audionetsink.h:86
void setDecimation(uint32_t decimation)
int decimation(float Fin, float Fout)
Definition: datvdemod.h:66
AudioCompressor m_audioCompressor
Definition: audionetsink.h:87
PCM A-law 8 bit samples.
Definition: audionetsink.h:49
uint32_t m_decimation
Definition: audionetsink.h:93
static const int m_bitrate
Fixed 64kb/s bitrate (8kB/s)
Definition: audioopus.h:37
Linear 8 bit samples.
Definition: audionetsink.h:48
char m_data[m_dataBlockSize]
Definition: audionetsink.h:95
void deleteDestination(const QString &address, uint16_t port)
Definition: rtpsink.cpp:194
int16_t m_opusIn[m_opusBlockSize]
Definition: audionetsink.h:96
unsigned int m_bufferIndex
Definition: audionetsink.h:100
void moveToThread(QThread *thread)
int m_codecInputIndex
Definition: audionetsink.h:98
int8_t compress8(int16_t sample)
static const int m_udpBlockSize
Definition: audionetsink.h:73
int encode(int frameSize, int16_t *in, uint8_t *out)
Definition: audioopus.cpp:86
unsigned int uint32_t
Definition: rtptypes_win.h:46
unsigned int m_port
Definition: audionetsink.h:102
AudioNetSink(QObject *parent)
without RTP
unsigned char uint8_t
Definition: rtptypes_win.h:42
unsigned short uint16_t
Definition: rtptypes_win.h:44
void setDestination(const QString &address, uint16_t port)
QHostAddress m_address
Definition: audionetsink.h:101
AudioG722 m_g722
Definition: audionetsink.h:88
void setEncoder(int32_t fs, int nChannels)
Definition: audioopus.cpp:43
static const int m_dataBlockSize
Definition: audionetsink.h:74
Opus compressed 8 bit samples at 64kbits/s (8kS/s out). Various input sample rates.
Definition: audionetsink.h:52
G722 compressed 8 bit samples 16kS/s in 8kS/s out.
Definition: audionetsink.h:51
void fillALaw()
A-law compression to 8 bits.
QUdpSocket * m_udpSocket
Definition: audionetsink.h:85
void addDestination(const QString &address, uint16_t port)
Definition: rtpsink.cpp:205
void fillULaw()
u-law compression to 8 bits
void write(const uint8_t *sampleByte)
Definition: rtpsink.cpp:221
void setDecimationFilters()
AudioOpus m_opus
Definition: audionetsink.h:89
void write(qint16 sample)
float run(const float &sample)
int encode(uint8_t g722_data[], const int16_t amp[], int len)
Definition: audiog722.cpp:236
float runLP(const float &sample)
uint32_t m_decimationCount
Definition: audionetsink.h:94
void setNewCodecData()
AudioFilter m_audioFilter
Definition: audionetsink.h:90
int m_codecInputSize
Definition: audionetsink.h:97
void deleteDestination(const QString &address, uint16_t port)
SinkType m_type
Definition: audionetsink.h:83
bool isValid() const
Definition: rtpsink.h:56
static const int m_opusBlockSize
Definition: audionetsink.h:76
void setDecimFilters(int srHigh, int srLow, float fcHigh, float fcLow, float gain=1.0f)
Definition: audiofilter.cpp:42
void addDestination(const QString &address, uint16_t port)
void setParameters(Codec codec, bool stereo, int sampleRate)
void setPayloadInformation(PayloadType payloadType, int sampleRate)
Definition: rtpsink.cpp:75
static const int m_g722BlockSize
Definition: audionetsink.h:75