Implement the "vocoder extension" to use Codec 2 with D-STAR

Implementation of the "vocoder extension" by SV9OAN, as described in https://github.com/chazapis/pydv, which allows the use of the open source Codec 2 with D-STAR. With the changes here, xlxd/ambed can be used to transcode and bridge the two formats.

The solution implemented uses an additional DExtra listener on a different port (30201 instead of 30001). The new port is to be used by reflectors using the "ORF" prefix (Open ReFlector). Any client connected to an ORF reflector will receive streams encoded with Codec 2. All other D-STAR protocol handlers will still send out data encoded with AMBE. Note that the protocol/port only affects data transmitted by the reflector. The stream vocoder is recognized by all protocol handlers, so a client can still transmit data using any vocoder on any port. The rationale behind this is that DExtra links may be used by repeaters or other reflectors, so it is not really possible to know what their clients support. So, nothing will change when linking a repeater to an XRF reflector, but will do when linking to an ORF one.

Summary of code changes in xlxd:
* Added protocol "DExtra Open", listening on port 30201. The protocol handler is subclassed from DExtra. Minor changes were required to make the DExtra implementation more "abstract" to allow that.
* Codec used no longer a protocol client property. All D-STAR protocol handlers use the header to determine the protocol. DMR protocol handlers just specify the appropriate codec when opening a stream.
* DV frames now also include Codec 2 data.
* Interface with ambed changed to 1 codec in, 2 codecs out.
* Interface with other xlx peers changed to accommodate the enlarged DV frames, so compatible peers will exchange fully transcoded streams.
* Major version changed to 3, to recognize peer compatibility.

Summary of code changes to ambed:

* Added (virtual) interface class for Codec 2.
* Vocodec channels now have 3 interfaces (1 in, 2 out) and are grouped together in triplets. Each group contains all possible permutations of respective interfaces - 3 channels. Only one channel from each group can be used at a given time.
* When an incoming packet is decoded in the interface loop, it is duplicated and sent to two separate voice queues (one for each outgoing codec).
* Interface changed to 1 codec in, 2 codecs out.
* Major version changed to 2, as the interface is incompatible with previous versions.
pull/118/head
Antony Chazapis 7 years ago
parent b3360b66e2
commit 9581d729a7

@ -0,0 +1,242 @@
//
// ccodec2interface.cpp
// ambed
//
// Created by Antony Chazapis (SV9OAN) on 26/12/2018.
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
//
// ----------------------------------------------------------------------------
// This file is part of ambed.
//
// xlxd 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, either version 3 of the License, or
// (at your option) any later version.
//
// xlxd 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 for more details.
//
// You should have received a copy of the GNU General Public License
// along with Foobar. If not, see <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include "main.h"
#include <string.h>
#include "ctimepoint.h"
#include "cambepacket.h"
#include "cvoicepacket.h"
#include "ccodec2interface.h"
#include "cvocodecchannel.h"
////////////////////////////////////////////////////////////////////////////////////////
// constructor
CCodec2Interface::CCodec2Interface()
: CVocodecInterface()
{}
////////////////////////////////////////////////////////////////////////////////////////
// destructor
CCodec2Interface::~CCodec2Interface()
{
codec2_destroy(codec2_state);
}
////////////////////////////////////////////////////////////////////////////////////////
// initialization
bool CCodec2Interface::Init(void)
{
bool ok = true;
// create codec state
codec2_state = codec2_create(CODEC2_MODE_3200);
if (codec2_state == NULL)
{
ok = false;
}
// base class
if ( ok )
{
ok &= CVocodecInterface::Init();
}
// done
return ok;
}
////////////////////////////////////////////////////////////////////////////////////////
// task
void CCodec2Interface::Task(void)
{
CPacketQueue *Queue;
CVocodecChannel *Channel;
CAmbePacket AmbePacket;
CVoicePacket VoicePacket;
bool done;
// process the streams (channels) incoming queue
do
{
done = true;
for ( int i = 0; i < GetNbChannels(); i++ )
{
// get active outgoing channel for interface channel
Channel = GetChannelWithChannelOut(i);
// any packet in voice queue 1 ?
if ( Channel != NULL && Channel->IsInterfaceOut1(this) )
{
CVoicePacket *Packet = NULL;
Queue = Channel->GetVoiceQueue1();
if ( !Queue->empty() )
{
// get packet
Packet = (CVoicePacket *)Queue->front();
Queue->pop();
}
Channel->ReleaseVoiceQueue1();
if ( Packet != NULL )
{
// this is second step of transcoding
// we just received from a decoded speech packet
// encode and cpush back to relevant channel outcoming queue
EncodeVoicePacket(Packet, &AmbePacket);
AmbePacket.SetPid(Packet->GetPid());
delete Packet;
CAmbePacket *clone = new CAmbePacket(AmbePacket);
Queue = Channel->GetPacketQueueOut1();
Queue->push(clone);
Channel->ReleasePacketQueueOut1();
// done
done = false;
}
}
// any packet in voice queue 1 ?
if ( Channel != NULL && Channel->IsInterfaceOut2(this) )
{
CVoicePacket *Packet = NULL;
Queue = Channel->GetVoiceQueue2();
if ( !Queue->empty() )
{
// get packet
Packet = (CVoicePacket *)Queue->front();
Queue->pop();
}
Channel->ReleaseVoiceQueue2();
if ( Packet != NULL )
{
// this is second step of transcoding
// we just received from a decoded speech packet
// encode and cpush back to relevant channel outcoming queue
EncodeVoicePacket(Packet, &AmbePacket);
AmbePacket.SetPid(Packet->GetPid());
delete Packet;
CAmbePacket *clone = new CAmbePacket(AmbePacket);
Queue = Channel->GetPacketQueueOut2();
Queue->push(clone);
Channel->ReleasePacketQueueOut2();
// done
done = false;
}
}
// get active incoming channel for interface channel
Channel = GetChannelWithChannelIn(i);
// any packet in ambe queue for us ?
if ( Channel != NULL && Channel->IsInterfaceIn(this) )
{
CAmbePacket *Packet = NULL;
Queue = Channel->GetPacketQueueIn();
if ( !Queue->empty() )
{
// get packet
Packet = (CAmbePacket *)Queue->front();
Queue->pop();
}
Channel->ReleasePacketQueueIn();
if ( Packet != NULL )
{
// this is first step of transcoding
// a fresh new packet to be transcoded is showing up
// decode and copy the result into both voice queues
DecodeAmbePacket(Packet, &VoicePacket);
VoicePacket.SetPid(Packet->GetPid());
delete Packet;
CVoicePacket *clone1 = new CVoicePacket(VoicePacket);
clone1->ApplyGain(Channel->GetSpeechGain());
CVoicePacket *clone2 = new CVoicePacket(*clone1);
Queue = Channel->GetVoiceQueue1();
Queue->push(clone1);
Channel->ReleaseVoiceQueue1();
Queue = Channel->GetVoiceQueue2();
Queue->push(clone2);
Channel->ReleaseVoiceQueue2();
// done
done = false;
}
}
}
} while (!done);
// and wait a bit
CTimePoint::TaskSleepFor(2);
}
////////////////////////////////////////////////////////////////////////////////////////
// decoder helper
void CCodec2Interface::DecodeAmbePacket(CAmbePacket *PacketIn, CVoicePacket *PacketOut)
{
short voice[160];
codec2_decode(codec2_state, voice, (unsigned char *)PacketIn->GetAmbe());
for ( int i = 0; i < 160; i++ )
{
voice[i] = MAKEWORD(HIBYTE(voice[i]), LOBYTE(voice[i]));
}
PacketOut->SetVoice((uint8 *)voice, 160 * 2);
}
////////////////////////////////////////////////////////////////////////////////////////
// encoder helpers
void CCodec2Interface::EncodeVoicePacket(CVoicePacket *PacketIn, CAmbePacket *PacketOut)
{
unsigned char ambe[AMBE_SIZE];
short voice[160];
::memcpy(voice, (short *)PacketIn->GetVoice(), 160 * 2);
for ( int i = 0; i < 160; i++ )
{
voice[i] = MAKEWORD(HIBYTE(voice[i]), LOBYTE(voice[i]));
}
codec2_encode(codec2_state, ambe, voice);
ambe[8] = 0x00;
PacketOut->SetCodec(CODEC_CODEC2);
PacketOut->SetAmbe((uint8 *)ambe);
}

@ -0,0 +1,68 @@
//
// ccodec2interface.h
// ambed
//
// Created by Antony Chazapis (SV9OAN) on 26/12/2018.
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
//
// ----------------------------------------------------------------------------
// This file is part of ambed.
//
// xlxd 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, either version 3 of the License, or
// (at your option) any later version.
//
// xlxd 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 for more details.
//
// You should have received a copy of the GNU General Public License
// along with Foobar. If not, see <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#ifndef ccodec2interface_h
#define ccodec2interface_h
#include "cvocodecinterface.h"
#include <codec2/codec2.h>
////////////////////////////////////////////////////////////////////////////////////////
// class
class CCodec2Interface : public CVocodecInterface
{
public:
// constructors
CCodec2Interface();
// destructor
virtual ~CCodec2Interface();
// initialization
bool Init(void);
// get
const char *GetName(void) const { return "Codec 2"; }
// manage channels
int GetNbChannels(void) const { return 1; }
uint8 GetChannelCodec(int) const { return CODEC_CODEC2; }
// task
void Task(void);
protected:
// decoder helper
void DecodeAmbePacket(CAmbePacket *, CVoicePacket *);
// encoder helpers
void EncodeVoicePacket(CVoicePacket *, CAmbePacket *);
// data
struct CODEC2 *codec2_state;
};
////////////////////////////////////////////////////////////////////////////////////////
#endif /* ccodec2interface_h */

