| 
									
										
										
										
											2018-01-29 01:59:03 +01:00
										 |  |  | ///////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							| 
									
										
										
										
											2023-11-19 06:43:20 +01:00
										 |  |  | // Copyright (C) 2018-2019 Edouard Griffiths, F4EXB <f4exb06@gmail.com>          //
 | 
					
						
							| 
									
										
										
										
											2018-01-29 01:59:03 +01:00
										 |  |  | //                                                                               //
 | 
					
						
							|  |  |  | // This program is free software; you can redistribute it and/or modify          //
 | 
					
						
							|  |  |  | // it under the terms of the GNU General Public License as published by          //
 | 
					
						
							|  |  |  | // the Free Software Foundation as version 3 of the License, or                  //
 | 
					
						
							| 
									
										
										
										
											2019-04-11 14:32:15 +02:00
										 |  |  | // (at your option) any later version.                                           //
 | 
					
						
							| 
									
										
										
										
											2018-01-29 01:59:03 +01:00
										 |  |  | //                                                                               //
 | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful,               //
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of                //
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the                  //
 | 
					
						
							|  |  |  | // GNU General Public License V3 for more details.                               //
 | 
					
						
							|  |  |  | //                                                                               //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU General Public License             //
 | 
					
						
							|  |  |  | // along with this program. If not, see <http://www.gnu.org/licenses/>.          //
 | 
					
						
							|  |  |  | ///////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-17 03:40:11 +01:00
										 |  |  | #include <algorithm>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 01:59:03 +01:00
										 |  |  | #include "audionetsink.h"
 | 
					
						
							| 
									
										
										
										
											2018-01-29 05:19:59 +01:00
										 |  |  | #include "util/rtpsink.h"
 | 
					
						
							| 
									
										
										
										
											2018-01-29 01:59:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-13 07:53:38 +01:00
										 |  |  | #include <QDebug>
 | 
					
						
							| 
									
										
										
										
											2018-03-08 00:16:24 +01:00
										 |  |  | #include <QUdpSocket>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 01:59:03 +01:00
										 |  |  | const int AudioNetSink::m_udpBlockSize = 512; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-27 09:04:10 +02:00
										 |  |  | AudioNetSink::AudioNetSink(QObject *parent) : | 
					
						
							| 
									
										
										
										
											2018-01-29 01:59:03 +01:00
										 |  |  |     m_type(SinkUDP), | 
					
						
							| 
									
										
										
										
											2019-02-13 07:53:38 +01:00
										 |  |  |     m_codec(CodecL16), | 
					
						
							| 
									
										
										
										
											2018-03-08 00:16:24 +01:00
										 |  |  |     m_rtpBufferAudio(0), | 
					
						
							| 
									
										
										
										
											2019-02-17 01:32:32 +01:00
										 |  |  |     m_sampleRate(48000), | 
					
						
							| 
									
										
										
										
											2019-02-18 18:29:37 +01:00
										 |  |  |     m_stereo(false), | 
					
						
							| 
									
										
										
										
											2019-02-14 17:21:14 +01:00
										 |  |  |     m_decimation(1), | 
					
						
							|  |  |  |     m_decimationCount(0), | 
					
						
							| 
									
										
										
										
											2019-02-18 18:29:37 +01:00
										 |  |  |     m_codecInputSize(960), | 
					
						
							|  |  |  |     m_codecInputIndex(0), | 
					
						
							| 
									
										
										
										
											2018-03-08 00:16:24 +01:00
										 |  |  |     m_bufferIndex(0), | 
					
						
							|  |  |  |     m_port(9998) | 
					
						
							| 
									
										
										
										
											2018-01-29 01:59:03 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-02-17 03:40:11 +01:00
										 |  |  |     std::fill(m_data, m_data+m_dataBlockSize, 0); | 
					
						
							| 
									
										
										
										
											2019-06-14 16:58:09 +02:00
										 |  |  |     std::fill(m_opusIn, m_opusIn+m_opusBlockSize, 0); | 
					
						
							|  |  |  |     m_codecRatio = (m_sampleRate / m_decimation) / (AudioOpus::m_bitrate / 8); // compressor ratio
 | 
					
						
							| 
									
										
										
										
											2018-03-08 00:16:24 +01:00
										 |  |  |     m_udpSocket = new QUdpSocket(parent); | 
					
						
							| 
									
										
										
										
											2018-03-27 09:04:10 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-03-27 08:13:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-27 09:04:10 +02:00
										 |  |  | AudioNetSink::AudioNetSink(QObject *parent, int sampleRate, bool stereo) : | 
					
						
							|  |  |  |     m_type(SinkUDP), | 
					
						
							| 
									
										
										
										
											2019-02-13 07:53:38 +01:00
										 |  |  |     m_codec(CodecL16), | 
					
						
							| 
									
										
										
										
											2018-03-27 09:04:10 +02:00
										 |  |  |     m_rtpBufferAudio(0), | 
					
						
							| 
									
										
										
										
											2019-02-17 01:32:32 +01:00
										 |  |  |     m_sampleRate(48000), | 
					
						
							| 
									
										
										
										
											2019-02-18 18:29:37 +01:00
										 |  |  |     m_stereo(false), | 
					
						
							| 
									
										
										
										
											2019-02-14 17:21:14 +01:00
										 |  |  |     m_decimation(1), | 
					
						
							|  |  |  |     m_decimationCount(0), | 
					
						
							| 
									
										
										
										
											2019-02-18 18:29:37 +01:00
										 |  |  |     m_codecInputSize(960), | 
					
						
							|  |  |  |     m_codecInputIndex(0), | 
					
						
							| 
									
										
										
										
											2018-03-27 09:04:10 +02:00
										 |  |  |     m_bufferIndex(0), | 
					
						
							|  |  |  |     m_port(9998) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-02-17 03:40:11 +01:00
										 |  |  |     std::fill(m_data, m_data+m_dataBlockSize, 0); | 
					
						
							| 
									
										
										
										
											2019-06-14 16:58:09 +02:00
										 |  |  |     std::fill(m_opusIn, m_opusIn+m_opusBlockSize, 0); | 
					
						
							|  |  |  |     m_codecRatio = (m_sampleRate / m_decimation) / (AudioOpus::m_bitrate / 8); // compressor ratio
 | 
					
						
							| 
									
										
										
										
											2018-03-27 09:04:10 +02:00
										 |  |  |     m_udpSocket = new QUdpSocket(parent); | 
					
						
							|  |  |  |     m_rtpBufferAudio = new RTPSink(m_udpSocket, sampleRate, stereo); | 
					
						
							| 
									
										
										
										
											2018-01-29 01:59:03 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | AudioNetSink::~AudioNetSink() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2018-01-31 00:40:54 +01:00
										 |  |  |     if (m_rtpBufferAudio) { | 
					
						
							|  |  |  |         delete m_rtpBufferAudio; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-03-08 00:16:24 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     m_udpSocket->deleteLater(); // this thread is not the owner thread (was moved)
 | 
					
						
							| 
									
										
										
										
											2018-01-29 01:59:03 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-08 00:16:24 +01:00
										 |  |  | bool AudioNetSink::isRTPCapable() const | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2018-03-27 08:13:06 +02:00
										 |  |  |     return m_rtpBufferAudio && m_rtpBufferAudio->isValid(); | 
					
						
							| 
									
										
										
										
											2018-03-08 00:16:24 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-02-20 19:18:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 01:59:03 +01:00
										 |  |  | bool AudioNetSink::selectType(SinkType type) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (type == SinkUDP) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         m_type = SinkUDP; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-06-21 19:28:11 +02:00
										 |  |  |     else // this is SinkRTP
 | 
					
						
							| 
									
										
										
										
											2018-01-29 01:59:03 +01:00
										 |  |  |     { | 
					
						
							|  |  |  |         m_type = SinkRTP; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-06-21 19:28:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return true; | 
					
						
							| 
									
										
										
										
											2018-01-29 01:59:03 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void AudioNetSink::setDestination(const QString& address, uint16_t port) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2018-03-08 00:16:24 +01:00
										 |  |  |     m_address.setAddress(const_cast<QString&>(address)); | 
					
						
							|  |  |  |     m_port = port; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-31 00:40:54 +01:00
										 |  |  |     if (m_rtpBufferAudio) { | 
					
						
							|  |  |  |         m_rtpBufferAudio->setDestination(address, port); | 
					
						
							| 
									
										
										
										
											2018-01-29 01:59:03 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void AudioNetSink::addDestination(const QString& address, uint16_t port) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2018-01-31 00:40:54 +01:00
										 |  |  |     if (m_rtpBufferAudio) { | 
					
						
							|  |  |  |         m_rtpBufferAudio->addDestination(address, port); | 
					
						
							| 
									
										
										
										
											2018-01-29 01:59:03 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void AudioNetSink::deleteDestination(const QString& address, uint16_t port) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2018-01-31 00:40:54 +01:00
										 |  |  |     if (m_rtpBufferAudio) { | 
					
						
							|  |  |  |         m_rtpBufferAudio->deleteDestination(address, port); | 
					
						
							| 
									
										
										
										
											2018-01-29 01:59:03 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-13 07:53:38 +01:00
										 |  |  | void AudioNetSink::setParameters(Codec codec, bool stereo, int sampleRate) | 
					
						
							| 
									
										
										
										
											2018-03-27 00:09:52 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-02-13 07:53:38 +01:00
										 |  |  |     qDebug() << "AudioNetSink::setParameters:" | 
					
						
							|  |  |  |             << " codec: " << codec | 
					
						
							|  |  |  |             << " stereo: " << stereo | 
					
						
							|  |  |  |             << " sampleRate: " << sampleRate; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     m_codec = codec; | 
					
						
							| 
									
										
										
										
											2019-02-18 18:29:37 +01:00
										 |  |  |     m_stereo = stereo; | 
					
						
							| 
									
										
										
										
											2019-02-17 01:32:32 +01:00
										 |  |  |     m_sampleRate = sampleRate; | 
					
						
							| 
									
										
										
										
											2019-02-17 13:32:17 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-18 18:29:37 +01:00
										 |  |  |     setNewCodecData(); | 
					
						
							| 
									
										
										
										
											2019-02-13 07:53:38 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (m_rtpBufferAudio) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         switch (m_codec) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |         case CodecPCMA: | 
					
						
							|  |  |  |             m_audioCompressor.fillALaw(); | 
					
						
							|  |  |  |             m_rtpBufferAudio->setPayloadInformation(RTPSink::PayloadPCMA8, sampleRate); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case CodecPCMU: | 
					
						
							|  |  |  |             m_audioCompressor.fillULaw(); | 
					
						
							|  |  |  |             m_rtpBufferAudio->setPayloadInformation(RTPSink::PayloadPCMU8, sampleRate); | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2019-02-13 10:34:36 +01:00
										 |  |  |         case CodecL8: | 
					
						
							|  |  |  |             m_rtpBufferAudio->setPayloadInformation(RTPSink::PayloadL8, sampleRate); | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2019-02-17 03:40:11 +01:00
										 |  |  |         case CodecG722: | 
					
						
							|  |  |  |             m_rtpBufferAudio->setPayloadInformation(RTPSink::PayloadG722, sampleRate/2); | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2019-02-18 02:30:43 +01:00
										 |  |  |         case CodecOpus: | 
					
						
							|  |  |  |             m_rtpBufferAudio->setPayloadInformation(RTPSink::PayloadOpus, sampleRate); | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2019-02-13 07:53:38 +01:00
										 |  |  |         case CodecL16: // actually no codec
 | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |             m_rtpBufferAudio->setPayloadInformation(stereo ? RTPSink::PayloadL16Stereo : RTPSink::PayloadL16Mono, sampleRate); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-03-27 08:13:06 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-03-27 00:09:52 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-14 17:21:14 +01:00
										 |  |  | void AudioNetSink::setDecimation(uint32_t decimation) | 
					
						
							| 
									
										
										
										
											2018-01-29 01:59:03 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-02-14 17:21:14 +01:00
										 |  |  |     m_decimation = decimation < 1 ? 1 : decimation > 6 ? 6 : decimation; | 
					
						
							|  |  |  |     qDebug() << "AudioNetSink::setDecimation: " << m_decimation << " from: " << decimation; | 
					
						
							| 
									
										
										
										
											2019-02-18 18:29:37 +01:00
										 |  |  |     setNewCodecData(); | 
					
						
							| 
									
										
										
										
											2019-02-14 17:21:14 +01:00
										 |  |  |     m_decimationCount = 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-18 18:29:37 +01:00
										 |  |  | void AudioNetSink::setNewCodecData() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (m_codec == CodecOpus) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         m_codecInputSize = m_sampleRate / (m_decimation * 50); // 20ms = 1/50s - size is per channel
 | 
					
						
							|  |  |  |         m_codecInputSize = m_codecInputSize > 960 ? 960 : m_codecInputSize; // hard limit of 48 kS/s
 | 
					
						
							|  |  |  |         m_codecRatio = (m_sampleRate / m_decimation) / (AudioOpus::m_bitrate / 8); // compressor ratio
 | 
					
						
							|  |  |  |         qDebug() << "AudioNetSink::setNewCodecData: CodecOpus:" | 
					
						
							|  |  |  |             << " m_codecInputSize: " << m_codecInputSize | 
					
						
							|  |  |  |             << " m_codecRatio: " << m_codecRatio | 
					
						
							|  |  |  |             << " Fs: " << m_sampleRate/m_decimation | 
					
						
							|  |  |  |             << " stereo: " << m_stereo; | 
					
						
							|  |  |  |         m_opus.setEncoder(m_sampleRate/m_decimation, m_stereo ? 2 : 1); | 
					
						
							| 
									
										
										
										
											2019-02-19 02:07:26 +01:00
										 |  |  |         m_codecInputIndex = 0; | 
					
						
							|  |  |  |         m_bufferIndex = 0; | 
					
						
							| 
									
										
										
										
											2019-02-18 18:29:37 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     setDecimationFilters(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-17 13:32:17 +01:00
										 |  |  | void AudioNetSink::setDecimationFilters() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int decimatedSampleRate = m_sampleRate / m_decimation; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch (m_codec) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     case CodecPCMA: | 
					
						
							|  |  |  |     case CodecPCMU: | 
					
						
							| 
									
										
										
										
											2023-12-09 12:52:56 +01:00
										 |  |  |         m_audioFilterR.setDecimFilters(m_sampleRate, decimatedSampleRate, 3300.0, 300.0); | 
					
						
							|  |  |  |         m_audioFilterL.setDecimFilters(m_sampleRate, decimatedSampleRate, 3300.0, 300.0); | 
					
						
							| 
									
										
										
										
											2019-02-17 13:32:17 +01:00
										 |  |  |         break; | 
					
						
							|  |  |  |     case CodecG722: | 
					
						
							| 
									
										
										
										
											2023-12-09 12:52:56 +01:00
										 |  |  |         m_audioFilterR.setDecimFilters(m_sampleRate, decimatedSampleRate, 7000.0, 50.0); | 
					
						
							|  |  |  |         m_audioFilterL.setDecimFilters(m_sampleRate, decimatedSampleRate, 7000.0, 50.0); | 
					
						
							| 
									
										
										
										
											2019-02-17 13:32:17 +01:00
										 |  |  |         break; | 
					
						
							| 
									
										
										
										
											2019-02-18 02:30:43 +01:00
										 |  |  |     case CodecOpus: | 
					
						
							| 
									
										
										
										
											2019-02-17 13:32:17 +01:00
										 |  |  |     case CodecL8: | 
					
						
							|  |  |  |     case CodecL16: | 
					
						
							|  |  |  |     default: | 
					
						
							| 
									
										
										
										
											2023-12-09 12:52:56 +01:00
										 |  |  |         m_audioFilterR.setDecimFilters(m_sampleRate, decimatedSampleRate, 0.45*decimatedSampleRate, 50.0); | 
					
						
							|  |  |  |         m_audioFilterL.setDecimFilters(m_sampleRate, decimatedSampleRate, 0.45*decimatedSampleRate, 50.0); | 
					
						
							| 
									
										
										
										
											2019-02-17 13:32:17 +01:00
										 |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-14 17:21:14 +01:00
										 |  |  | void AudioNetSink::write(qint16 isample) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     qint16& sample = isample; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (m_decimation > 1) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-12-09 12:52:56 +01:00
										 |  |  |         float lpSample = m_audioFilterR.run(sample / 32768.0f); | 
					
						
							| 
									
										
										
										
											2019-02-14 17:21:14 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (m_decimationCount >= m_decimation - 1) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             sample = lpSample * 32768.0f; | 
					
						
							|  |  |  |             m_decimationCount = 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             m_decimationCount++; | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-08 00:16:24 +01:00
										 |  |  |     if (m_type == SinkUDP) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2019-02-17 06:15:12 +01:00
										 |  |  |         if (m_codec == CodecG722) | 
					
						
							| 
									
										
										
										
											2018-03-08 00:16:24 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2019-02-17 06:15:12 +01:00
										 |  |  |             if (m_bufferIndex >= 2*m_udpBlockSize) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 m_udpSocket->writeDatagram((const char*) m_data, (qint64 ) m_udpBlockSize, m_address, m_port); | 
					
						
							|  |  |  |                 m_bufferIndex = 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             if (m_bufferIndex >= m_udpBlockSize) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 m_udpSocket->writeDatagram((const char*) m_data, (qint64 ) m_udpBlockSize, m_address, m_port); | 
					
						
							|  |  |  |                 m_bufferIndex = 0; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2018-03-08 00:16:24 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-02-17 03:40:11 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         switch(m_codec) | 
					
						
							| 
									
										
										
										
											2018-03-08 00:16:24 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2019-02-17 03:40:11 +01:00
										 |  |  |         case CodecPCMA: | 
					
						
							|  |  |  |         case CodecPCMU: | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             qint8 *p = (qint8*) &m_data[m_bufferIndex]; | 
					
						
							|  |  |  |             *p = m_audioCompressor.compress8(sample); | 
					
						
							|  |  |  |             m_bufferIndex += sizeof(qint8); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case CodecL8: | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             qint8 *p = (qint8*) &m_data[m_bufferIndex]; | 
					
						
							|  |  |  |             *p = sample / 256; | 
					
						
							|  |  |  |             m_bufferIndex += sizeof(qint8); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case CodecG722: | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             qint16 *p = (qint16*) &m_data[m_udpBlockSize + 2*m_bufferIndex]; | 
					
						
							|  |  |  |             *p = sample; | 
					
						
							|  |  |  |             m_bufferIndex += 1; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-17 06:15:12 +01:00
										 |  |  |             if (m_bufferIndex == 2*m_udpBlockSize) { | 
					
						
							|  |  |  |                 m_g722.encode((uint8_t *) m_data, (const int16_t*) &m_data[m_udpBlockSize], 2*m_udpBlockSize); | 
					
						
							| 
									
										
										
										
											2019-02-13 07:53:38 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2019-02-18 07:57:03 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2019-02-19 00:36:32 +01:00
										 |  |  |         case CodecOpus: | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             if (m_codecInputIndex == m_codecInputSize) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 int nbBytes = m_opus.encode(m_codecInputSize, m_opusIn, (uint8_t *) m_data); | 
					
						
							|  |  |  |                 nbBytes = nbBytes > m_udpBlockSize ? m_udpBlockSize : nbBytes; | 
					
						
							|  |  |  |                 m_udpSocket->writeDatagram((const char*) m_data, (qint64 ) nbBytes, m_address, m_port); | 
					
						
							|  |  |  |                 m_codecInputIndex = 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             m_opusIn[m_codecInputIndex++] = sample; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2019-02-17 03:40:11 +01:00
										 |  |  |         case CodecL16: | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             qint16 *p = (qint16*) &m_data[m_bufferIndex]; | 
					
						
							|  |  |  |             *p = sample; | 
					
						
							|  |  |  |             m_bufferIndex += sizeof(qint16); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-03-08 00:16:24 +01:00
										 |  |  |     } | 
					
						
							|  |  |  |     else if (m_type == SinkRTP) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2019-02-13 07:53:38 +01:00
										 |  |  |         switch(m_codec) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |         case CodecPCMA: | 
					
						
							|  |  |  |         case CodecPCMU: | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             qint8 p = m_audioCompressor.compress8(sample); | 
					
						
							|  |  |  |             m_rtpBufferAudio->write((uint8_t *) &p); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2019-02-13 10:34:36 +01:00
										 |  |  |         case CodecL8: | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             qint8 p = sample / 256; | 
					
						
							|  |  |  |             m_rtpBufferAudio->write((uint8_t *) &p); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2019-02-17 03:40:11 +01:00
										 |  |  |         case CodecG722: | 
					
						
							|  |  |  |         { | 
					
						
							| 
									
										
										
										
											2019-02-17 06:15:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |             if (m_bufferIndex >= 2*m_g722BlockSize) | 
					
						
							| 
									
										
										
										
											2019-02-17 03:40:11 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2019-02-17 06:15:12 +01:00
										 |  |  |                 m_g722.encode((uint8_t *) m_data, (const int16_t*) &m_data[m_g722BlockSize], 2*m_g722BlockSize); | 
					
						
							| 
									
										
										
										
											2019-02-17 03:40:11 +01:00
										 |  |  |                 m_bufferIndex = 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-18 18:29:37 +01:00
										 |  |  |             if (m_bufferIndex % 2 == 0) { | 
					
						
							| 
									
										
										
										
											2019-02-17 06:15:12 +01:00
										 |  |  |                 m_rtpBufferAudio->write((uint8_t *) &m_data[m_bufferIndex/2]); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             qint16 *p = (qint16*) &m_data[m_g722BlockSize + 2*m_bufferIndex]; | 
					
						
							| 
									
										
										
										
											2019-02-17 03:40:11 +01:00
										 |  |  |             *p = sample; | 
					
						
							| 
									
										
										
										
											2019-02-17 06:15:12 +01:00
										 |  |  |             m_bufferIndex += 1; | 
					
						
							| 
									
										
										
										
											2019-02-17 03:40:11 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2019-02-19 00:36:32 +01:00
										 |  |  |         case CodecOpus: | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             if (m_codecInputIndex == m_codecInputSize) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 int nbBytes = m_opus.encode(m_codecInputSize, m_opusIn, (uint8_t *) m_data); | 
					
						
							|  |  |  |                 if (nbBytes != AudioOpus::m_bitrate/400) { // 8 bits for 1/50s (20ms)
 | 
					
						
							|  |  |  |                     qWarning("AudioNetSink::write: CodecOpus mono: unexpected output frame size: %d bytes", nbBytes); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 m_bufferIndex = 0; | 
					
						
							|  |  |  |                 m_codecInputIndex = 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (m_codecInputIndex % m_codecRatio == 0) { | 
					
						
							|  |  |  |                 m_rtpBufferAudio->write((uint8_t *) &m_data[m_bufferIndex++]); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             m_opusIn[m_codecInputIndex++] = sample; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2019-02-13 07:53:38 +01:00
										 |  |  |         case CodecL16: | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |             m_rtpBufferAudio->write((uint8_t *) &sample); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-01-29 01:59:03 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-14 17:21:14 +01:00
										 |  |  | void AudioNetSink::write(qint16 ilSample, qint16 irSample) | 
					
						
							| 
									
										
										
										
											2018-03-26 22:58:17 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-02-14 17:21:14 +01:00
										 |  |  |     qint16& lSample = ilSample; | 
					
						
							|  |  |  |     qint16& rSample = irSample; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (m_decimation > 1) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-12-09 12:52:56 +01:00
										 |  |  |         float lpLSample = m_audioFilterL.runLP(lSample / 32768.0f); | 
					
						
							|  |  |  |         float lpRSample = m_audioFilterR.runLP(rSample / 32768.0f); | 
					
						
							| 
									
										
										
										
											2019-02-14 17:21:14 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (m_decimationCount >= m_decimation - 1) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             lSample = lpLSample * 32768.0f; | 
					
						
							|  |  |  |             rSample = lpRSample * 32768.0f; | 
					
						
							|  |  |  |             m_decimationCount = 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             m_decimationCount++; | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-26 22:58:17 +02:00
										 |  |  |     if (m_type == SinkUDP) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (m_bufferIndex >= m_udpBlockSize) | 
					
						
							|  |  |  |         { | 
					
						
							| 
									
										
										
										
											2019-02-18 18:29:37 +01:00
										 |  |  |             m_udpSocket->writeDatagram((const char*) m_data, (qint64 ) m_udpBlockSize, m_address, m_port); | 
					
						
							| 
									
										
										
										
											2018-03-26 22:58:17 +02:00
										 |  |  |             m_bufferIndex = 0; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-02-18 18:29:37 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         switch(m_codec) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |         case CodecPCMA: | 
					
						
							|  |  |  |         case CodecPCMU: | 
					
						
							|  |  |  |         case CodecG722: | 
					
						
							|  |  |  |             break; // mono modes - do nothing
 | 
					
						
							| 
									
										
										
										
											2019-02-19 00:36:32 +01:00
										 |  |  |         case CodecOpus: | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             if (m_codecInputIndex == m_codecInputSize) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 int nbBytes = m_opus.encode(m_codecInputSize, m_opusIn, (uint8_t *) m_data); | 
					
						
							|  |  |  |                 nbBytes = nbBytes > m_udpBlockSize ? m_udpBlockSize : nbBytes; | 
					
						
							|  |  |  |                 m_udpSocket->writeDatagram((const char*) m_data, (qint64 ) nbBytes, m_address, m_port); | 
					
						
							|  |  |  |                 m_codecInputIndex = 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             m_opusIn[2*m_codecInputIndex]   = lSample; | 
					
						
							|  |  |  |             m_opusIn[2*m_codecInputIndex+1] = rSample; | 
					
						
							|  |  |  |             m_codecInputIndex++; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2019-02-18 18:29:37 +01:00
										 |  |  |         case CodecL8: | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             qint8 *p = (qint8*) &m_data[m_bufferIndex]; | 
					
						
							|  |  |  |             *p = lSample / 256; | 
					
						
							|  |  |  |             m_bufferIndex += sizeof(qint8); | 
					
						
							|  |  |  |             p = (qint8*) &m_data[m_bufferIndex]; | 
					
						
							|  |  |  |             *p = rSample / 256; | 
					
						
							|  |  |  |             m_bufferIndex += sizeof(qint8); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case CodecL16: | 
					
						
							|  |  |  |         default: | 
					
						
							| 
									
										
										
										
											2018-03-26 22:58:17 +02:00
										 |  |  |         { | 
					
						
							|  |  |  |             qint16 *p = (qint16*) &m_data[m_bufferIndex]; | 
					
						
							|  |  |  |             *p = lSample; | 
					
						
							|  |  |  |             m_bufferIndex += sizeof(qint16); | 
					
						
							|  |  |  |             p = (qint16*) &m_data[m_bufferIndex]; | 
					
						
							|  |  |  |             *p = rSample; | 
					
						
							|  |  |  |             m_bufferIndex += sizeof(qint16); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-02-18 18:29:37 +01:00
										 |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-03-26 22:58:17 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |     else if (m_type == SinkRTP) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2019-02-18 18:29:37 +01:00
										 |  |  |         switch(m_codec) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |         case CodecPCMA: | 
					
						
							|  |  |  |         case CodecPCMU: | 
					
						
							|  |  |  |         case CodecG722: | 
					
						
							|  |  |  |             break; // mono modes - do nothing
 | 
					
						
							| 
									
										
										
										
											2019-02-19 00:36:32 +01:00
										 |  |  |         case CodecOpus: | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             if (m_codecInputIndex == m_codecInputSize) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 int nbBytes = m_opus.encode(m_codecInputSize, m_opusIn, (uint8_t *) m_data); | 
					
						
							|  |  |  |                 if (nbBytes != AudioOpus::m_bitrate/400) { // 8 bits for 1/50s (20ms)
 | 
					
						
							|  |  |  |                     qWarning("AudioNetSink::write: CodecOpus stereo: unexpected output frame size: %d bytes", nbBytes); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 m_bufferIndex = 0; | 
					
						
							|  |  |  |                 m_codecInputIndex = 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (m_codecInputIndex % m_codecRatio == 0) { | 
					
						
							|  |  |  |                 m_rtpBufferAudio->write((uint8_t *) &m_data[m_bufferIndex++]); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             m_opusIn[2*m_codecInputIndex]   = lSample; | 
					
						
							|  |  |  |             m_opusIn[2*m_codecInputIndex+1] = rSample; | 
					
						
							|  |  |  |             m_codecInputIndex++; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2019-02-18 18:29:37 +01:00
										 |  |  |         case CodecL8: | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             qint8 pl = lSample / 256; | 
					
						
							|  |  |  |             qint8 pr = rSample / 256; | 
					
						
							|  |  |  |             m_rtpBufferAudio->write((uint8_t *) &pl, (uint8_t *) &pr); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case CodecL16: | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |             m_rtpBufferAudio->write((uint8_t *) &lSample, (uint8_t *) &rSample); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-03-26 22:58:17 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-06 02:23:47 +01:00
										 |  |  | void AudioNetSink::moveToThread(QThread *thread) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2018-03-08 00:16:24 +01:00
										 |  |  |     m_udpSocket->moveToThread(thread); | 
					
						
							| 
									
										
										
										
											2018-03-06 02:23:47 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-01-29 01:59:03 +01:00
										 |  |  | 
 |