add experimental support to encode audio in G.711 uLaw, optionally including a length header;

82-dvmbridge---implement-notch-filter-for-2175hz-trc-guard-tone
Bryan Biedenkapp 1 year ago
parent c1d8186da9
commit 333ac5c164

@ -58,7 +58,7 @@ network:
# Enable PCM audio over UDP.
udpAudio: false
# Enable meta data such as dstId and srcId in the UDP data
# Enable meta data such as dstId and srcId in the UDP data.
udpMetadata: false
# PCM over UDP send port.
udpSendPort: 34001
@ -68,6 +68,11 @@ network:
udpReceivePort: 32001
# PCM over UDP receive address.
udpReceiveAddress: "127.0.0.1"
# Flag indicating UDP audio should be encoded using G.711 uLaw.
udpUseULaw: false
# Flag indicating UDP audio should be transmitted without the length leader.
# NOTE: This flag is only applicable when encoding G.711 uLaw.
udpNoIncludeLength: false
# Source "Radio ID" for transmitted audio frames.
sourceId: 1234567

@ -55,6 +55,18 @@ const int NUMBER_OF_BUFFERS = 32;
#define LOCAL_CALL "Local Traffic"
#define UDP_CALL "UDP Traffic"
#define SIGN_BIT (0x80) // sign bit for a A-law byte
#define QUANT_MASK (0xf) // quantization field mask
#define NSEGS (8) // number of A-law segments
#define SEG_SHIFT (4) // left shift for segment number
#define SEG_MASK (0x70) // segment field mask
static short seg_aend[8] = { 0x1F, 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF };
static short seg_uend[8] = { 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF };
#define BIAS (0x84) // bias for linear code
#define CLIP 8159
// ---------------------------------------------------------------------------
// Static Class Members
// ---------------------------------------------------------------------------
@ -135,6 +147,128 @@ void mdcPacketDetected(int frameCount, mdc_u8_t op, mdc_u8_t arg, mdc_u16_t unit
}
}
/* */
static short search(short val, short* table, short size)
{
for (short i = 0; i < size; i++) {
if (val <= *table++)
return (i);
}
return (size);
}
/* Helper to convert PCM into G.711 aLaw. */
uint8_t pcmToaLaw(short pcm)
{
short mask;
unsigned char aval;
pcm = pcm >> 3;
if (pcm >= 0) {
mask = 0xD5; // sign (7th) bit = 1
} else {
mask = 0x55; // sign bit = 0
pcm = -pcm - 1;
}
// convert the scaled magnitude to segment number
short seg = search(pcm, seg_aend, 8);
/*
** combine the sign, segment, quantization bits
*/
if (seg >= 8) // out of range, return maximum value
return (uint8_t)(0x7F ^ mask);
else {
aval = (uint8_t) seg << SEG_SHIFT;
if (seg < 2)
aval |= (pcm >> 1) & QUANT_MASK;
else
aval |= (pcm >> seg) & QUANT_MASK;
return (aval ^ mask);
}
}
/* Helper to convert G.711 aLaw into PCM. */
short aLawToPCM(uint8_t alaw)
{
alaw ^= 0x55;
short t = (alaw & QUANT_MASK) << 4;
short seg = ((unsigned)alaw & SEG_MASK) >> SEG_SHIFT;
switch (seg) {
case 0:
t += 8;
break;
case 1:
t += 0x108;
break;
default:
t += 0x108;
t <<= seg - 1;
}
return ((alaw & SIGN_BIT) ? t : -t);
}
/* Helper to convert PCM into G.711 uLaw. */
uint8_t pcmTouLaw(short pcm)
{
short mask;
// get the sign and the magnitude of the value
pcm = pcm >> 2;
if (pcm < 0) {
pcm = -pcm;
mask = 0x7FU;
} else {
mask = 0xFFU;
}
// clip the magnitude
if (pcm > CLIP)
pcm = CLIP;
pcm += (BIAS >> 2);
// convert the scaled magnitude to segment number
short seg = search(pcm, seg_uend, 8);
/*
** combine the sign, segment, quantization bits;
** and complement the code word
*/
if (seg >= 8) // out of range, return maximum value.
return (uint8_t)(0x7F ^ mask);
else {
uint8_t ulaw = (uint8_t)(seg << 4) | ((pcm >> (seg + 1)) & 0xF);
return (ulaw ^ mask);
}
}
/* Helper to convert G.711 uLaw into PCM. */
short uLawToPCM(uint8_t ulaw)
{
// complement to obtain normal u-law value
ulaw = ~ulaw;
/*
** extract and bias the quantization bits; then
** shift up by the segment number and subtract out the bias
*/
short t = ((ulaw & QUANT_MASK) << 3) + BIAS;
t <<= ((unsigned)ulaw & SEG_MASK) >> SEG_SHIFT;
return ((ulaw & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
}
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
@ -152,6 +286,8 @@ HostBridge::HostBridge(const std::string& confFile) :
m_udpSendAddress("127.0.0.1"),
m_udpReceivePort(32001),
m_udpReceiveAddress("127.0.0.1"),
m_udpNoIncludeLength(false),
m_udpUseULaw(false),
m_srcId(p25::defines::WUID_FNE),
m_srcIdOverride(0U),
m_overrideSrcIdFromMDC(false),
@ -867,6 +1003,13 @@ bool HostBridge::createNetwork()
m_udpSendAddress = networkConf["udpSendAddress"].as<std::string>();
m_udpReceivePort = (uint16_t)networkConf["udpReceivePort"].as<uint32_t>(34001);
m_udpReceiveAddress = networkConf["udpReceiveAddress"].as<std::string>();
m_udpUseULaw = networkConf["udpUseULaw"].as<bool>(false);
if (m_udpUseULaw)
m_udpNoIncludeLength = networkConf["udpNoIncludeLength"].as<bool>(false);
if (m_udpUseULaw && m_udpMetadata)
m_udpMetadata = false; // metadata isn't supported when encoding uLaw
m_srcId = (uint32_t)networkConf["sourceId"].as<uint32_t>(p25::defines::WUID_FNE);
m_overrideSrcIdFromMDC = networkConf["overrideSourceIdFromMDC"].as<bool>(false);
@ -929,6 +1072,10 @@ bool HostBridge::createNetwork()
LogInfo(" UDP Audio Send Port: %u", m_udpSendPort);
LogInfo(" UDP Audio Receive Address: %s", m_udpReceiveAddress.c_str());
LogInfo(" UDP Audio Receive Port: %u", m_udpReceivePort);
LogInfo(" UDP Audio Use uLaw Encoding: %u", m_udpUseULaw ? "yes" : "no");
if (m_udpUseULaw) {
LogInfo(" UDP Audio No Length Header: %u", m_udpNoIncludeLength ? "yes" : "no");
}
}
LogInfo(" Source ID: %u", m_srcId);
@ -1004,12 +1151,22 @@ void HostBridge::processUDPAudio()
if (m_debug)
Utils::dump(1U, "UDP Audio Network Packet", buffer, length);
uint32_t pcmLength = __GET_UINT32(buffer, 0U);
uint32_t pcmLength = 0;
if (m_udpNoIncludeLength) {
pcmLength = length;
} else {
pcmLength = __GET_UINT32(buffer, 0U);
}
UInt8Array __pcm = std::make_unique<uint8_t[]>(pcmLength);
uint8_t* pcm = __pcm.get();
::memcpy(pcm, buffer + 4U, pcmLength);
if (m_udpNoIncludeLength) {
::memcpy(pcm, buffer, pcmLength);
}
else {
::memcpy(pcm, buffer + 4U, pcmLength);
}
// Utils::dump(1U, "PCM RECV BYTE BUFFER", pcm, pcmLength);
@ -1025,9 +1182,17 @@ void HostBridge::processUDPAudio()
int smpIdx = 0;
short samples[MBE_SAMPLES_LENGTH];
for (uint32_t pcmIdx = 0; pcmIdx < pcmLength; pcmIdx += 2) {
samples[smpIdx] = (short)((pcm[pcmIdx + 1] << 8) + pcm[pcmIdx + 0]);
smpIdx++;
if (m_udpUseULaw) {
for (uint32_t pcmIdx = 0; pcmIdx < pcmLength; pcmIdx++) {
samples[smpIdx] = uLawToPCM(pcm[pcmIdx]);
smpIdx++;
}
}
else {
for (uint32_t pcmIdx = 0; pcmIdx < pcmLength; pcmIdx += 2) {
samples[smpIdx] = (short)((pcm[pcmIdx + 1] << 8) + pcm[pcmIdx + 0]);
smpIdx++;
}
}
m_inputAudio.addData(samples, MBE_SAMPLES_LENGTH);
@ -1307,18 +1472,37 @@ void HostBridge::decodeDMRAudioFrame(uint8_t* ambe, uint32_t srcId, uint32_t dst
if (m_udpAudio) {
int pcmIdx = 0;
uint8_t pcm[MBE_SAMPLES_LENGTH * 2U];
for (uint32_t smpIdx = 0; smpIdx < MBE_SAMPLES_LENGTH; smpIdx++) {
pcm[pcmIdx + 0] = (uint8_t)(samples[smpIdx] & 0xFF);
pcm[pcmIdx + 1] = (uint8_t)((samples[smpIdx] >> 8) & 0xFF);
pcmIdx += 2;
if (m_udpUseULaw) {
for (uint32_t smpIdx = 0; smpIdx < MBE_SAMPLES_LENGTH; smpIdx++) {
pcm[smpIdx] = pcmTouLaw(samples[smpIdx]);
}
}
else {
for (uint32_t smpIdx = 0; smpIdx < MBE_SAMPLES_LENGTH; smpIdx++) {
pcm[pcmIdx + 0] = (uint8_t)(samples[smpIdx] & 0xFF);
pcm[pcmIdx + 1] = (uint8_t)((samples[smpIdx] >> 8) & 0xFF);
pcmIdx += 2;
}
}
uint32_t length = (MBE_SAMPLES_LENGTH * 2U) + 4U;
uint8_t* audioData = nullptr;
if (!m_udpMetadata) {
audioData = new uint8_t[(MBE_SAMPLES_LENGTH * 2U) + 4U]; // PCM + 4 bytes (PCM length)
__SET_UINT32((MBE_SAMPLES_LENGTH * 2U), audioData, 0U);
::memcpy(audioData + 4U, pcm, MBE_SAMPLES_LENGTH * 2U);
if (m_udpUseULaw) {
length = (MBE_SAMPLES_LENGTH) + 4U;
if (m_udpNoIncludeLength) {
length = MBE_SAMPLES_LENGTH;
::memcpy(audioData, pcm, MBE_SAMPLES_LENGTH);
} else {
__SET_UINT32(MBE_SAMPLES_LENGTH, audioData, 0U);
::memcpy(audioData + 4U, pcm, MBE_SAMPLES_LENGTH);
}
}
else {
__SET_UINT32((MBE_SAMPLES_LENGTH * 2U), audioData, 0U);
::memcpy(audioData + 4U, pcm, MBE_SAMPLES_LENGTH * 2U);
}
}
else {
length = (MBE_SAMPLES_LENGTH * 2U) + 12U;
@ -1854,18 +2038,37 @@ void HostBridge::decodeP25AudioFrame(uint8_t* ldu, uint32_t srcId, uint32_t dstI
if (m_udpAudio) {
int pcmIdx = 0;
uint8_t pcm[MBE_SAMPLES_LENGTH * 2U];
for (uint32_t smpIdx = 0; smpIdx < MBE_SAMPLES_LENGTH; smpIdx++) {
pcm[pcmIdx + 0] = (uint8_t)(samples[smpIdx] & 0xFF);
pcm[pcmIdx + 1] = (uint8_t)((samples[smpIdx] >> 8) & 0xFF);
pcmIdx += 2;
if (m_udpUseULaw) {
for (uint32_t smpIdx = 0; smpIdx < MBE_SAMPLES_LENGTH; smpIdx++) {
pcm[smpIdx] = pcmTouLaw(samples[smpIdx]);
}
}
else {
for (uint32_t smpIdx = 0; smpIdx < MBE_SAMPLES_LENGTH; smpIdx++) {
pcm[pcmIdx + 0] = (uint8_t)(samples[smpIdx] & 0xFF);
pcm[pcmIdx + 1] = (uint8_t)((samples[smpIdx] >> 8) & 0xFF);
pcmIdx += 2;
}
}
uint32_t length = (MBE_SAMPLES_LENGTH * 2U) + 4U;
uint8_t* audioData = nullptr;
if (!m_udpMetadata) {
audioData = new uint8_t[(MBE_SAMPLES_LENGTH * 2U) + 4U]; // PCM + 4 bytes (PCM length)
__SET_UINT32((MBE_SAMPLES_LENGTH * 2U), audioData, 0U);
::memcpy(audioData + 4U, pcm, MBE_SAMPLES_LENGTH * 2U);
if (m_udpUseULaw) {
length = (MBE_SAMPLES_LENGTH) + 4U;
if (m_udpNoIncludeLength) {
length = MBE_SAMPLES_LENGTH;
::memcpy(audioData, pcm, MBE_SAMPLES_LENGTH);
} else {
__SET_UINT32(MBE_SAMPLES_LENGTH, audioData, 0U);
::memcpy(audioData + 4U, pcm, MBE_SAMPLES_LENGTH);
}
}
else {
__SET_UINT32((MBE_SAMPLES_LENGTH * 2U), audioData, 0U);
::memcpy(audioData + 4U, pcm, MBE_SAMPLES_LENGTH * 2U);
}
}
else {
length = (MBE_SAMPLES_LENGTH * 2U) + 12U;

@ -91,6 +91,31 @@ void audioCallback(ma_device* device, void* output, const void* input, ma_uint32
void mdcPacketDetected(int frameCount, mdc_u8_t op, mdc_u8_t arg, mdc_u16_t unitID,
mdc_u8_t extra0, mdc_u8_t extra1, mdc_u8_t extra2, mdc_u8_t extra3, void* context);
/**
* @brief Helper to convert PCM into G.711 aLaw.
* @param pcm PCM value.
* @return uint8_t aLaw value.
*/
uint8_t pcmToaLaw(short pcm);
/**
* @brief Helper to convert G.711 aLaw into PCM.
* @param alaw aLaw value.
* @return short PCM value.
*/
short aLawToPCM(uint8_t alaw);
/**
* @brief Helper to convert PCM into G.711 uLaw.
* @param pcm PCM value.
* @return uint8_t uLaw value.
*/
uint8_t pcmTouLaw(short pcm);
/**
* @brief Helper to convert G.711 uLaw into PCM.
* @param ulaw uLaw value.
* @return short PCM value.
*/
short uLawToPCM(uint8_t ulaw);
// ---------------------------------------------------------------------------
// Class Declaration
// ---------------------------------------------------------------------------
@ -134,6 +159,8 @@ private:
std::string m_udpSendAddress;
uint16_t m_udpReceivePort;
std::string m_udpReceiveAddress;
bool m_udpNoIncludeLength;
bool m_udpUseULaw;
uint32_t m_srcId;
uint32_t m_srcIdOverride;

Loading…
Cancel
Save

Powered by TurnKey Linux.