@ -124,7 +124,7 @@ void CController::Task(void)
CIp Ip;
CCallsign Callsign;
uint8 CodecIn;
uint8 CodecOut;
uint8 CodecsOut;
uint16 StreamId;
CStream *Stream;
@ -132,12 +132,12 @@ void CController::Task(void)
if ( m_Socket.Receive(&Buffer, &Ip, 20) != -1 )
{
// crack packet
if ( IsValidOpenstreamPacket(Buffer, &Callsign, &CodecIn, &CodecOut) )
if ( IsValidOpenstreamPacket(Buffer, &Callsign, &CodecIn, &CodecsOut) )
{
std::cout << "Stream Open from " << Callsign << std::endl;
// try create the stream
Stream = OpenStream(Callsign, Ip, CodecIn, CodecOut);
Stream = OpenStream(Callsign, Ip, CodecIn, CodecsOut);
// send back details
if ( Stream != NULL )
@ -202,14 +202,14 @@ void CController::Task(void)
////////////////////////////////////////////////////////////////////////////////////////
// streams management
CStream *CController::OpenStream(const CCallsign &Callsign, const CIp &Ip, uint8 CodecIn, uint8 CodecOut)
CStream *CController::OpenStream(const CCallsign &Callsign, const CIp &Ip, uint8 CodecIn, uint8 CodecsOut)
{
CStream *stream = NULL;
// create a new stream
m_uiLastStreamId = (m_uiLastStreamId + 1);
m_uiLastStreamId = (m_uiLastStreamId == NB_MAX_STREAMS+1) ? 1 : m_uiLastStreamId;
stream = new CStream(m_uiLastStreamId, Callsign, Ip, CodecIn, CodecOut);
stream = new CStream(m_uiLastStreamId, Callsign, Ip, CodecIn, CodecsOut);
if ( stream->Init(TRANSCODER_PORT+m_uiLastStreamId) )
{
std::cout << "Opened stream " << m_uiLastStreamId << std::endl;
@ -293,7 +293,7 @@ bool CController::IsValidKeepAlivePacket(const CBuffer &Buffer, CCallsign *Calls
return valid;
}
bool CController::IsValidOpenstreamPacket(const CBuffer &Buffer, CCallsign *Callsign, uint8 *CodecIn, uint8 *CodecOut)
bool CController::IsValidOpenstreamPacket(const CBuffer &Buffer, CCallsign *Callsign, uint8 *CodecIn, uint8 *CodecsOut)
{
uint8 tag[] = { 'A','M','B','E','D','O','S' };
@ -303,10 +303,10 @@ bool CController::IsValidOpenstreamPacket(const CBuffer &Buffer, CCallsign *Call
// get callsign here
Callsign->SetCallsign(&(Buffer.data()[7]), 8);
*CodecIn = Buffer.data()[15];
*CodecOut = Buffer.data()[16];
*CodecsOut = Buffer.data()[16];
// valid ?
valid = Callsign->IsValid() && IsValidCodecIn(*CodecIn) && IsValidCodecOut(*CodecOut);
valid = Callsign->IsValid() && IsValidCodecIn(*CodecIn) && IsValidCodecsOut(*CodecsOut);
}
return valid;
}
@ -347,7 +347,7 @@ void CController::EncodeStreamDescrPacket(CBuffer *Buffer, const CStream &Stream
// codecin
Buffer->Append((uint8)Stream.GetCodecIn());
// codecout
Buffer->Append((uint8)Stream.GetCodecOut());
Buffer->Append((uint8)Stream.GetCodecsOut());
}
void CController::EncodeNoStreamAvailablePacket(CBuffer *Buffer)
@ -363,11 +363,13 @@ void CController::EncodeNoStreamAvailablePacket(CBuffer *Buffer)
bool CController::IsValidCodecIn(uint8 codec)
{
return ((codec == CODEC_AMBEPLUS) || (codec == CODEC_AMBE2PLUS) );
return ((codec == CODEC_AMBEPLUS) || (codec == CODEC_AMBE2PLUS) || (codec == CODEC_CODEC2));
}
bool CController::IsValidCodecOut(uint8 codec)
bool CController::IsValidCodecsOut(uint8 codec)
{
return ((codec == CODEC_AMBEPLUS) || (codec == CODEC_AMBE2PLUS) );
return ((codec == (CODEC_AMBEPLUS | CODEC_CODEC2)) ||
(codec == (CODEC_AMBE2PLUS | CODEC_CODEC2)) ||
(codec == (CODEC_AMBEPLUS | CODEC_AMBE2PLUS)));
}

@ -76,7 +76,7 @@ protected:
// codec helpers
bool IsValidCodecIn(uint8);
bool IsValidCodecOut(uint8);
bool IsValidCodecsOut(uint8);
protected:
// control socket

@ -28,6 +28,7 @@
#include "cusb3003interface.h"
#include "cusb3003hrinterface.h"
#include "cusb3003df2etinterface.h"
#include "ccodec2interface.h"
#include "cftdidevicedescr.h"
@ -193,6 +194,25 @@ int CFtdiDeviceDescr::GetNbChannels(void) const
return iNbChs;
}
////////////////////////////////////////////////////////////////////////////////////////
// factory helper
void CFtdiDeviceDescr::CreateChannelGroup(CVocodecInterface *InterfaceAmbe, int ChannelAmbe, CVocodecInterface *InterfaceAmbePlus, int ChannelAmbePlus, CVocodecInterface *InterfaceCodec2, int ChannelCodec2, std::vector<CVocodecChannel *>*channels)
{
CVocodecChannel *ChannelA = new CVocodecChannel(InterfaceAmbe, ChannelAmbe, InterfaceAmbePlus, ChannelAmbePlus, InterfaceCodec2, ChannelCodec2, CODECGAIN_AMBEPLUS);
CVocodecChannel *ChannelB = new CVocodecChannel(InterfaceAmbePlus, ChannelAmbePlus, InterfaceAmbe, ChannelAmbe, InterfaceCodec2, ChannelCodec2, CODECGAIN_AMBE2PLUS);
CVocodecChannel *ChannelC = new CVocodecChannel(InterfaceCodec2, ChannelCodec2, InterfaceAmbe, ChannelAmbe, InterfaceAmbePlus, ChannelAmbePlus, CODECGAIN_CODEC2);
ChannelA->AddGroupChannel(ChannelB);
ChannelA->AddGroupChannel(ChannelC);
ChannelB->AddGroupChannel(ChannelA);
ChannelB->AddGroupChannel(ChannelC);
ChannelC->AddGroupChannel(ChannelA);
ChannelC->AddGroupChannel(ChannelB);
channels->push_back(ChannelA);
channels->push_back(ChannelB);
channels->push_back(ChannelC);
}
////////////////////////////////////////////////////////////////////////////////////////
// DVSI's USB-3012 factory helper
//
@ -214,69 +234,31 @@ int CFtdiDeviceDescr::CreateUsb3012(CFtdiDeviceDescr *descr, std::vector<CVocode
new CUsb3003HRInterface(descr->GetVid(), descr->GetPid(), descr->GetChannelDescription(2), descr->GetChannelSerialNumber(2));
CUsb3003HRInterface *Usb3003D =
new CUsb3003HRInterface(descr->GetVid(), descr->GetPid(), descr->GetChannelDescription(3), descr->GetChannelSerialNumber(3));
// create the virtual interfaces for Codec 2
CCodec2Interface *Codec2A = new CCodec2Interface();
CCodec2Interface *Codec2B = new CCodec2Interface();
CCodec2Interface *Codec2C = new CCodec2Interface();
CCodec2Interface *Codec2D = new CCodec2Interface();
CCodec2Interface *Codec2E = new CCodec2Interface();
CCodec2Interface *Codec2F = new CCodec2Interface();
// init the interfaces
if ( Usb3003A->Init(CODEC_AMBEPLUS) && Usb3003B->Init(CODEC_AMBE2PLUS) &&
Usb3003C->Init(CODEC_AMBEPLUS) && Usb3003D->Init(CODEC_AMBE2PLUS) )
Usb3003C->Init(CODEC_AMBEPLUS) && Usb3003D->Init(CODEC_AMBE2PLUS) &&
Codec2A->Init() && Codec2B->Init() &&
Codec2C->Init() && Codec2D->Init() &&
Codec2E->Init() && Codec2F->Init() )
{
CVocodecChannel *Channel;
// create all channels
{
// ch1
Channel = new CVocodecChannel(Usb3003A, 0, Usb3003A, 1, CODECGAIN_AMBEPLUS);
channels->push_back(Channel);
Usb3003A->AddChannel(Channel);
// ch2
Channel = new CVocodecChannel(Usb3003A, 1, Usb3003A, 0, CODECGAIN_AMBE2PLUS);
channels->push_back(Channel);
Usb3003A->AddChannel(Channel);
// ch3
Channel = new CVocodecChannel(Usb3003B, 0, Usb3003B, 1, CODECGAIN_AMBEPLUS);
channels->push_back(Channel);
Usb3003B->AddChannel(Channel);
// ch4
Channel = new CVocodecChannel(Usb3003B, 1, Usb3003B, 0, CODECGAIN_AMBE2PLUS);
channels->push_back(Channel);
Usb3003B->AddChannel(Channel);
// ch5
Channel = new CVocodecChannel(Usb3003A, 2, Usb3003B, 2, CODECGAIN_AMBEPLUS);
channels->push_back(Channel);
Usb3003A->AddChannel(Channel);
Usb3003B->AddChannel(Channel);
// ch6
Channel = new CVocodecChannel(Usb3003B, 2, Usb3003A, 2, CODECGAIN_AMBE2PLUS);
channels->push_back(Channel);
Usb3003A->AddChannel(Channel);
Usb3003B->AddChannel(Channel);
// ch7
Channel = new CVocodecChannel(Usb3003C, 0, Usb3003C, 1, CODECGAIN_AMBEPLUS);
channels->push_back(Channel);
Usb3003C->AddChannel(Channel);
// ch8
Channel = new CVocodecChannel(Usb3003C, 1, Usb3003C, 0, CODECGAIN_AMBE2PLUS);
channels->push_back(Channel);
Usb3003C->AddChannel(Channel);
// ch9
Channel = new CVocodecChannel(Usb3003D, 0, Usb3003D, 1, CODECGAIN_AMBEPLUS);
channels->push_back(Channel);
Usb3003D->AddChannel(Channel);
// ch10
Channel = new CVocodecChannel(Usb3003D, 1, Usb3003D, 0, CODECGAIN_AMBE2PLUS);
channels->push_back(Channel);
Usb3003D->AddChannel(Channel);
// ch11
Channel = new CVocodecChannel(Usb3003C, 2, Usb3003D, 2, CODECGAIN_AMBEPLUS);
channels->push_back(Channel);
Usb3003C->AddChannel(Channel);
Usb3003D->AddChannel(Channel);
// ch12
Channel = new CVocodecChannel(Usb3003D, 2, Usb3003C, 2, CODECGAIN_AMBE2PLUS);
channels->push_back(Channel);
Usb3003C->AddChannel(Channel);
Usb3003D->AddChannel(Channel);
//done
nStreams = 12;
}
// create the channels in groups
CreateChannelGroup(Usb3003A, 0, Usb3003A, 1, Codec2A, 0, channels);
CreateChannelGroup(Usb3003B, 0, Usb3003B, 1, Codec2B, 0, channels);
CreateChannelGroup(Usb3003A, 2, Usb3003B, 2, Codec2C, 0, channels);
CreateChannelGroup(Usb3003C, 0, Usb3003C, 1, Codec2D, 0, channels);
CreateChannelGroup(Usb3003D, 0, Usb3003D, 1, Codec2E, 0, channels);
CreateChannelGroup(Usb3003C, 2, Usb3003D, 2, Codec2F, 0, channels);
// done
nStreams = 18;
}
else
{
@ -285,6 +267,12 @@ int CFtdiDeviceDescr::CreateUsb3012(CFtdiDeviceDescr *descr, std::vector<CVocode
delete Usb3003B;
delete Usb3003C;
delete Usb3003D;
delete Codec2A;
delete Codec2B;
delete Codec2C;
delete Codec2D;
delete Codec2E;
delete Codec2F;
}
// done
@ -307,48 +295,31 @@ int CFtdiDeviceDescr::CreateUsb3006(CFtdiDeviceDescr *descr, std::vector<CVocode
new CUsb3003Interface(descr->GetVid(), descr->GetPid(), descr->GetChannelDescription(0), descr->GetChannelSerialNumber(0));
CUsb3003Interface *Usb3003B =
new CUsb3003Interface(descr->GetVid(), descr->GetPid(), descr->GetChannelDescription(1), descr->GetChannelSerialNumber(1));
// create the virtual interfaces for Codec 2
CCodec2Interface *Codec2A = new CCodec2Interface();
CCodec2Interface *Codec2B = new CCodec2Interface();
CCodec2Interface *Codec2C = new CCodec2Interface();
// init the interfaces
if ( Usb3003A->Init(CODEC_AMBEPLUS) && Usb3003B->Init(CODEC_AMBE2PLUS) )
if ( Usb3003A->Init(CODEC_AMBEPLUS) && Usb3003B->Init(CODEC_AMBE2PLUS) &&
Codec2A->Init() && Codec2B->Init() && Codec2C->Init() )
{
CVocodecChannel *Channel;
// create all channels
{
// ch1
Channel = new CVocodecChannel(Usb3003A, 0, Usb3003A, 1, CODECGAIN_AMBEPLUS);
channels->push_back(Channel);
Usb3003A->AddChannel(Channel);
// ch2
Channel = new CVocodecChannel(Usb3003A, 1, Usb3003A, 0, CODECGAIN_AMBE2PLUS);
channels->push_back(Channel);
Usb3003A->AddChannel(Channel);
// ch3
Channel = new CVocodecChannel(Usb3003B, 0, Usb3003B, 1, CODECGAIN_AMBEPLUS);
channels->push_back(Channel);
Usb3003B->AddChannel(Channel);
// ch4
Channel = new CVocodecChannel(Usb3003B, 1, Usb3003B, 0, CODECGAIN_AMBE2PLUS);
channels->push_back(Channel);
Usb3003B->AddChannel(Channel);
// ch5
Channel = new CVocodecChannel(Usb3003A, 2, Usb3003B, 2, CODECGAIN_AMBEPLUS);
channels->push_back(Channel);
Usb3003A->AddChannel(Channel);
Usb3003B->AddChannel(Channel);
// ch6
Channel = new CVocodecChannel(Usb3003B, 2, Usb3003A, 2, CODECGAIN_AMBE2PLUS);
channels->push_back(Channel);
Usb3003A->AddChannel(Channel);
Usb3003B->AddChannel(Channel);
//done
nStreams = 6;
}
// create the channels in groups
CreateChannelGroup(Usb3003A, 0, Usb3003A, 1, Codec2A, 0, channels);
CreateChannelGroup(Usb3003B, 0, Usb3003B, 1, Codec2B, 0, channels);
CreateChannelGroup(Usb3003A, 2, Usb3003B, 2, Codec2C, 0, channels);
// done
nStreams = 9;
}
else
{
// cleanup
delete Usb3003A;
delete Usb3003B;
delete Codec2A;
delete Codec2B;
delete Codec2C;
}
// done
@ -372,29 +343,23 @@ int CFtdiDeviceDescr::CreateUsb3003(CFtdiDeviceDescr *descr, std::vector<CVocode
// create the interfaces for the 3003 chip
CUsb3003Interface *Usb3003 = InstantiateUsb3003(descr);
// create the virtual interfaces for Codec 2
CCodec2Interface *Codec2 = new CCodec2Interface();
// init the interface
if ( (Usb3003 != NULL) && Usb3003->Init(CODEC_NONE) )
if ( (Usb3003 != NULL) && Usb3003->Init(CODEC_NONE) && Codec2->Init() )
{
CVocodecChannel *Channel;
// create all channels
{
// ch1
Channel = new CVocodecChannel(Usb3003, 0, Usb3003, 1, CODECGAIN_AMBEPLUS);
channels->push_back(Channel);
Usb3003->AddChannel(Channel);
// ch2
Channel = new CVocodecChannel(Usb3003, 1, Usb3003, 0, CODECGAIN_AMBE2PLUS);
channels->push_back(Channel);
Usb3003->AddChannel(Channel);
// done
nStreams = 2;
}
// create the channels in groups
CreateChannelGroup(Usb3003, 0, Usb3003, 1, Codec2, 0, channels);
// done
nStreams = 3;
}
else
{
// cleanup
delete Usb3003;
delete Codec2;
}
// done
@ -408,32 +373,24 @@ int CFtdiDeviceDescr::CreateUsb3003(CFtdiDeviceDescr *descr, std::vector<CVocode
int CFtdiDeviceDescr::CreatePair(CUsb3000Interface *Usb3000A, CUsb3000Interface *Usb3000B, std::vector<CVocodecChannel *>*channels)
{
int nStreams = 0;
// create the virtual interfaces for Codec 2
CCodec2Interface *Codec2 = new CCodec2Interface();
// init the interfaces
if ( Usb3000A->Init(CODEC_AMBEPLUS) && Usb3000B->Init(CODEC_AMBE2PLUS) )
if ( Usb3000A->Init(CODEC_AMBEPLUS) && Usb3000B->Init(CODEC_AMBE2PLUS) && Codec2->Init() )
{
CVocodecChannel *Channel;
// create all channels
{
// ch1
Channel = new CVocodecChannel(Usb3000A, 0, Usb3000B, 0, CODECGAIN_AMBEPLUS);
channels->push_back(Channel);
Usb3000A->AddChannel(Channel);
Usb3000B->AddChannel(Channel);
// ch2
Channel = new CVocodecChannel(Usb3000B, 0, Usb3000A, 0, CODECGAIN_AMBE2PLUS);
channels->push_back(Channel);
Usb3000A->AddChannel(Channel);
Usb3000B->AddChannel(Channel);
// done
nStreams = 2;
}
// create the channels in groups
CreateChannelGroup(Usb3000A, 0, Usb3000B, 0, Codec2, 0, channels);
// done
nStreams = 3;
}
else
{
// cleanup
delete Usb3000A;
delete Usb3000B;
delete Codec2;
}
// done
@ -447,48 +404,31 @@ int CFtdiDeviceDescr::CreatePair(CUsb3000Interface *Usb3000A, CUsb3000Interface
int CFtdiDeviceDescr::CreatePair(CUsb3003Interface *Usb3003A, CUsb3003Interface *Usb3003B, std::vector<CVocodecChannel *>*channels)
{
int nStreams = 0;
// create the virtual interfaces for Codec 2
CCodec2Interface *Codec2A = new CCodec2Interface();
CCodec2Interface *Codec2B = new CCodec2Interface();
CCodec2Interface *Codec2C = new CCodec2Interface();
// init the interfaces
if ( Usb3003A->Init(CODEC_AMBEPLUS) && Usb3003B->Init(CODEC_AMBE2PLUS) )
if ( Usb3003A->Init(CODEC_AMBEPLUS) && Usb3003B->Init(CODEC_AMBE2PLUS) &&
Codec2A->Init() && Codec2B->Init() && Codec2C->Init() )
{
CVocodecChannel *Channel;
// create all channels
{
// ch1
Channel = new CVocodecChannel(Usb3003A, 0, Usb3003A, 1, CODECGAIN_AMBEPLUS);
channels->push_back(Channel);
Usb3003A->AddChannel(Channel);
// ch2
Channel = new CVocodecChannel(Usb3003A, 1, Usb3003A, 0, CODECGAIN_AMBE2PLUS);
channels->push_back(Channel);
Usb3003A->AddChannel(Channel);
// ch3
Channel = new CVocodecChannel(Usb3003B, 0, Usb3003B, 1, CODECGAIN_AMBEPLUS);
channels->push_back(Channel);
Usb3003B->AddChannel(Channel);
// ch4
Channel = new CVocodecChannel(Usb3003B, 1, Usb3003B, 0, CODECGAIN_AMBE2PLUS);
channels->push_back(Channel);
Usb3003B->AddChannel(Channel);
// ch5
Channel = new CVocodecChannel(Usb3003A, 2, Usb3003B, 2, CODECGAIN_AMBEPLUS);
channels->push_back(Channel);
Usb3003A->AddChannel(Channel);
Usb3003B->AddChannel(Channel);
// ch6
Channel = new CVocodecChannel(Usb3003B, 2, Usb3003A, 2, CODECGAIN_AMBE2PLUS);
channels->push_back(Channel);
Usb3003A->AddChannel(Channel);
Usb3003B->AddChannel(Channel);
// done
nStreams = 6;
}
// create the channels in groups
CreateChannelGroup(Usb3003A, 0, Usb3003A, 1, Codec2A, 0, channels);
CreateChannelGroup(Usb3003B, 0, Usb3003B, 1, Codec2B, 0, channels);
CreateChannelGroup(Usb3003A, 2, Usb3003B, 2, Codec2C, 0, channels);
// done
nStreams = 9;
}
else
{
// cleanup
delete Usb3003A;
delete Usb3003B;
delete Codec2A;
delete Codec2B;
delete Codec2C;
}
// done
@ -501,40 +441,28 @@ int CFtdiDeviceDescr::CreatePair(CUsb3003Interface *Usb3003A, CUsb3003Interface
int CFtdiDeviceDescr::CreatePair(CUsb3003Interface *Usb3003A, CUsb3000Interface *Usb3000B, std::vector<CVocodecChannel *>*channels)
{
int nStreams = 0;
// create the virtual interfaces for Codec 2
CCodec2Interface *Codec2A = new CCodec2Interface();
CCodec2Interface *Codec2B = new CCodec2Interface();
// init the interfaces
if ( Usb3003A->Init(CODEC_AMBEPLUS) && Usb3000B->Init(CODEC_AMBE2PLUS) )
if ( Usb3003A->Init(CODEC_AMBEPLUS) && Usb3000B->Init(CODEC_AMBE2PLUS) &&
Codec2A->Init() && Codec2B->Init() )
{
CVocodecChannel *Channel;
// create all channels
{
// ch1
Channel = new CVocodecChannel(Usb3003A, 0, Usb3003A, 1, CODECGAIN_AMBEPLUS);
channels->push_back(Channel);
Usb3003A->AddChannel(Channel);
// ch2
Channel = new CVocodecChannel(Usb3003A, 1, Usb3003A, 0, CODECGAIN_AMBE2PLUS);
channels->push_back(Channel);
Usb3003A->AddChannel(Channel);
// ch3
Channel = new CVocodecChannel(Usb3003A, 2, Usb3000B, 0, CODECGAIN_AMBEPLUS);
channels->push_back(Channel);
Usb3003A->AddChannel(Channel);
Usb3000B->AddChannel(Channel);
// ch4
Channel = new CVocodecChannel(Usb3000B, 0, Usb3003A, 2, CODECGAIN_AMBE2PLUS);
channels->push_back(Channel);
Usb3003A->AddChannel(Channel);
Usb3000B->AddChannel(Channel);
// done
nStreams = 4;
}
// create the channels in groups
CreateChannelGroup(Usb3003A, 0, Usb3003A, 1, Codec2A, 0, channels);
CreateChannelGroup(Usb3003A, 2, Usb3000B, 0, Codec2B, 0, channels);
// done
nStreams = 6;
}
else
{
// cleanup
delete Usb3003A;
delete Usb3000B;
delete Codec2A;
delete Codec2B;
}
// done

@ -73,6 +73,7 @@ public:
protected:
// factory helper
static void CreateChannelGroup(CVocodecInterface *, int, CVocodecInterface *, int, CVocodecInterface *, int, std::vector<CVocodecChannel *>*);
static int CreateUsb3012(CFtdiDeviceDescr *, std::vector<CVocodecChannel *>*);
static int CreateUsb3006(CFtdiDeviceDescr *, std::vector<CVocodecChannel *>*);
static int CreateUsb3003(CFtdiDeviceDescr *, std::vector<CVocodecChannel *>*);

@ -50,14 +50,14 @@ CStream::CStream()
m_iLostPackets = 0;
}
CStream::CStream(uint16 uiId, const CCallsign &Callsign, const CIp &Ip, uint8 uiCodecIn, uint8 uiCodecOut)
CStream::CStream(uint16 uiId, const CCallsign &Callsign, const CIp &Ip, uint8 uiCodecIn, uint8 uiCodecsOut)
{
m_uiId = uiId;
m_Callsign = Callsign;
m_Ip = Ip;
m_uiPort = 0;
m_uiCodecIn = uiCodecIn;
m_uiCodecOut = uiCodecOut;
m_uiCodecsOut = uiCodecsOut;
m_bStopThread = false;
m_pThread = NULL;
m_VocodecChannel = NULL;
@ -84,6 +84,8 @@ CStream::~CStream()
m_pThread->join();
delete m_pThread;
}
PurgeAllQueues();
}
////////////////////////////////////////////////////////////////////////////////////////
@ -101,10 +103,12 @@ bool CStream::Init(uint16 uiPort)
if ( ok )
{
// open the vocodecchannel
ok &= ((m_VocodecChannel = g_Vocodecs.OpenChannel(m_uiCodecIn, m_uiCodecOut)) != NULL);
ok &= ((m_VocodecChannel = g_Vocodecs.OpenChannel(m_uiCodecIn, m_uiCodecsOut)) != NULL);
if ( ok )
{
PurgeAllQueues();
// store port
m_uiPort = uiPort;
@ -149,6 +153,8 @@ void CStream::Close(void)
m_pThread = NULL;
}
PurgeAllQueues();
// report
std::cout << m_iLostPackets << " of " << m_iTotalPackets << " packets lost" << std::endl;
}
@ -173,7 +179,8 @@ void CStream::Task(void)
static CIp Ip;
uint8 uiPid;
uint8 Ambe[AMBE_FRAME_SIZE];
CAmbePacket *packet;
CAmbePacket *packet1;
CAmbePacket *packet2;
CPacketQueue *queue;
// anything coming in from codec client ?
@ -187,27 +194,51 @@ void CStream::Task(void)
m_iTotalPackets++;
// post packet to VocoderChannel
packet = new CAmbePacket(uiPid, m_uiCodecIn, Ambe);
packet1 = new CAmbePacket(uiPid, m_uiCodecIn, Ambe);
queue = m_VocodecChannel->GetPacketQueueIn();
queue->push(packet);
queue->push(packet1);
m_VocodecChannel->ReleasePacketQueueIn();
}
}
// anything in our queue ?
queue = m_VocodecChannel->GetPacketQueueOut();
queue = m_VocodecChannel->GetPacketQueueOut1();
while ( !queue->empty() )
{
// get the packet
packet1 = (CAmbePacket *)queue->front();
queue->pop();
// add it to the outgoing queue
m_QueuePacketOut1.push(packet1);
}
m_VocodecChannel->ReleasePacketQueueOut1();
queue = m_VocodecChannel->GetPacketQueueOut2();
while ( !queue->empty() )
{
// get the packet
packet = (CAmbePacket *)queue->front();
packet2 = (CAmbePacket *)queue->front();
queue->pop();
// add it to the outgoing queue
m_QueuePacketOut2.push(packet2);
}
m_VocodecChannel->ReleasePacketQueueOut2();
while ( !m_QueuePacketOut1.empty() && !m_QueuePacketOut2.empty() )
{
packet1 = (CAmbePacket *)m_QueuePacketOut1.front();
m_QueuePacketOut1.pop();
packet2 = (CAmbePacket *)m_QueuePacketOut2.front();
m_QueuePacketOut2.pop();
// send it to client
EncodeDvFramePacket(&Buffer, packet->GetPid(), packet->GetAmbe());
m_Socket.Send(Buffer, Ip, m_uiPort);
// TODO :
// when packet PIDs are preserverd, make sure that they match
EncodeDvFramePacket(&Buffer, packet1->GetPid(), packet1->GetCodec(), packet1->GetAmbe(), packet2->GetCodec(), packet2->GetAmbe());
m_Socket.Send(Buffer, Ip);
// and done
delete packet;
delete packet1;
delete packet2;
}
m_VocodecChannel->ReleasePacketQueueOut();
}
////////////////////////////////////////////////////////////////////////////////////////
@ -229,13 +260,24 @@ bool CStream::IsValidDvFramePacket(const CBuffer &Buffer, uint8 *pid, uint8 *amb
}
////////////////////////////////////////////////////////////////////////////////////////
// packet encodeing helpers
// packet encoding helpers
void CStream::EncodeDvFramePacket(CBuffer *Buffer, uint8 Pid, uint8 *Ambe)
void CStream::EncodeDvFramePacket(CBuffer *Buffer, uint8 Pid, uint8 Codec1, uint8 *Ambe1, uint8 Codec2, uint8 *Ambe2)
{
Buffer->clear();
Buffer->Append((uint8)GetCodecOut());
Buffer->Append((uint8)Codec1);
Buffer->Append((uint8)Codec2);
Buffer->Append((uint8)Pid);
Buffer->Append(Ambe, 9);
Buffer->Append(Ambe1, 9);
Buffer->Append(Ambe2, 9);
}
////////////////////////////////////////////////////////////////////////////////////////
// queues helpers
void CStream::PurgeAllQueues(void)
{
m_QueuePacketOut1.Purge();
m_QueuePacketOut2.Purge();
}

@ -50,7 +50,7 @@ public:
uint16 GetId(void) const { return m_uiId; }
uint16 GetPort(void) const { return m_uiPort; }
uint8 GetCodecIn(void) const { return m_uiCodecIn; }
uint8 GetCodecOut(void) const { return m_uiCodecOut; }
uint8 GetCodecsOut(void) const { return m_uiCodecsOut; }
// activity timer
bool IsActive(void) const { return m_LastActivity.DurationSinceNow() <= STREAM_ACTIVITY_TIMEOUT; }
@ -64,8 +64,10 @@ protected:
bool IsValidDvFramePacket(const CBuffer &, uint8 *, uint8 *);
// packet encodeing helpers
void EncodeDvFramePacket(CBuffer *, uint8, uint8 *);
void EncodeDvFramePacket(CBuffer *, uint8, uint8, uint8 *, uint8, uint8 *);
// queues helpers
void PurgeAllQueues(void);
protected:
// data
@ -73,7 +75,7 @@ protected:
CUdpSocket m_Socket;
uint16 m_uiPort;
uint8 m_uiCodecIn;
uint8 m_uiCodecOut;
uint8 m_uiCodecsOut;
CVocodecChannel *m_VocodecChannel;
// client details
@ -91,6 +93,9 @@ protected:
bool m_bStopThread;
std::thread *m_pThread;
// outgoing packet queues
CPacketQueue m_QueuePacketOut1;
CPacketQueue m_QueuePacketOut2;
};
////////////////////////////////////////////////////////////////////////////////////////

@ -72,43 +72,6 @@ uint8 CUsb3000Interface::GetChannelCodec(int iCh) const
return (iCh == 0) ? m_uiChCodec : CODEC_NONE;
}
////////////////////////////////////////////////////////////////////////////////////////
// manage vocodec channels
CVocodecChannel *CUsb3000Interface::GetChannelWithChannelIn(int iCh)
{
CVocodecChannel *Channel = NULL;
bool done = false;
for ( int i = 0; (i < m_Channels.size()) && !done; i++ )
{
if ( iCh == 0 )
{
if ( (m_Channels[i]->GetChannelIn() == iCh) && !(m_Channels[i]->IsInterfaceOut(this)) )
{
Channel = m_Channels[i];
done = true;
}
}
}
return Channel;
}
CVocodecChannel *CUsb3000Interface::GetChannelWithChannelOut(int iCh)
{
CVocodecChannel *Channel = NULL;
bool done = false;
for ( int i = 0; (i < m_Channels.size()) && !done; i++ )
{
if ( (m_Channels[i]->GetChannelOut() == iCh) && (m_Channels[i]->IsInterfaceOut(this)) )
{
Channel = m_Channels[i];
done = true;
}
}
return Channel;
}
////////////////////////////////////////////////////////////////////////////////////////
// decoder helper

@ -54,10 +54,6 @@ public:
int GetNbChannels(void) const { return USB3000_NB_CH; }
uint8 GetChannelCodec(int) const;
// manage vocodec channels
CVocodecChannel *GetChannelWithChannelIn(int);
CVocodecChannel *GetChannelWithChannelOut(int);
protected:
// decoder helper
bool IsValidChannelPacket(const CBuffer &, int *, CAmbePacket *);

@ -81,50 +81,6 @@ uint8 CUsb3003Interface::GetChannelCodec(int iCh) const
return uiCodec;
}
////////////////////////////////////////////////////////////////////////////////////////
// manage vocodec channels
CVocodecChannel *CUsb3003Interface::GetChannelWithChannelIn(int iCh)
{
CVocodecChannel *Channel = NULL;
bool done = false;
for ( int i = 0; (i < m_Channels.size()) && !done; i++ )
{
if ( iCh == 2 )
{
if ( (m_Channels[i]->GetChannelIn() == iCh) && !(m_Channels[i]->IsInterfaceOut(this)) )
{
Channel = m_Channels[i];
done = true;
}
}
else
{
if ( (m_Channels[i]->GetChannelIn() == iCh) && (m_Channels[i]->IsInterfaceOut(this)) )
{
Channel = m_Channels[i];
done = true;
}
}
}
return Channel;
}
CVocodecChannel *CUsb3003Interface::GetChannelWithChannelOut(int iCh)
{
CVocodecChannel *Channel = NULL;
bool done = false;
for ( int i = 0; (i < m_Channels.size()) && !done; i++ )
{
if ( (m_Channels[i]->GetChannelOut() == iCh) && (m_Channels[i]->IsInterfaceOut(this)) )
{
Channel = m_Channels[i];
done = true;
}
}
return Channel;
}
////////////////////////////////////////////////////////////////////////////////////////
// decoder helper

@ -53,10 +53,6 @@ public:
int GetNbChannels(void) const { return USB3003_NB_CH; }
uint8 GetChannelCodec(int) const;
// manage vocodec channels
CVocodecChannel *GetChannelWithChannelIn(int);
CVocodecChannel *GetChannelWithChannelOut(int);
protected:
// decoder helper
bool IsValidChannelPacket(const CBuffer &, int *, CAmbePacket *);

@ -150,11 +150,17 @@ void CUsb3xxxInterface::Task(void)
Channel = GetChannelWithChannelIn(iCh);
if ( Channel != NULL )
{
Queue = Channel->GetVoiceQueue();
CVoicePacket *clone = new CVoicePacket(VoicePacket);
clone->ApplyGain(Channel->GetSpeechGain());
Queue->push(clone);
Channel->ReleaseVoiceQueue();
CVoicePacket *clone1 = new CVoicePacket(VoicePacket);
clone1->ApplyGain(Channel->GetSpeechGain());
CVoicePacket *clone2 = new CVoicePacket(*clone1);
Queue = Channel->GetVoiceQueue1();
Queue->push(clone1);
Channel->ReleaseVoiceQueue1();
Queue = Channel->GetVoiceQueue2();
Queue->push(clone2);
Channel->ReleaseVoiceQueue2();
}
}
else if ( IsValidChannelPacket(Buffer, &iCh, &AmbePacket) )
@ -170,10 +176,20 @@ void CUsb3xxxInterface::Task(void)
Channel = GetChannelWithChannelOut(iCh);
if ( Channel != NULL )
{
Queue = Channel->GetPacketQueueOut();
CAmbePacket *clone = new CAmbePacket(AmbePacket);
Queue->push(clone);
Channel->ReleasePacketQueueOut();
if ( Channel->IsInterfaceOut1(this) )
{
Queue = Channel->GetPacketQueueOut1();
Queue->push(clone);
Channel->ReleasePacketQueueOut1();
}
else if ( Channel->IsInterfaceOut2(this) )
{
Queue = Channel->GetPacketQueueOut2();
Queue->push(clone);
Channel->ReleasePacketQueueOut2();
}
}
}
}
@ -184,15 +200,15 @@ void CUsb3xxxInterface::Task(void)
do
{
done = true;
for ( int i = 0; i < m_Channels.size(); i++)
for ( int i = 0; i < GetNbChannels(); i++ )
{
// get channel
Channel = m_Channels[i];
// get active outgoing channel for interface channel
Channel = GetChannelWithChannelOut(i);
// any packet in voice queue ?
if ( Channel->IsInterfaceOut(this) )
// any packet in voice queue 1 ?
if ( Channel != NULL && Channel->IsInterfaceOut1(this) )
{
Queue = Channel->GetVoiceQueue();
Queue = Channel->GetVoiceQueue1();
if ( !Queue->empty() )
{
// get packet
@ -201,17 +217,41 @@ void CUsb3xxxInterface::Task(void)
// this is second step of transcoding
// we just received from hardware a decoded speech packet
// post it to relevant channel encoder
int i = Channel->GetChannelOut();
int i = Channel->GetChannelOut1();
Packet->SetChannel(i);
m_SpeechQueues[i]->push(Packet);
// done
done = false;
}
Channel->ReleaseVoiceQueue();
Channel->ReleaseVoiceQueue1();
}
// any packet in voice queue 2 ?
if ( Channel != NULL && Channel->IsInterfaceOut2(this) )
{
Queue = Channel->GetVoiceQueue2();
if ( !Queue->empty() )
{
// get packet
CVoicePacket *Packet = (CVoicePacket *)Queue->front();
Queue->pop();
// this is second step of transcoding
// we just received from hardware a decoded speech packet
// post it to relevant channel encoder
int i = Channel->GetChannelOut2();
Packet->SetChannel(i);
m_SpeechQueues[i]->push(Packet);
// done
done = false;
}
Channel->ReleaseVoiceQueue2();
}
// get active incoming channel for interface channel
Channel = GetChannelWithChannelIn(i);
// any packet in ambe queue for us ?
if ( Channel->IsInterfaceIn(this) )
if ( Channel != NULL && Channel->IsInterfaceIn(this) )
{
Queue = Channel->GetPacketQueueIn();
if ( !Queue->empty() )

@ -31,13 +31,16 @@
////////////////////////////////////////////////////////////////////////////////////////
// constructor
CVocodecChannel::CVocodecChannel(CVocodecInterface *InterfaceIn, int iChIn, CVocodecInterface *InterfaceOut, int iChOut, int iSpeechGain)
CVocodecChannel::CVocodecChannel(CVocodecInterface *InterfaceIn, int iChIn, CVocodecInterface *InterfaceOut1, int iChOut1, CVocodecInterface *InterfaceOut2, int iChOut2, int iSpeechGain)
{
m_bOpen = false;
m_GroupChannels.reserve(2);
m_InterfaceIn = InterfaceIn;
m_iChannelIn = iChIn;
m_InterfaceOut = InterfaceOut;
m_iChannelOut = iChOut;
m_InterfaceOut1 = InterfaceOut1;
m_iChannelOut1 = iChOut1;
m_InterfaceOut2 = InterfaceOut2;
m_iChannelOut2 = iChOut2;
m_iSpeechGain = iSpeechGain;
}
@ -46,9 +49,41 @@ CVocodecChannel::CVocodecChannel(CVocodecInterface *InterfaceIn, int iChIn, CVoc
CVocodecChannel::~CVocodecChannel()
{
// empty array of grouped channels
// channels are deleted by their owner (CVocodecs)
m_GroupChannels.clear();
PurgeAllQueues();
}
////////////////////////////////////////////////////////////////////////////////////////
// manage group
void CVocodecChannel::AddGroupChannel(CVocodecChannel *Channel)
{
m_GroupChannels.push_back(Channel);
}
bool CVocodecChannel::IsAvailable(void) const
{
if ( m_bOpen )
{
return false;
}
bool available = true;
for ( int i = 0; (i < m_GroupChannels.size()) && available; i++ )
{
if ( m_GroupChannels[i]->m_bOpen )
{
available = false;
}
}
return available;
}
////////////////////////////////////////////////////////////////////////////////////////
// open & close
@ -60,9 +95,13 @@ bool CVocodecChannel::Open(void)
m_bOpen = true;
ok = true;
PurgeAllQueues();
m_InterfaceIn->SetChannelWithChannelIn(this, m_iChannelIn);
m_InterfaceOut1->SetChannelWithChannelOut(this, m_iChannelOut1);
m_InterfaceOut2->SetChannelWithChannelOut(this, m_iChannelOut2);
std::cout << "Vocodec channel " <<
m_InterfaceIn->GetName() << ":" << (int)m_iChannelIn << " -> " <<
m_InterfaceOut->GetName() << ":" << (int)m_iChannelOut << " open" << std::endl;
m_InterfaceOut1->GetName() << ":" << (int)m_iChannelOut1 << ", " <<
m_InterfaceOut2->GetName() << ":" << (int)m_iChannelOut2 << " open" << std::endl;
}
return ok;
}
@ -73,9 +112,13 @@ void CVocodecChannel::Close(void)
{
m_bOpen = false;
PurgeAllQueues();
m_InterfaceIn->SetChannelWithChannelIn(NULL, m_iChannelIn);
m_InterfaceOut1->SetChannelWithChannelOut(NULL, m_iChannelOut1);
m_InterfaceOut2->SetChannelWithChannelOut(NULL, m_iChannelOut2);
std::cout << "Vocodec channel " <<
m_InterfaceIn->GetName() << ":" << (int)m_iChannelIn << " -> " <<
m_InterfaceOut->GetName() << ":" << (int)m_iChannelOut << " closed" << std::endl;
m_InterfaceIn->GetName() << ":" << (int)m_iChannelIn << " -> " <<
m_InterfaceOut1->GetName() << ":" << (int)m_iChannelOut1 << ", " <<
m_InterfaceOut2->GetName() << ":" << (int)m_iChannelOut2 << " closed" << std::endl;
}
}
@ -87,9 +130,19 @@ uint8 CVocodecChannel::GetCodecIn(void) const
return m_InterfaceIn->GetChannelCodec(m_iChannelIn);
}
uint8 CVocodecChannel::GetCodecOut(void) const
uint8 CVocodecChannel::GetCodecOut1(void) const
{
return m_InterfaceOut1->GetChannelCodec(m_iChannelOut1);
}
uint8 CVocodecChannel::GetCodecOut2(void) const
{
return m_InterfaceOut2->GetChannelCodec(m_iChannelOut2);
}
uint8 CVocodecChannel::GetCodecsOut(void) const
{
return m_InterfaceOut->GetChannelCodec(m_iChannelOut);
return GetCodecOut1() | GetCodecOut2();
}
////////////////////////////////////////////////////////////////////////////////////////
@ -99,10 +152,14 @@ void CVocodecChannel::PurgeAllQueues(void)
{
GetPacketQueueIn()->Purge();
ReleasePacketQueueIn();
GetPacketQueueOut()->Purge();
ReleasePacketQueueOut();
GetVoiceQueue()->Purge();
ReleaseVoiceQueue();
GetPacketQueueOut1()->Purge();
ReleasePacketQueueOut1();
GetPacketQueueOut2()->Purge();
ReleasePacketQueueOut2();
GetVoiceQueue1()->Purge();
ReleaseVoiceQueue1();
GetVoiceQueue2()->Purge();
ReleaseVoiceQueue2();
}

@ -37,34 +37,45 @@ class CVocodecChannel
{
public:
// constructors
CVocodecChannel(CVocodecInterface *, int, CVocodecInterface *, int, int);
CVocodecChannel(CVocodecInterface *, int, CVocodecInterface *, int, CVocodecInterface *, int, int);
// destructor
virtual ~CVocodecChannel();
// manage group
void AddGroupChannel(CVocodecChannel *);
bool IsAvailable(void) const;
// open & close
bool Open(void);
bool IsOpen(void) const { return m_bOpen; }
void Close(void);
// get
uint8 GetCodecIn(void) const;
uint8 GetCodecOut(void) const;
uint8 GetCodecOut1(void) const;
uint8 GetCodecOut2(void) const;
uint8 GetCodecsOut(void) const;
int GetChannelIn(void) const { return m_iChannelIn; }
int GetChannelOut(void) const { return m_iChannelOut; }
int GetChannelOut1(void) const { return m_iChannelOut1; }
int GetChannelOut2(void) const { return m_iChannelOut2; }
int GetSpeechGain(void) const { return m_iSpeechGain; }
// interfaces
bool IsInterfaceIn(const CVocodecInterface *interface) { return (interface == m_InterfaceIn); }
bool IsInterfaceOut(const CVocodecInterface *interface) { return (interface == m_InterfaceOut); }
bool IsInterfaceOut1(const CVocodecInterface *interface) { return (interface == m_InterfaceOut1); }
bool IsInterfaceOut2(const CVocodecInterface *interface) { return (interface == m_InterfaceOut2); }
// queues
CPacketQueue *GetPacketQueueIn(void) { m_QueuePacketIn.Lock(); return &m_QueuePacketIn; }
void ReleasePacketQueueIn(void) { m_QueuePacketIn.Unlock(); }
CPacketQueue *GetPacketQueueOut(void) { m_QueuePacketOut.Lock(); return &m_QueuePacketOut; }
void ReleasePacketQueueOut(void) { m_QueuePacketOut.Unlock(); }
CPacketQueue *GetVoiceQueue(void) { m_QueueVoice.Lock(); return &m_QueueVoice; }
void ReleaseVoiceQueue(void) { m_QueueVoice.Unlock(); }
CPacketQueue *GetPacketQueueOut1(void) { m_QueuePacketOut1.Lock(); return &m_QueuePacketOut1; }
void ReleasePacketQueueOut1(void) { m_QueuePacketOut1.Unlock(); }
CPacketQueue *GetPacketQueueOut2(void) { m_QueuePacketOut2.Lock(); return &m_QueuePacketOut2; }
void ReleasePacketQueueOut2(void) { m_QueuePacketOut2.Unlock(); }
CPacketQueue *GetVoiceQueue1(void) { m_QueueVoice1.Lock(); return &m_QueueVoice1; }
void ReleaseVoiceQueue1(void) { m_QueueVoice1.Unlock(); }
CPacketQueue *GetVoiceQueue2(void) { m_QueueVoice2.Lock(); return &m_QueueVoice2; }
void ReleaseVoiceQueue2(void) { m_QueueVoice2.Unlock(); }
// operators
//virtual bool operator ==(const CVocodecChannel &) const { return false; }
@ -75,22 +86,29 @@ protected:
protected:
// status
bool m_bOpen;
bool m_bOpen;
// array of grouped channels
std::vector<CVocodecChannel *> m_GroupChannels;
// connected interfaces
CVocodecInterface *m_InterfaceIn;
int m_iChannelIn;
CVocodecInterface *m_InterfaceOut;
int m_iChannelOut;
CVocodecInterface *m_InterfaceIn;
int m_iChannelIn;
CVocodecInterface *m_InterfaceOut1;
int m_iChannelOut1;
CVocodecInterface *m_InterfaceOut2;
int m_iChannelOut2;
// ambe queues
CPacketQueue m_QueuePacketIn;
CPacketQueue m_QueuePacketOut;
// voice queue
CPacketQueue m_QueueVoice;
CPacketQueue m_QueuePacketIn;
CPacketQueue m_QueuePacketOut1;
CPacketQueue m_QueuePacketOut2;
// voice queues
CPacketQueue m_QueueVoice1;
CPacketQueue m_QueueVoice2;
// settings
int m_iSpeechGain;
int m_iSpeechGain;
};

@ -33,7 +33,6 @@
CVocodecInterface::CVocodecInterface()
{
m_Channels.reserve(5);
m_bStopThread = false;
m_pThread = NULL;
}
@ -43,10 +42,6 @@ CVocodecInterface::CVocodecInterface()
CVocodecInterface::~CVocodecInterface()
{
// empty channel array
// chennels are deleted by their owner (CVocodecs)
m_Channels.clear();
// stop thread
m_bStopThread = true;
if ( m_pThread != NULL )
@ -61,6 +56,13 @@ CVocodecInterface::~CVocodecInterface()
bool CVocodecInterface::Init(void)
{
// no open channel state
for ( int i = 0; i < GetNbChannels(); i++ )
{
m_ChannelIn.push_back(NULL);
m_ChannelOut.push_back(NULL);
}
// reset stop flag
m_bStopThread = false;
@ -85,11 +87,39 @@ void CVocodecInterface::Thread(CVocodecInterface *This)
////////////////////////////////////////////////////////////////////////////////////////
// manage Channels
// manage open Channel state
void CVocodecInterface::SetChannelWithChannelIn(CVocodecChannel *Channel, int iCh)
{
m_MutexChannels.lock();
m_ChannelIn[iCh] = Channel;
m_ChannelOut[iCh] = NULL;
m_MutexChannels.unlock();
}
void CVocodecInterface::SetChannelWithChannelOut(CVocodecChannel *Channel, int iCh)
{
m_MutexChannels.lock();
m_ChannelIn[iCh] = NULL;
m_ChannelOut[iCh] = Channel;
m_MutexChannels.unlock();
}
void CVocodecInterface::AddChannel(CVocodecChannel *Channel)
CVocodecChannel *CVocodecInterface::GetChannelWithChannelIn(int iCh)
{
m_Channels.push_back(Channel);
CVocodecChannel *Channel;
m_MutexChannels.lock();
Channel = m_ChannelIn[iCh];
m_MutexChannels.unlock();
return Channel;
}
CVocodecChannel *CVocodecInterface::GetChannelWithChannelOut(int iCh)
{
CVocodecChannel *Channel;
m_MutexChannels.lock();
Channel = m_ChannelOut[iCh];
m_MutexChannels.unlock();
return Channel;
}

@ -50,9 +50,12 @@ public:
// manage channels
virtual int GetNbChannels(void) const { return 0; }
virtual uint8 GetChannelCodec(int) const { return CODEC_NONE; }
void AddChannel(CVocodecChannel *);
virtual CVocodecChannel *GetChannelWithChannelIn(int) { return NULL; }
virtual CVocodecChannel *GetChannelWithChannelOut(int) { return NULL; }
// manage open channel state
void SetChannelWithChannelIn(CVocodecChannel *, int);
void SetChannelWithChannelOut(CVocodecChannel *, int);
CVocodecChannel *GetChannelWithChannelIn(int);
CVocodecChannel *GetChannelWithChannelOut(int);
// task
static void Thread(CVocodecInterface *);
@ -62,9 +65,11 @@ public:
virtual bool operator ==(const CVocodecInterface &) const { return false; }
protected:
// array of channels
std::vector<CVocodecChannel *> m_Channels;
// open channel state
std::mutex m_MutexChannels;
std::vector<CVocodecChannel *> m_ChannelIn;
std::vector<CVocodecChannel *> m_ChannelOut;
// thread
bool m_bStopThread;
std::thread *m_pThread;

@ -200,13 +200,14 @@ bool CVocodecs::Init(void)
// done
iNbCh /= 3;
if ( ok )
{
std::cout << "Codec interfaces initialized successfully : " << iNbCh << " channels available" << std::endl;
std::cout << "Codec interfaces initialized successfully : " << iNbCh << " channel group" << ( iNbCh > 1 ? "s" : "" ) << " available" << std::endl;
}
else
{
std::cout << "At least one codec interfaces failed to initialize : " << iNbCh << " channels availables" << std::endl;
std::cout << "At least one codec interfaces failed to initialize : " << iNbCh << " channel group" << ( iNbCh > 1 ? "s" : "" ) << " available" << std::endl;
}
// done
return ok;
@ -267,7 +268,7 @@ bool CVocodecs::DiscoverFtdiDevices(void)
////////////////////////////////////////////////////////////////////////////////////////
// manage channels
CVocodecChannel *CVocodecs::OpenChannel(uint8 uiCodecIn, uint8 uiCodecOut)
CVocodecChannel *CVocodecs::OpenChannel(uint8 uiCodecIn, uint8 uiCodecsOut)
{
CVocodecChannel *Channel = NULL;
bool done = false;
@ -276,9 +277,9 @@ CVocodecChannel *CVocodecs::OpenChannel(uint8 uiCodecIn, uint8 uiCodecOut)
m_MutexChannels.lock();
for ( int i = 0; (i < m_Channels.size()) && !done; i++ )
{
if ( !m_Channels[i]->IsOpen() &&
(m_Channels[i]->GetCodecIn() == uiCodecIn) &&
(m_Channels[i]->GetCodecOut() == uiCodecOut) )
if ( (m_Channels[i]->GetCodecIn() == uiCodecIn) &&
(m_Channels[i]->GetCodecsOut() == uiCodecsOut) &&
m_Channels[i]->IsAvailable() )
{
if ( m_Channels[i]->Open() )
{
@ -295,5 +296,7 @@ CVocodecChannel *CVocodecs::OpenChannel(uint8 uiCodecIn, uint8 uiCodecOut)
void CVocodecs::CloseChannel(CVocodecChannel *Channel)
{
m_MutexChannels.lock();
Channel->Close();
m_MutexChannels.unlock();
}

@ -75,6 +75,11 @@ void CVoicePacket::SetVoice(const uint8 *voice, int size)
void CVoicePacket::ApplyGain(int dB)
{
if ( dB == 0 )
{
return;
}
float mult = pow(10, dB/20.0);
for ( int i = 0; i < m_iSize; i += 2 )
{

@ -47,9 +47,9 @@
// version -----------------------------------------------------
#define VERSION_MAJOR 1
#define VERSION_MINOR 3
#define VERSION_REVISION 2
#define VERSION_MAJOR 2
#define VERSION_MINOR 0
#define VERSION_REVISION 0
// global ------------------------------------------------------
@ -66,10 +66,13 @@
#define CODEC_NONE 0
#define CODEC_AMBEPLUS 1
#define CODEC_AMBE2PLUS 2
#define CODEC_CODEC2 4
#define CODEC_ALL 7 // all available bits set
// Transcoding speech gains
#define CODECGAIN_AMBEPLUS -10 // in dB
#define CODECGAIN_AMBEPLUS -10 // in dB
#define CODECGAIN_AMBE2PLUS +10 // in dB
#define CODECGAIN_CODEC2 0 // in dB
// Timeouts -----------------------------------------------------
#define STREAM_ACTIVITY_TIMEOUT 3 // in seconds

@ -8,7 +8,7 @@ EXECUTABLE=ambed
all: $(SOURCES) $(EXECUTABLE)
$(EXECUTABLE): $(OBJECTS)
$(CC) $(LDFLAGS) $(OBJECTS) -lftd2xx -Wl,-rpath,/usr/local/lib -o $@
$(CC) $(LDFLAGS) $(OBJECTS) -lftd2xx -lcodec2 -Wl,-rpath,/usr/local/lib -o $@
.cpp.o:
$(CC) $(CFLAGS) $< -o $@

@ -34,14 +34,14 @@
////////////////////////////////////////////////////////////////////////////////////////
// constructor
CCodecStream::CCodecStream(uint16 uiId, uint8 uiCodecIn, uint8 uiCodecOut)
CCodecStream::CCodecStream(uint16 uiId, uint8 uiCodecIn, uint8 uiCodecsOut)
{
m_bStopThread = false;
m_pThread = NULL;
m_uiStreamId = uiId;
m_uiPid = 0;
m_uiCodecIn = uiCodecIn;
m_uiCodecOut = uiCodecOut;
m_uiCodecsOut = uiCodecsOut;
m_bConnected = false;
m_iAmbeSrcPtr = 0;
m_iAmbeDestPtr = 0;
@ -166,7 +166,10 @@ void CCodecStream::Task(void)
{
CBuffer Buffer;
CIp Ip;
uint8 Ambe[AMBE_SIZE];
uint8 Codec1;
uint8 Codec2;
uint8 Ambe1[AMBE_SIZE];
uint8 Ambe2[AMBE_SIZE];
// connected ?
if ( m_bConnected )
@ -208,7 +211,7 @@ void CCodecStream::Task(void)
if ( m_Socket.Receive(&Buffer, &Ip, 1) != -1 )
{
// crack
if ( IsValidAmbePacket(Buffer, Ambe) )
if ( IsValidAmbePacket(Buffer, &Codec1, Ambe1, &Codec2, Ambe2) )
{
m_TimeoutTimer.Now();
@ -246,13 +249,16 @@ void CCodecStream::Task(void)
////////////////////////////////////////////////////////////////////////////////////////
/// packet decoding helpers
bool CCodecStream::IsValidAmbePacket(const CBuffer &Buffer, uint8 *Ambe)
bool CCodecStream::IsValidAmbePacket(const CBuffer &Buffer, uint8 *Codec1, uint8 *Ambe1, uint8 *Codec2, uint8 *Ambe2)
{
bool valid = false;
if ( (Buffer.size() == 11) && (Buffer.data()[0] == m_uiCodecOut) )
if ( (Buffer.size() == 21) && ((Buffer.data()[0] | Buffer.data()[1]) == m_uiCodecsOut) )
{
::memcpy(Ambe, &(Buffer.data()[2]), 9);
*Codec1 = Buffer.data()[0];
::memcpy(Ambe1, &(Buffer.data()[3]), 9);
*Codec2 = Buffer.data()[1];
::memcpy(Ambe2, &(Buffer.data()[12]), 9);
valid = true;
}
return valid;
@ -299,7 +305,7 @@ void CCodecStream::DisplayStats(void)
// displays
char sz[256];
sprintf(sz, "Stream %d (%d->%d) : %u / %u / %u : %.1f fps",
m_uiStreamId, m_uiCodecIn, m_uiCodecOut,
m_uiStreamId, m_uiCodecIn, m_uiCodecsOut,
uiSent, uiReceived, uiBad, fps);
std::cout << sz << std::endl;
}

@ -66,7 +66,7 @@ public:
protected:
// packet decoding helpers
bool IsValidAmbePacket(const CBuffer &, uint8 *);
bool IsValidAmbePacket(const CBuffer &, uint8 *, uint8 *, uint8 *, uint8 *);
// packet encoding helpers
void EncodeAmbePacket(CBuffer *, const uint8 *);
@ -87,7 +87,7 @@ protected:
uint16 m_uiPort;
uint8 m_uiPid;
uint8 m_uiCodecIn;
uint8 m_uiCodecOut;
uint8 m_uiCodecsOut;
// socket
CIp m_Ip;

@ -221,7 +221,7 @@ CCodecStream *CTranscoder::GetStream(uint8 uiCodecIn)
if ( m_bConnected )
{
// yes, post openstream request
EncodeOpenstreamPacket(&Buffer, uiCodecIn, (uiCodecIn == CODEC_AMBEPLUS) ? CODEC_AMBE2PLUS : CODEC_AMBEPLUS);
EncodeOpenstreamPacket(&Buffer, uiCodecIn, CODEC_ALL ^ uiCodecIn);
m_Socket.Send(Buffer, m_AmbedIp, TRANSCODER_PORT);
// wait relpy here
@ -232,7 +232,7 @@ CCodecStream *CTranscoder::GetStream(uint8 uiCodecIn)
std::cout << "ambed openstream(" << m_StreamidOpenStream << ") ok" << std::endl;
// create stream object
stream = new CCodecStream(m_StreamidOpenStream, uiCodecIn, (uiCodecIn == CODEC_AMBEPLUS) ? CODEC_AMBE2PLUS : CODEC_AMBEPLUS);
stream = new CCodecStream(m_StreamidOpenStream, uiCodecIn, CODEC_ALL ^ uiCodecIn);
// init it
if ( stream->Init(m_PortOpenStream) )
@ -368,14 +368,14 @@ void CTranscoder::EncodeKeepAlivePacket(CBuffer *Buffer)
Buffer->Append((uint8 *)(const char *)"XLX000 ", 8);
}
void CTranscoder::EncodeOpenstreamPacket(CBuffer *Buffer, uint8 uiCodecIn, uint8 uiCodecOut)
void CTranscoder::EncodeOpenstreamPacket(CBuffer *Buffer, uint8 uiCodecIn, uint8 uiCodecsOut)
{
uint8 tag[] = { 'A','M','B','E','D','O','S' };
Buffer->Set(tag, sizeof(tag));
Buffer->Append((uint8 *)(const char *)"XLX000 ", 8);
Buffer->Append((uint8)uiCodecIn);
Buffer->Append((uint8)uiCodecOut);
Buffer->Append((uint8)uiCodecsOut);
}
void CTranscoder::EncodeClosestreamPacket(CBuffer *Buffer, uint16 uiStreamId)

@ -32,10 +32,10 @@ int main(int argc, const char * argv[])
std::vector<CCodecStream *> Streams;
// check args
if ( argc != 5 )
if ( argc != 6 )
{
std::cout << "Usage: ambedtest myip ambedip nbdmrstreams nbdstarstreams" << std::endl;
std::cout << "example: ambed 192.168.178.212 127.0.0.1 2 2" << std::endl;
std::cout << "Usage: ambedtest myip ambedip nbdmrstreams nbdstarstreams nbcodec2streams" << std::endl;
std::cout << "example: ambed 192.168.178.212 127.0.0.1 2 2 2" << std::endl;
return 1;
}
@ -49,6 +49,7 @@ int main(int argc, const char * argv[])
// create streams
int nDmr = atoi(argv[3]);
int nDstar = atoi(argv[4]);
int nCodec2 = atoi(argv[5]);
for ( int i = 0; i < nDmr; i++ )
{
@ -61,6 +62,12 @@ int main(int argc, const char * argv[])
CTimePoint::TaskSleepFor(300);
Streams.push_back(g_Transcoder.GetStream(CODEC_AMBEPLUS));
}
for ( int i = 0; i < nCodec2; i++ )
{
CTimePoint::TaskSleepFor(300);
Streams.push_back(g_Transcoder.GetStream(CODEC_CODEC2));
}
// and loop wait
std::cin.get();

@ -47,7 +47,7 @@
// version -----------------------------------------------------
#define VERSION_MAJOR 1
#define VERSION_MAJOR 2
#define VERSION_MINOR 0
#define VERSION_REVISION 0
@ -66,6 +66,8 @@
#define CODEC_NONE 0
#define CODEC_AMBEPLUS 1 // DStar
#define CODEC_AMBE2PLUS 2 // DMR
#define CODEC_CODEC2 4 // Codec 2
#define CODEC_ALL 7 // all available bits set

@ -51,7 +51,6 @@ public:
int GetProtocol(void) const { return PROTOCOL_XLX; }
int GetProtocolRevision(void) const { return XLX_PROTOCOL_REVISION_2; }
const char *GetProtocolName(void) const { return "XLX"; }
int GetCodec(void) const { return CODEC_AMBE2PLUS; }
bool IsPeer(void) const { return true; }
// status

@ -65,7 +65,6 @@ public:
// identity
virtual int GetProtocol(void) const { return PROTOCOL_NONE; }
virtual int GetProtocolRevision(void) const { return 0; }
virtual int GetCodec(void) const { return CODEC_NONE; }
virtual const char *GetProtocolName(void) const { return "none"; }
virtual bool IsNode(void) const { return false; }
virtual bool IsPeer(void) const { return false; }

@ -36,14 +36,14 @@
////////////////////////////////////////////////////////////////////////////////////////
// constructor
CCodecStream::CCodecStream(CPacketStream *PacketStream, uint16 uiId, uint8 uiCodecIn, uint8 uiCodecOut)
CCodecStream::CCodecStream(CPacketStream *PacketStream, uint16 uiId, uint8 uiCodecIn, uint8 uiCodecsOut)
{
m_bStopThread = false;
m_pThread = NULL;
m_uiStreamId = uiId;
m_uiPid = 0;
m_uiCodecIn = uiCodecIn;
m_uiCodecOut = uiCodecOut;
m_uiCodecsOut = uiCodecsOut;
m_bConnected = false;
m_fPingMin = -1;
m_fPingMax = -1;
@ -155,14 +155,17 @@ void CCodecStream::Task(void)
{
CBuffer Buffer;
CIp Ip;
uint8 Ambe[AMBE_SIZE];
uint8 Codec1;
uint8 Codec2;
uint8 Ambe1[AMBE_SIZE];
uint8 Ambe2[AMBE_SIZE];
uint8 DStarSync[] = { 0x55,0x2D,0x16 };
// any packet from transcoder
if ( m_Socket.Receive(&Buffer, &Ip, 5) != -1 )
{
// crack
if ( IsValidAmbePacket(Buffer, Ambe) )
if ( IsValidAmbePacket(Buffer, &Codec1, Ambe1, &Codec2, Ambe2) )
{
// tickle
m_TimeoutTimer.Now();
@ -191,9 +194,10 @@ void CCodecStream::Task(void)
m_LocalQueue.pop();
// todo: check the PID
// update content with transcoded ambe
Packet->SetAmbe(m_uiCodecOut, Ambe);
Packet->SetAmbe(Codec1, Ambe1);
Packet->SetAmbe(Codec2, Ambe2);
// tag syncs in DvData
if ( (m_uiCodecOut == CODEC_AMBEPLUS) && (Packet->GetPacketId() % 21) == 0 )
if ( ((Codec1 == CODEC_AMBEPLUS) || (Codec2 == CODEC_AMBEPLUS)) && (Packet->GetPacketId() % 21) == 0 )
{
Packet->SetDvData(DStarSync);
}
@ -240,13 +244,16 @@ void CCodecStream::Task(void)
////////////////////////////////////////////////////////////////////////////////////////
/// packet decoding helpers
bool CCodecStream::IsValidAmbePacket(const CBuffer &Buffer, uint8 *Ambe)
bool CCodecStream::IsValidAmbePacket(const CBuffer &Buffer, uint8 *Codec1, uint8 *Ambe1, uint8 *Codec2, uint8 *Ambe2)
{
bool valid = false;
if ( (Buffer.size() == 11) && (Buffer.data()[0] == m_uiCodecOut) )
if ( (Buffer.size() == 21) && ((Buffer.data()[0] | Buffer.data()[1]) == m_uiCodecsOut) )
{
::memcpy(Ambe, &(Buffer.data()[2]), 9);
*Codec1 = Buffer.data()[0];
::memcpy(Ambe1, &(Buffer.data()[3]), 9);
*Codec2 = Buffer.data()[1];
::memcpy(Ambe2, &(Buffer.data()[12]), 9);
valid = true;
}
return valid;

@ -73,7 +73,7 @@ public:
protected:
// packet decoding helpers
bool IsValidAmbePacket(const CBuffer &, uint8 *);
bool IsValidAmbePacket(const CBuffer &, uint8 *, uint8 *, uint8 *, uint8 *);
// packet encoding helpers
void EncodeAmbePacket(CBuffer *, const uint8 *);
@ -85,7 +85,7 @@ protected:
uint16 m_uiPort;
uint8 m_uiPid;
uint8 m_uiCodecIn;
uint8 m_uiCodecOut;
uint8 m_uiCodecsOut;
// socket
CIp m_Ip;

@ -48,7 +48,6 @@ public:
// identity
int GetProtocol(void) const { return PROTOCOL_DCS; }
const char *GetProtocolName(void) const { return "DCS"; }
int GetCodec(void) const { return CODEC_AMBEPLUS; }
bool IsNode(void) const { return true; }
// status

@ -220,7 +220,7 @@ bool CDcsProtocol::OnDvHeaderPacketIn(CDvHeaderPacket *Header, const CIp &Ip)
// get client callsign
via = client->GetCallsign();
// and try to open the stream
if ( (stream = g_Reflector.OpenStream(Header, client)) != NULL )
if ( (stream = g_Reflector.OpenStream(Header, client, Header->GetCodec())) != NULL )
{
// keep the handle
m_Streams.push_back(stream);
@ -537,7 +537,7 @@ void CDcsProtocol::EncodeDvPacket(const CDvHeaderPacket &Header, const CDvFrameP
uint8 tag[] = { '0','0','0','1' };
struct dstar_header DstarHeader;
Header.ConvertToDstarStruct(&DstarHeader);
Header.ConvertToDstarStruct(&DstarHeader, CODEC_AMBEPLUS);
Buffer->Set(tag, sizeof(tag));
Buffer->Append((uint8 *)&DstarHeader, sizeof(struct dstar_header) - sizeof(uint16));

@ -49,7 +49,6 @@ public:
int GetProtocol(void) const { return PROTOCOL_DEXTRA; }
int GetProtocolRevision(void) const { return m_ProtRev; }
const char *GetProtocolName(void) const { return "DExtra"; }
int GetCodec(void) const { return CODEC_AMBEPLUS; }
bool IsNode(void) const { return true; }
// status

@ -0,0 +1,42 @@
//
// cdextraopenclient.cpp
// xlxd
//
// Created by Antony Chazapis (SV9OAN) on 19/12/2018.
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
//
// ----------------------------------------------------------------------------
// This file is part of xlxd.
//
// xlxd 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, either version 3 of the License, or
// (at your option) any later version.
//
// xlxd 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 for more details.
//
// You should have received a copy of the GNU General Public License
// along with Foobar. If not, see <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include "main.h"
#include "cdextraopenclient.h"
////////////////////////////////////////////////////////////////////////////////////////
// constructors
CDextraOpenClient::CDextraOpenClient()
: CDextraClient()
{}
CDextraOpenClient::CDextraOpenClient(const CCallsign &callsign, const CIp &ip, char reflectorModule, int protRev)
: CDextraClient(callsign, ip, reflectorModule, protRev)
{}
CDextraOpenClient::CDextraOpenClient(const CDextraClient &client)
: CDextraClient(client)
{}

@ -0,0 +1,54 @@
//
// cdextraopenclient.h
// xlxd
//
// Created by Antony Chazapis (SV9OAN) on 19/12/2018.
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
//
// ----------------------------------------------------------------------------
// This file is part of xlxd.
//
// xlxd 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, either version 3 of the License, or
// (at your option) any later version.
//
// xlxd 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 for more details.
//
// You should have received a copy of the GNU General Public License
// along with Foobar. If not, see <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#ifndef cdextraopenclient_h
#define cdextraopenclient_h
#include "cdextraclient.h"
////////////////////////////////////////////////////////////////////////////////////////
// define
////////////////////////////////////////////////////////////////////////////////////////
// class
class CDextraOpenClient : public CDextraClient
{
public:
// constructors
CDextraOpenClient();
CDextraOpenClient(const CCallsign &, const CIp &, char = ' ', int = 0);
CDextraOpenClient(const CDextraClient &);
// destructor
virtual ~CDextraOpenClient() {};
// identity
int GetProtocol(void) const { return PROTOCOL_DEXTRA_OPEN; }
const char *GetProtocolName(void) const { return "DExtra Open"; }
};
////////////////////////////////////////////////////////////////////////////////////////
#endif /* cdextraopenclient_h */

@ -0,0 +1,98 @@
//
// cdextraopenprotocol.cpp
// xlxd
//
// Created by Antony Chazapis (SV9OAN) on 19/12/2018.
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
//
// ----------------------------------------------------------------------------
// This file is part of xlxd.
//
// xlxd 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, either version 3 of the License, or
// (at your option) any later version.
//
// xlxd 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 for more details.
//
// You should have received a copy of the GNU General Public License
// along with Foobar. If not, see <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include "main.h"
#include <string.h>
#include "cdextraopenprotocol.h"
#include "cdextraopenclient.h"
#include "creflector.h"
////////////////////////////////////////////////////////////////////////////////////////
// operation
bool CDextraOpenProtocol::Init(void)
{
bool ok;
// base class
ok = CProtocol::Init();
// update the reflector callsign
m_ReflectorCallsign.PatchCallsign(0, (const uint8 *)"ORF", 3);
// create our socket
ok &= m_Socket.Open(DEXTRA_OPEN_PORT);
if ( !ok )
{
std::cout << "Error opening socket on port UDP" << DEXTRA_OPEN_PORT << " on ip " << g_Reflector.GetListenIp() << std::endl;
}
// update time
m_LastKeepaliveTime.Now();
// done
return ok;
}
////////////////////////////////////////////////////////////////////////////////////////
// create client
CClient *CDextraOpenProtocol::CreateClient(const CCallsign &callsign, const CIp &ip, char reflectormodule, int revision) const
{
CClient *client = new CDextraOpenClient(callsign, ip, reflectormodule, revision);
return client;
}
////////////////////////////////////////////////////////////////////////////////////////
// packet encoding helpers
bool CDextraOpenProtocol::EncodeDvHeaderPacket(const CDvHeaderPacket &Packet, CBuffer *Buffer) const
{
uint8 tag[] = { 'D','S','V','T',0x10,0x00,0x00,0x00,0x20,0x00,0x01,0x02 };
struct dstar_header DstarHeader;
Packet.ConvertToDstarStruct(&DstarHeader, CODEC_CODEC2);
Buffer->Set(tag, sizeof(tag));
Buffer->Append(Packet.GetStreamId());
Buffer->Append((uint8)0x80);
Buffer->Append((uint8 *)&DstarHeader, sizeof(struct dstar_header));
return true;
}
bool CDextraOpenProtocol::EncodeDvFramePacket(const CDvFramePacket &Packet, CBuffer *Buffer) const
{
uint8 tag[] = { 'D','S','V','T',0x20,0x00,0x00,0x00,0x20,0x00,0x01,0x02 };
Buffer->Set(tag, sizeof(tag));
Buffer->Append(Packet.GetStreamId());
Buffer->Append((uint8)(Packet.GetPacketId() % 21));
Buffer->Append((uint8 *)Packet.GetCodec2(), AMBE_SIZE);
Buffer->Append((uint8 *)Packet.GetDvData(), DVDATA_SIZE);
return true;
}

@ -0,0 +1,82 @@
//
// cdextraopenprotocol.h
// xlxd
//
// Created by Antony Chazapis (SV9OAN) on 19/12/2018.
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
//
// ----------------------------------------------------------------------------
// This file is part of xlxd.
//
// xlxd 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, either version 3 of the License, or
// (at your option) any later version.
//
// xlxd 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 for more details.
//
// You should have received a copy of the GNU General Public License
// along with Foobar. If not, see <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#ifndef cdextraopenprotocol_h
#define cdextraopenprotocol_h
#include "cdextraprotocol.h"
#include "cdvheaderpacket.h"
#include "cdvframepacket.h"
#include "cclient.h"
////////////////////////////////////////////////////////////////////////////////////////
// With the D-STAR vocoder extension by SV9OAN, voice in D-STAR streams can be encoded
// with either AMBE or Codec 2. Although the extension is backwards compatible and
// implementations can use the same reflector connections as AMBE-only clients,
// it should be avoided, to save the user confusion and establish interoperability.
// By implementing another DExtra listener on a different port (30201 instead of 30001),
// xlxd can be used to bridge between the two formats. The new port is to be used by
// reflectors using the "ORF" prefix (Open ReFlector).
// Any client connected to an ORF reflector will receive streams encoded with Codec 2.
// All other D-STAR protocol handlers will still send out data encoded with AMBE.
// Note that the protocol/port only affects data transmitted by the reflector.
// The stream vocoder is recognized by all protocol handlers, so a client can still
// transmit data using any vocoder on any port. The rationale behind this is that
// DExtra links may be used by repeaters or other reflectors, so it is not really
// possible to know what their clients support. So, nothing will change when linking
// a repeater to an XRF reflector, but will do when linking to an ORF one.
////////////////////////////////////////////////////////////////////////////////////////
// class
class CDextraOpenProtocol : public CDextraProtocol
{
public:
// constructor
CDextraOpenProtocol() {};
// destructor
virtual ~CDextraOpenProtocol() {};
// initialization
bool Init(void);
// identity
int GetProtocol(void) const { return PROTOCOL_DEXTRA_OPEN; }
protected:
// create client
CClient *CreateClient(const CCallsign &, const CIp &, char, int) const;
// packet encoding helpers
bool EncodeDvHeaderPacket(const CDvHeaderPacket &, CBuffer *) const;
bool EncodeDvFramePacket(const CDvFramePacket &, CBuffer *) const;
};
////////////////////////////////////////////////////////////////////////////////////////
#endif /* cdextraopenprotocol_h */

@ -24,8 +24,8 @@
#include "main.h"
#include <string.h>
#include "cdextraclient.h"
#include "cdextraprotocol.h"
#include "cdextraclient.h"
#include "creflector.h"
#include "cgatekeeper.h"
@ -88,7 +88,7 @@ void CDextraProtocol::Task(void)
//std::cout << "DExtra DV header:" << std::endl;
// callsign muted?
if ( g_GateKeeper.MayTransmit(Header->GetMyCallsign(), Ip, PROTOCOL_DEXTRA, Header->GetRpt2Module()) )
if ( g_GateKeeper.MayTransmit(Header->GetMyCallsign(), Ip, GetProtocol(), Header->GetRpt2Module()) )
{
// handle it
OnDvHeaderPacketIn(Header, Ip);
@ -110,7 +110,7 @@ void CDextraProtocol::Task(void)
std::cout << "DExtra connect packet for module " << ToLinkModule << " from " << Callsign << " at " << Ip << " rev " << ProtRev << std::endl;
// callsign authorized?
if ( g_GateKeeper.MayLink(Callsign, Ip, PROTOCOL_DEXTRA) )
if ( g_GateKeeper.MayLink(Callsign, Ip, GetProtocol()) )
{
// valid module ?
if ( g_Reflector.IsValidModule(ToLinkModule) )
@ -120,7 +120,7 @@ void CDextraProtocol::Task(void)
m_Socket.Send(Buffer, Ip);
// create the client
CDextraClient *client = new CDextraClient(Callsign, Ip, ToLinkModule, ProtRev);
CClient *client = CreateClient(Callsign, Ip, ToLinkModule, ProtRev);
// and append
g_Reflector.GetClients()->AddClient(client);
@ -148,7 +148,7 @@ void CDextraProtocol::Task(void)
// find client & remove it
CClients *clients = g_Reflector.GetClients();
CClient *client = clients->FindClient(Ip, PROTOCOL_DEXTRA);
CClient *client = clients->FindClient(Ip, GetProtocol());
if ( client != NULL )
{
// ack disconnect packet
@ -174,7 +174,7 @@ void CDextraProtocol::Task(void)
CClients *clients = g_Reflector.GetClients();
int index = -1;
CClient *client = NULL;
while ( (client = clients->FindNextClient(Callsign, Ip, PROTOCOL_DEXTRA, &index)) != NULL )
while ( (client = clients->FindNextClient(Callsign, Ip, GetProtocol(), &index)) != NULL )
{
client->Alive();
}
@ -204,6 +204,15 @@ void CDextraProtocol::Task(void)
}
}
////////////////////////////////////////////////////////////////////////////////////////
// create client
CClient *CDextraProtocol::CreateClient(const CCallsign &callsign, const CIp &ip, char reflectormodule, int revision) const
{
CClient *client = new CDextraClient(callsign, ip, reflectormodule, revision);
return client;
}
////////////////////////////////////////////////////////////////////////////////////////
// queue helper
@ -224,7 +233,7 @@ void CDextraProtocol::HandleQueue(void)
CClients *clients = g_Reflector.GetClients();
int index = -1;
CClient *client = NULL;
while ( (client = clients->FindNextClient(PROTOCOL_DEXTRA, &index)) != NULL )
while ( (client = clients->FindNextClient(GetProtocol(), &index)) != NULL )
{
// is this client busy ?
if ( !client->IsAMaster() && (client->GetReflectorModule() == packet->GetModuleId()) )
@ -262,7 +271,7 @@ void CDextraProtocol::HandleKeepalives(void)
CClients *clients = g_Reflector.GetClients();
int index = -1;
CClient *client = NULL;
while ( (client = clients->FindNextClient(PROTOCOL_DEXTRA, &index)) != NULL )
while ( (client = clients->FindNextClient(GetProtocol(), &index)) != NULL )
{
// send keepalive
m_Socket.Send(keepalive, client->GetIp());
@ -305,7 +314,7 @@ bool CDextraProtocol::OnDvHeaderPacketIn(CDvHeaderPacket *Header, const CIp &Ip)
CCallsign via(Header->GetRpt1Callsign());
// find this client
CClient *client = g_Reflector.GetClients()->FindClient(Ip, PROTOCOL_DEXTRA);
CClient *client = g_Reflector.GetClients()->FindClient(Ip, GetProtocol());
if ( client != NULL )
{
// get client callsign
@ -318,7 +327,7 @@ bool CDextraProtocol::OnDvHeaderPacketIn(CDvHeaderPacket *Header, const CIp &Ip)
Header->SetRpt2Module(client->GetReflectorModule());
}
// and try to open the stream
if ( (stream = g_Reflector.OpenStream(Header, client)) != NULL )
if ( (stream = g_Reflector.OpenStream(Header, client, Header->GetCodec())) != NULL )
{
// keep the handle
m_Streams.push_back(stream);
@ -521,7 +530,7 @@ bool CDextraProtocol::EncodeDvHeaderPacket(const CDvHeaderPacket &Packet, CBuffe
uint8 tag[] = { 'D','S','V','T',0x10,0x00,0x00,0x00,0x20,0x00,0x01,0x02 };
struct dstar_header DstarHeader;
Packet.ConvertToDstarStruct(&DstarHeader);
Packet.ConvertToDstarStruct(&DstarHeader, CODEC_AMBEPLUS);
Buffer->Set(tag, sizeof(tag));
Buffer->Append(Packet.GetStreamId());

@ -30,6 +30,7 @@
#include "cdvheaderpacket.h"
#include "cdvframepacket.h"
#include "cdvlastframepacket.h"
#include "cclient.h"
////////////////////////////////////////////////////////////////////////////////////////
@ -70,7 +71,13 @@ public:
// task
void Task(void);
// identity
int GetProtocol(void) const { return PROTOCOL_DEXTRA; }
protected:
// create client
CClient *CreateClient(const CCallsign &, const CIp &, char, int) const;
// queue helper
void HandleQueue(void);

@ -48,7 +48,6 @@ public:
// identity
int GetProtocol(void) const { return PROTOCOL_DMRMMDVM; }
const char *GetProtocolName(void) const { return "DMRMmdvm"; }
int GetCodec(void) const { return CODEC_AMBE2PLUS; }
bool IsNode(void) const { return true; }
// status

@ -328,7 +328,7 @@ bool CDmrmmdvmProtocol::OnDvHeaderPacketIn(CDvHeaderPacket *Header, const CIp &I
if ( g_Reflector.IsValidModule(Header->GetRpt2Module()) && (CallType == DMR_GROUP_CALL) )
{
// yes, try to open the stream
if ( (stream = g_Reflector.OpenStream(Header, client)) != NULL )
if ( (stream = g_Reflector.OpenStream(Header, client, CODEC_AMBE2PLUS)) != NULL )
{
// keep the handle
m_Streams.push_back(stream);

@ -48,7 +48,6 @@ public:
// identity
int GetProtocol(void) const { return PROTOCOL_DMRPLUS; }
const char *GetProtocolName(void) const { return "DMRplus"; }
int GetCodec(void) const { return CODEC_AMBE2PLUS; }
bool IsNode(void) const { return true; }
// status

@ -219,7 +219,7 @@ bool CDmrplusProtocol::OnDvHeaderPacketIn(CDvHeaderPacket *Header, const CIp &Ip
if ( client != NULL )
{
// and try to open the stream
if ( (stream = g_Reflector.OpenStream(Header, client)) != NULL )
if ( (stream = g_Reflector.OpenStream(Header, client, CODEC_AMBE2PLUS)) != NULL )
{
// keep the handle
m_Streams.push_back(stream);

@ -48,7 +48,6 @@ public:
// identity
int GetProtocol(void) const { return PROTOCOL_DPLUS; }
const char *GetProtocolName(void) const { return "DPlus"; }
int GetCodec(void) const { return CODEC_AMBEPLUS; }
bool IsNode(void) const { return true; }
bool IsDextraDongle(void) const { return m_bDextraDongle; }
void SetDextraDongle(void) { m_bDextraDongle = true; }

@ -225,7 +225,7 @@ bool CDplusProtocol::OnDvHeaderPacketIn(CDvHeaderPacket *Header, const CIp &Ip)
// get client callsign
via = client->GetCallsign();
// and try to open the stream
if ( (stream = g_Reflector.OpenStream(Header, client)) != NULL )
if ( (stream = g_Reflector.OpenStream(Header, client, Header->GetCodec())) != NULL )
{
// keep the handle
m_Streams.push_back(stream);
@ -552,7 +552,7 @@ bool CDplusProtocol::EncodeDvHeaderPacket(const CDvHeaderPacket &Packet, CBuffer
uint8 tag[] = { 0x3A,0x80,0x44,0x53,0x56,0x54,0x10,0x00,0x00,0x00,0x20,0x00,0x01,0x02 };
struct dstar_header DstarHeader;
Packet.ConvertToDstarStruct(&DstarHeader);
Packet.ConvertToDstarStruct(&DstarHeader, CODEC_AMBEPLUS);
Buffer->Set(tag, sizeof(tag));
Buffer->Append(Packet.GetStreamId());

@ -34,6 +34,7 @@
CDvFramePacket::CDvFramePacket()
{
::memset(m_uiAmbe, 0, sizeof(m_uiAmbe));
::memset(m_uiCodec2, 0, sizeof(m_uiCodec2));
::memset(m_uiDvData, 0, sizeof(m_uiDvData));
::memset(m_uiAmbePlus, 0, sizeof(m_uiAmbePlus));
::memset(m_uiDvSync, 0, sizeof(m_uiDvSync));
@ -45,6 +46,7 @@ CDvFramePacket::CDvFramePacket(const struct dstar_dvframe *dvframe, uint16 sid,
: CPacket(sid, pid)
{
::memcpy(m_uiAmbe, dvframe->AMBE, sizeof(m_uiAmbe));
::memset(m_uiCodec2, 0, sizeof(m_uiCodec2));
::memcpy(m_uiDvData, dvframe->DVDATA, sizeof(m_uiDvData));
::memset(m_uiAmbePlus, 0, sizeof(m_uiAmbePlus));
::memset(m_uiDvSync, 0, sizeof(m_uiDvSync));
@ -58,18 +60,20 @@ CDvFramePacket::CDvFramePacket(const uint8 *ambe, const uint8 *sync, uint16 sid,
::memcpy(m_uiAmbePlus, ambe, sizeof(m_uiAmbePlus));
::memcpy(m_uiDvSync, sync, sizeof(m_uiDvSync));
::memset(m_uiAmbe, 0, sizeof(m_uiAmbe));
::memset(m_uiCodec2, 0, sizeof(m_uiCodec2));
::memset(m_uiDvData, 0, sizeof(m_uiDvData));
}
// dstar + dmr constructor
// dstar + codec 2 + dmr constructor
CDvFramePacket::CDvFramePacket
(uint16 sid,
uint8 dstarpid, const uint8 *dstarambe, const uint8 *dstardvdata,
uint8 dstarpid, const uint8 *dstarambe, const uint8 *dstarcodec2, const uint8 *dstardvdata,
uint8 dmrpid, uint8 dprspid, const uint8 *dmrambe, const uint8 *dmrsync)
: CPacket(sid, dstarpid, dmrpid, dprspid)
{
::memcpy(m_uiAmbe, dstarambe, sizeof(m_uiAmbe));
::memcpy(m_uiCodec2, dstarcodec2, sizeof(m_uiCodec2));
::memcpy(m_uiDvData, dstardvdata, sizeof(m_uiDvData));
::memcpy(m_uiAmbePlus, dmrambe, sizeof(m_uiAmbePlus));
::memcpy(m_uiDvSync, dmrsync, sizeof(m_uiDvSync));
@ -81,6 +85,7 @@ CDvFramePacket::CDvFramePacket(const CDvFramePacket &DvFrame)
: CPacket(DvFrame)
{
::memcpy(m_uiAmbe, DvFrame.m_uiAmbe, sizeof(m_uiAmbe));
::memcpy(m_uiCodec2, DvFrame.m_uiCodec2, sizeof(m_uiCodec2));
::memcpy(m_uiDvData, DvFrame.m_uiDvData, sizeof(m_uiDvData));
::memcpy(m_uiAmbePlus, DvFrame.m_uiAmbePlus, sizeof(m_uiAmbePlus));
::memcpy(m_uiDvSync, DvFrame.m_uiDvSync, sizeof(m_uiDvSync));
@ -103,6 +108,7 @@ const uint8 *CDvFramePacket::GetAmbe(uint8 uiCodec) const
{
case CODEC_AMBEPLUS: return m_uiAmbe;
case CODEC_AMBE2PLUS: return m_uiAmbePlus;
case CODEC_CODEC2: return m_uiCodec2;
default: return NULL;
}
}
@ -123,11 +129,29 @@ void CDvFramePacket::SetAmbe(uint8 uiCodec, uint8 *Ambe)
::memcpy(m_uiAmbe, Ambe, sizeof(m_uiAmbe));
break;
case CODEC_AMBE2PLUS:
::memcpy(m_uiAmbePlus, Ambe, sizeof(m_uiAmbe));
::memcpy(m_uiAmbePlus, Ambe, sizeof(m_uiAmbePlus));
break;
case CODEC_CODEC2:
::memcpy(m_uiCodec2, Ambe, sizeof(m_uiCodec2));
break;
}
}
void CDvFramePacket::ClearAmbe(uint8 uiCodec)
{
switch (uiCodec)
{
case CODEC_AMBEPLUS:
::memset(m_uiAmbe, 0, sizeof(m_uiAmbe));
break;
case CODEC_AMBE2PLUS:
::memset(m_uiAmbePlus, 0, sizeof(m_uiAmbePlus));
break;
case CODEC_CODEC2:
::memset(m_uiCodec2, 0, sizeof(m_uiCodec2));
break;
}
}
////////////////////////////////////////////////////////////////////////////////////////
// operators
@ -135,6 +159,7 @@ void CDvFramePacket::SetAmbe(uint8 uiCodec, uint8 *Ambe)
bool CDvFramePacket::operator ==(const CDvFramePacket &DvFrame) const
{
return ( (::memcmp(m_uiAmbe, DvFrame.m_uiAmbe, sizeof(m_uiAmbe)) == 0) &&
(::memcmp(m_uiCodec2, DvFrame.m_uiCodec2, sizeof(m_uiCodec2)) == 0) &&
(::memcmp(m_uiDvData, DvFrame.m_uiDvData, sizeof(m_uiDvData)) == 0) &&
(::memcmp(m_uiAmbePlus, DvFrame.m_uiAmbePlus, sizeof(m_uiAmbePlus)) == 0) &&
(::memcmp(m_uiDvSync, DvFrame.m_uiDvSync, sizeof(m_uiDvSync)) == 0) );

@ -55,7 +55,7 @@ public:
CDvFramePacket();
CDvFramePacket(const struct dstar_dvframe *, uint16, uint8);
CDvFramePacket(const uint8 *, const uint8 *, uint16, uint8, uint8);
CDvFramePacket(uint16, uint8, const uint8 *, const uint8 *, uint8, uint8, const uint8 *, const uint8 *);
CDvFramePacket(uint16, uint8, const uint8 *, const uint8 *, const uint8 *, uint8, uint8, const uint8 *, const uint8 *);
CDvFramePacket(const CDvFramePacket &);
// destructor
@ -72,12 +72,14 @@ public:
const uint8 *GetAmbe(uint8) const;
const uint8 *GetAmbe(void) const { return m_uiAmbe; }
const uint8 *GetAmbePlus(void) const { return m_uiAmbePlus; }
const uint8 *GetCodec2(void) const { return m_uiCodec2; }
const uint8 *GetDvData(void) const { return m_uiDvData; }
const uint8 *GetDvSync(void) const { return m_uiDvSync; }
// set
void SetDvData(uint8 *);
void SetAmbe(uint8, uint8 *);
void ClearAmbe(uint8);
// operators
bool operator ==(const CDvFramePacket &) const;
@ -86,10 +88,12 @@ protected:
// get
uint8 *GetAmbeData(void) { return m_uiAmbe; }
uint8 *GetAmbePlusData(void) { return m_uiAmbePlus; }
uint8 *GetCodec2Data(void) { return m_uiCodec2; }
protected:
// data (dstar)
uint8 m_uiAmbe[AMBE_SIZE];
uint8 m_uiCodec2[AMBE_SIZE];
uint8 m_uiDvData[DVDATA_SIZE];
// data (dmr)
uint8 m_uiAmbePlus[AMBEPLUS_SIZE];

@ -97,12 +97,12 @@ CPacket *CDvHeaderPacket::Duplicate(void) const
////////////////////////////////////////////////////////////////////////////////////////
// conversion
void CDvHeaderPacket::ConvertToDstarStruct(struct dstar_header *buffer) const
void CDvHeaderPacket::ConvertToDstarStruct(struct dstar_header *buffer, uint8 CodecOut) const
{
::memset(buffer, 0, sizeof(struct dstar_header));
buffer->Flag1 = m_uiFlag1;
buffer->Flag2 = m_uiFlag2;
buffer->Flag3 = m_uiFlag3;
buffer->Flag3 = (CodecOut == CODEC_AMBEPLUS) ? 0x00 : 0x01 ;
m_csUR.GetCallsign(buffer->UR);
m_csRPT1.GetCallsign(buffer->RPT1);
m_csRPT2.GetCallsign(buffer->RPT2);
@ -127,6 +127,24 @@ bool CDvHeaderPacket::IsValid(void) const
}
////////////////////////////////////////////////////////////////////////////////////////
// get codec
uint8 CDvHeaderPacket::GetCodec(void) const
{
// The D-STAR vocoder extension by SV9OAN uses Flag 3 of the header
// to identify whether the voice data payload is in Codec 2 format.
// Two Codec 2 modes are allowed (3200 or 2400) and optional FEC.
// Only support 3200 mode and no FEC here.
if ( m_uiFlag3 == 0x01 )
{
return CODEC_CODEC2;
}
return CODEC_AMBEPLUS;
}
////////////////////////////////////////////////////////////////////////////////////////
// operators

@ -75,10 +75,13 @@ public:
bool IsDvHeader(void) const { return true; }
// conversion
void ConvertToDstarStruct(struct dstar_header *) const;
void ConvertToDstarStruct(struct dstar_header *, uint8) const;
// get valid
bool IsValid(void) const;
// get codec
uint8 GetCodec(void) const;
// get callsigns
const CCallsign &GetUrCallsign(void) const { return m_csUR; }

@ -51,9 +51,9 @@ CDvLastFramePacket::CDvLastFramePacket(const uint8 *ambe, const uint8 *sync, uin
CDvLastFramePacket::CDvLastFramePacket
(uint16 sid,
uint8 dstarpid, const uint8 *dstarambe, const uint8 *dstardvdata,
uint8 dstarpid, const uint8 *dstarambe, const uint8 *dstarcodec2, const uint8 *dstardvdata,
uint8 dmrpid, uint8 dprspid, const uint8 *dmrambe, const uint8 *dmrsync)
: CDvFramePacket(sid, dstarpid, dstarambe, dstardvdata, dmrpid, dprspid, dmrambe, dmrsync)
: CDvFramePacket(sid, dstarpid, dstarambe, dstarcodec2, dstardvdata, dmrpid, dprspid, dmrambe, dmrsync)
{
}

@ -42,7 +42,7 @@ public:
CDvLastFramePacket();
CDvLastFramePacket(const struct dstar_dvframe *, uint16, uint8);
CDvLastFramePacket(const uint8 *, const uint8 *, uint16, uint8, uint8);
CDvLastFramePacket(uint16, uint8, const uint8 *, const uint8 *, uint8, uint8, const uint8 *, const uint8 *);
CDvLastFramePacket(uint16, uint8, const uint8 *, const uint8 *, const uint8 *, uint8, uint8, const uint8 *, const uint8 *);
CDvLastFramePacket(const CDvLastFramePacket &);
// destructor

@ -97,6 +97,7 @@ bool CGateKeeper::MayLink(const CCallsign &callsign, const CIp &ip, int protocol
{
// repeaters
case PROTOCOL_DEXTRA:
case PROTOCOL_DEXTRA_OPEN:
case PROTOCOL_DPLUS:
case PROTOCOL_DCS:
case PROTOCOL_DMRPLUS:
@ -137,6 +138,7 @@ bool CGateKeeper::MayTransmit(const CCallsign &callsign, const CIp &ip, int prot
// repeaters, protocol specific
case PROTOCOL_ANY:
case PROTOCOL_DEXTRA:
case PROTOCOL_DEXTRA_OPEN:
case PROTOCOL_DPLUS:
case PROTOCOL_DCS:
case PROTOCOL_DMRPLUS:

@ -34,13 +34,14 @@ CPacketStream::CPacketStream()
m_uiStreamId = 0;
m_uiPacketCntr = 0;
m_OwnerClient = NULL;
m_CodecIn = CODEC_NONE;
m_CodecStream = NULL;
}
////////////////////////////////////////////////////////////////////////////////////////
// open / close
bool CPacketStream::Open(const CDvHeaderPacket &DvHeader, CClient *client)
bool CPacketStream::Open(const CDvHeaderPacket &DvHeader, CClient *client, uint8 CodecIn)
{
bool ok = false;
@ -54,7 +55,8 @@ bool CPacketStream::Open(const CDvHeaderPacket &DvHeader, CClient *client)
m_DvHeader = DvHeader;
m_OwnerClient = client;
m_LastPacketTime.Now();
m_CodecStream = g_Transcoder.GetStream(this, client->GetCodec());
m_CodecIn = CodecIn;
m_CodecStream = g_Transcoder.GetStream(this, CodecIn);
ok = true;
}
return ok;
@ -67,6 +69,7 @@ void CPacketStream::Close(void)
m_uiStreamId = 0;
m_OwnerClient = NULL;
g_Transcoder.ReleaseStream(m_CodecStream);
m_CodecIn = CODEC_NONE;
m_CodecStream = NULL;
}

@ -48,7 +48,7 @@ public:
virtual ~CPacketStream() {};
// open / close
bool Open(const CDvHeaderPacket &, CClient *);
bool Open(const CDvHeaderPacket &, CClient *, uint8);
void Close(void);
// push & pop
@ -63,6 +63,7 @@ public:
bool IsOpen(void) const { return m_bOpen; }
uint16 GetStreamId(void) const { return m_uiStreamId; }
const CCallsign &GetUserCallsign(void) const { return m_DvHeader.GetMyCallsign(); }
uint8 GetCodec(void) const { return m_CodecIn; }
protected:
// data
@ -72,6 +73,7 @@ protected:
CClient *m_OwnerClient;
CTimePoint m_LastPacketTime;
CDvHeaderPacket m_DvHeader;
uint8 m_CodecIn;
CCodecStream *m_CodecStream;
};

@ -140,6 +140,13 @@ void CProtocol::OnDvFramePacketIn(CDvFramePacket *Frame, const CIp *Ip)
CPacketStream *stream = GetStream(Frame->GetStreamId(), Ip);
if ( stream != NULL )
{
// place the voice data in the appropriate place
if ( stream->GetCodec() == CODEC_CODEC2 )
{
Frame->SetAmbe(CODEC_CODEC2, (uint8 *)Frame->GetAmbe());
Frame->ClearAmbe(CODEC_AMBEPLUS);
}
//std::cout << "DV frame" << "from " << *Ip << std::endl;
// and push
stream->Lock();
@ -154,6 +161,13 @@ void CProtocol::OnDvLastFramePacketIn(CDvLastFramePacket *Frame, const CIp *Ip)
CPacketStream *stream = GetStream(Frame->GetStreamId(), Ip);
if ( stream != NULL )
{
// place the voice data in the appropriate place
if ( stream->GetCodec() == CODEC_CODEC2 )
{
Frame->SetAmbe(CODEC_CODEC2, (uint8 *)Frame->GetAmbe());
Frame->ClearAmbe(CODEC_AMBEPLUS);
}
// push
stream->Lock();
stream->Push(Frame);

@ -24,6 +24,7 @@
#include "main.h"
#include "cdextraprotocol.h"
#include "cdextraopenprotocol.h"
#include "cdplusprotocol.h"
#include "cdcsprotocol.h"
#include "cxlxprotocol.h"
@ -67,35 +68,40 @@ bool CProtocols::Init(void)
m_Mutex.lock();
{
// create and initialize DEXTRA
// create and initialize DEXTRA (AMBE output)
delete m_Protocols[0];
m_Protocols[0] = new CDextraProtocol;
ok &= m_Protocols[0]->Init();
// create and initialize DPLUS
// create and initialize DEXTRA (Codec 2 output)
delete m_Protocols[1];
m_Protocols[1] = new CDplusProtocol;
m_Protocols[1] = new CDextraOpenProtocol;
ok &= m_Protocols[1]->Init();
// create and initialize DCS
// create and initialize DPLUS
delete m_Protocols[2];
m_Protocols[2] = new CDcsProtocol;
m_Protocols[2] = new CDplusProtocol;
ok &= m_Protocols[2]->Init();
// create and initialize XLX - interlink
// create and initialize DCS
delete m_Protocols[3];
m_Protocols[3] = new CXlxProtocol;
m_Protocols[3] = new CDcsProtocol;
ok &= m_Protocols[3]->Init();
// create and initialize DMRPLUS
// create and initialize XLX - interlink
delete m_Protocols[4];
m_Protocols[4] = new CDmrplusProtocol;
m_Protocols[4] = new CXlxProtocol;
ok &= m_Protocols[4]->Init();
// create and initialize DMRMMDVM
// create and initialize DMRPLUS
delete m_Protocols[5];
m_Protocols[5] = new CDmrmmdvmProtocol;
m_Protocols[5] = new CDmrplusProtocol;
ok &= m_Protocols[5]->Init();
// create and initialize DMRMMDVM
delete m_Protocols[6];
m_Protocols[6] = new CDmrmmdvmProtocol;
ok &= m_Protocols[6]->Init();
}
m_Mutex.unlock();

@ -183,7 +183,7 @@ bool CReflector::IsStreaming(char module)
return false;
}
CPacketStream *CReflector::OpenStream(CDvHeaderPacket *DvHeader, CClient *client)
CPacketStream *CReflector::OpenStream(CDvHeaderPacket *DvHeader, CClient *client, uint8 CodecIn)
{
CPacketStream *retStream = NULL;
@ -208,7 +208,7 @@ CPacketStream *CReflector::OpenStream(CDvHeaderPacket *DvHeader, CClient *client
// lock it
stream->Lock();
// is it available ?
if ( stream->Open(*DvHeader, client) )
if ( stream->Open(*DvHeader, client, CodecIn) )
{
// stream open, mark client as master
// so that it can't be deleted

@ -75,7 +75,7 @@ public:
void ReleasePeers(void) { m_Peers.Unlock(); }
// stream opening & closing
CPacketStream *OpenStream(CDvHeaderPacket *, CClient *);
CPacketStream *OpenStream(CDvHeaderPacket *, CClient *, uint8 CodecIn);
bool IsStreaming(char);
void CloseStream(CPacketStream *);

@ -215,7 +215,7 @@ CCodecStream *CTranscoder::GetStream(CPacketStream *PacketStream, uint8 uiCodecI
if ( m_bConnected )
{
// yes, post openstream request
EncodeOpenstreamPacket(&Buffer, uiCodecIn, (uiCodecIn == CODEC_AMBEPLUS) ? CODEC_AMBE2PLUS : CODEC_AMBEPLUS);
EncodeOpenstreamPacket(&Buffer, uiCodecIn, CODEC_ALL ^ uiCodecIn);
m_Socket.Send(Buffer, m_Ip, TRANSCODER_PORT);
// wait relpy here
@ -226,7 +226,7 @@ CCodecStream *CTranscoder::GetStream(CPacketStream *PacketStream, uint8 uiCodecI
std::cout << "ambed openstream ok" << std::endl;
// create stream object
stream = new CCodecStream(PacketStream, m_StreamidOpenStream, uiCodecIn, (uiCodecIn == CODEC_AMBEPLUS) ? CODEC_AMBE2PLUS : CODEC_AMBEPLUS);
stream = new CCodecStream(PacketStream, m_StreamidOpenStream, uiCodecIn, CODEC_ALL ^ uiCodecIn);
// init it
if ( stream->Init(m_PortOpenStream) )
@ -381,14 +381,14 @@ void CTranscoder::EncodeKeepAlivePacket(CBuffer *Buffer)
Buffer->Append((uint8 *)(const char *)g_Reflector.GetCallsign(), CALLSIGN_LEN);
}
void CTranscoder::EncodeOpenstreamPacket(CBuffer *Buffer, uint8 uiCodecIn, uint8 uiCodecOut)
void CTranscoder::EncodeOpenstreamPacket(CBuffer *Buffer, uint8 uiCodecIn, uint8 uiCodecsOut)
{
uint8 tag[] = { 'A','M','B','E','D','O','S' };
Buffer->Set(tag, sizeof(tag));
Buffer->Append((uint8 *)(const char *)g_Reflector.GetCallsign(), CALLSIGN_LEN);
Buffer->Append((uint8)uiCodecIn);
Buffer->Append((uint8)uiCodecOut);
Buffer->Append((uint8)uiCodecsOut);
}
void CTranscoder::EncodeClosestreamPacket(CBuffer *Buffer, uint16 uiStreamId)

@ -62,6 +62,9 @@ int CXlxClient::GetCodec(void) const
codec = CODEC_AMBEPLUS;
break;
case XLX_PROTOCOL_REVISION_2:
codec = CODEC_AMBEPLUS; // transcode again anyway
break;
case XLX_PROTOCOL_REVISION_3:
codec = CODEC_NONE;
break;
}

@ -33,7 +33,8 @@
#define XLX_PROTOCOL_REVISION_0 0 // AMBE only, original connect mechanism
#define XLX_PROTOCOL_REVISION_1 1 // AMBE only, revised connect mechanism
#define XLX_PROTOCOL_REVISION_2 2 // Transcoded AMBE+AMBE2 interlink
#define XLX_PROTOCOL_REVISION_2 2 // Transcoded AMBE+AMBE2 interlink (use AMBE only)
#define XLX_PROTOCOL_REVISION_3 3 // Transcoded AMBE+AMBE2+Codec 2 interlink
////////////////////////////////////////////////////////////////////////////////////////

@ -93,7 +93,11 @@ int CXlxPeer::GetProtocolRevision(const CVersion &version)
{
int protrev = XLX_PROTOCOL_REVISION_0;
if ( version.IsEqualOrHigherTo(CVersion(2,2,0)) )
if ( version.IsEqualOrHigherTo(CVersion(3,0,0)) )
{
protrev = XLX_PROTOCOL_REVISION_3;
}
else if ( version.IsEqualOrHigherTo(CVersion(2,2,0)) )
{
protrev = XLX_PROTOCOL_REVISION_2;
}

@ -137,6 +137,7 @@ void CXlxProtocol::Task(void)
break;
case XLX_PROTOCOL_REVISION_1:
case XLX_PROTOCOL_REVISION_2:
case XLX_PROTOCOL_REVISION_3:
default:
// acknowledge the request
EncodeConnectAckPacket(&Buffer, Modules);
@ -425,7 +426,7 @@ bool CXlxProtocol::OnDvHeaderPacketIn(CDvHeaderPacket *Header, const CIp &Ip)
if ( client != NULL )
{
// and try to open the stream
if ( (stream = g_Reflector.OpenStream(Header, client)) != NULL )
if ( (stream = g_Reflector.OpenStream(Header, client, ((CXlxClient *)client)->GetCodec())) != NULL )
{
// keep the handle
m_Streams.push_back(stream);
@ -560,15 +561,32 @@ CDvFramePacket *CXlxProtocol::IsValidDvFramePacket(const CBuffer &Buffer)
(Buffer.size() == 45) && (Buffer.Compare((uint8 *)"DSVT", 4) == 0) &&
(Buffer.data()[4] == 0x20) && (Buffer.data()[8] == 0x20) &&
((Buffer.data()[14] & 0x40) == 0) )
{
// create packet
dvframe = new CDvFramePacket((struct dstar_dvframe *)&(Buffer.data()[15]),
*((uint16 *)&(Buffer.data()[12])), Buffer.data()[14]);
// check validity of packet
if ( !dvframe->IsValid() )
{
delete dvframe;
dvframe = NULL;
}
}
// otherwise try protocol revision 3
if ( (dvframe == NULL) &&
(Buffer.size() == 54) && (Buffer.Compare((uint8 *)"DSVT", 4) == 0) &&
(Buffer.data()[4] == 0x20) && (Buffer.data()[8] == 0x20) &&
((Buffer.data()[14] & 0x40) == 0) )
{
// create packet
dvframe = new CDvFramePacket(
// sid
*((uint16 *)&(Buffer.data()[12])),
// dstar
Buffer.data()[14], &(Buffer.data()[15]), &(Buffer.data()[24]),
Buffer.data()[14], &(Buffer.data()[15]), &(Buffer.data()[24]), &(Buffer.data()[33]),
// dmr
Buffer.data()[27], Buffer.data()[28], &(Buffer.data()[29]), &(Buffer.data()[38]));
Buffer.data()[36], Buffer.data()[37], &(Buffer.data()[38]), &(Buffer.data()[47]));
// check validity of packet
if ( !dvframe->IsValid() )
@ -594,15 +612,32 @@ CDvLastFramePacket *CXlxProtocol::IsValidDvLastFramePacket(const CBuffer &Buffer
(Buffer.size() == 45) && (Buffer.Compare((uint8 *)"DSVT", 4) == 0) &&
(Buffer.data()[4] == 0x20) && (Buffer.data()[8] == 0x20) &&
((Buffer.data()[14] & 0x40) != 0) )
{
// create packet
dvframe = new CDvLastFramePacket((struct dstar_dvframe *)&(Buffer.data()[15]),
*((uint16 *)&(Buffer.data()[12])), Buffer.data()[14]);
// check validity of packet
if ( !dvframe->IsValid() )
{
delete dvframe;
dvframe = NULL;
}
}
// otherwise try protocol revision 3
if ( (dvframe == NULL) &&
(Buffer.size() == 54) && (Buffer.Compare((uint8 *)"DSVT", 4) == 0) &&
(Buffer.data()[4] == 0x20) && (Buffer.data()[8] == 0x20) &&
((Buffer.data()[14] & 0x40) != 0) )
{
// create packet
dvframe = new CDvLastFramePacket(
// sid
*((uint16 *)&(Buffer.data()[12])),
// dstar
Buffer.data()[14], &(Buffer.data()[15]), &(Buffer.data()[24]),
Buffer.data()[14], &(Buffer.data()[15]), &(Buffer.data()[24]), &(Buffer.data()[33]),
// dmr
Buffer.data()[27], Buffer.data()[28], &(Buffer.data()[29]), &(Buffer.data()[38]));
Buffer.data()[36], Buffer.data()[37], &(Buffer.data()[38]), &(Buffer.data()[47]));
// check validity of packet
if ( !dvframe->IsValid() )
@ -692,6 +727,7 @@ bool CXlxProtocol::EncodeDvFramePacket(const CDvFramePacket &Packet, CBuffer *Bu
Buffer->Append(Packet.GetStreamId());
Buffer->Append((uint8)(Packet.GetDstarPacketId() % 21));
Buffer->Append((uint8 *)Packet.GetAmbe(), AMBE_SIZE);
Buffer->Append((uint8 *)Packet.GetCodec2(), AMBE_SIZE);
Buffer->Append((uint8 *)Packet.GetDvData(), DVDATA_SIZE);
Buffer->Append((uint8)Packet.GetDmrPacketId());
@ -713,6 +749,7 @@ bool CXlxProtocol::EncodeDvLastFramePacket(const CDvLastFramePacket &Packet, CBu
Buffer->Append(Packet.GetStreamId());
Buffer->Append((uint8)((Packet.GetPacketId() % 21) | 0x40));
Buffer->Append(dstarambe, sizeof(dstarambe));
Buffer->Append(dstarambe, sizeof(dstarambe));
Buffer->Append(dstardvdata, sizeof(dstardvdata));

@ -52,6 +52,9 @@ public:
// task
void Task(void);
// identity
int GetProtocol(void) const { return PROTOCOL_XLX; }
protected:
// queue helper
void HandleQueue(void);

@ -47,9 +47,9 @@
// version -----------------------------------------------------
#define VERSION_MAJOR 2
#define VERSION_MINOR 2
#define VERSION_REVISION 2
#define VERSION_MAJOR 3
#define VERSION_MINOR 0
#define VERSION_REVISION 0
// global ------------------------------------------------------
@ -65,22 +65,26 @@
// protocols ---------------------------------------------------
#define NB_OF_PROTOCOLS 6
#define NB_OF_PROTOCOLS 7
#define PROTOCOL_ANY -1
#define PROTOCOL_NONE 0
#define PROTOCOL_DEXTRA 1
#define PROTOCOL_DPLUS 2
#define PROTOCOL_DCS 3
#define PROTOCOL_XLX 4
#define PROTOCOL_DMRPLUS 5
#define PROTOCOL_DMRMMDVM 6
// DExtra
#define PROTOCOL_DEXTRA_OPEN 2
#define PROTOCOL_DPLUS 3
#define PROTOCOL_DCS 4
#define PROTOCOL_XLX 5
#define PROTOCOL_DMRPLUS 6
#define PROTOCOL_DMRMMDVM 7
// DExtra (AMBE output)
#define DEXTRA_PORT 30001 // UDP port
#define DEXTRA_KEEPALIVE_PERIOD 3 // in seconds
#define DEXTRA_KEEPALIVE_TIMEOUT (DEXTRA_KEEPALIVE_PERIOD*10) // in seconds
// DExtra (Codec 2 output)
#define DEXTRA_OPEN_PORT 30201 // UDP port
// DPlus
#define DPLUS_PORT 20001 // UDP port
#define DPLUS_KEEPALIVE_PERIOD 1 // in seconds
@ -123,6 +127,8 @@
#define CODEC_NONE 0
#define CODEC_AMBEPLUS 1 // DStar
#define CODEC_AMBE2PLUS 2 // DMR
#define CODEC_CODEC2 4 // Codec 2
#define CODEC_ALL 7 // all available bits set
// DMRid database -----------------------------------------------

Loading…
Cancel
Save

Powered by TurnKey Linux.