mirror of
https://github.com/dj0abr/SSB_HighSpeed_Modem.git
synced 2024-11-22 12:19:49 -05:00
253 lines
7.4 KiB
C++
253 lines
7.4 KiB
C++
|
/*
|
||
|
* High Speed modem to transfer data in a 2,7kHz SSB channel
|
||
|
* =========================================================
|
||
|
* Author: DJ0ABR
|
||
|
*
|
||
|
* (c) DJ0ABR
|
||
|
* www.dj0abr.de
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation; either version 2 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program; if not, write to the Free Software
|
||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
|
*
|
||
|
* voiceprocessor.c ... function to handle voice transfer
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include "hsmodem.h"
|
||
|
|
||
|
int opusbitrate = 0;
|
||
|
|
||
|
void sendCodecToModulator(uint8_t* pdata, int len);
|
||
|
|
||
|
OpusEncoder* opusenc = (OpusEncoder*)NULL;
|
||
|
OpusDecoder* opusdec = (OpusDecoder*)NULL;
|
||
|
|
||
|
void init_voiceproc()
|
||
|
{
|
||
|
if (codec == 1)
|
||
|
{
|
||
|
init_codec2();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (opusbitrate == 0)
|
||
|
{
|
||
|
printf("Codec bitrate not set\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
close_voiceproc();
|
||
|
if (VoiceAudioMode == VOICEMODE_CODECLOOP || VoiceAudioMode == VOICEMODE_DV_FULLDUPLEX || VoiceAudioMode == VOICEMODE_DV_RXONLY)
|
||
|
{
|
||
|
int err;
|
||
|
opusenc = opus_encoder_create(VOICE_SAMPRATE, 1, OPUS_APPLICATION_VOIP, &err);
|
||
|
if (opusenc == (OpusEncoder*)NULL || err != OPUS_OK)
|
||
|
{
|
||
|
printf("opus_encoder_create failed\n");
|
||
|
return;
|
||
|
}
|
||
|
opus_encoder_ctl(opusenc, OPUS_SET_BITRATE(opusbitrate));
|
||
|
printf("set opus rate: %d\n", opusbitrate);
|
||
|
opus_encoder_ctl(opusenc, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE));
|
||
|
opus_encoder_ctl(opusenc, OPUS_SET_VBR(0)); // hard-CBR
|
||
|
opus_encoder_ctl(opusenc, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND));
|
||
|
|
||
|
opusdec = opus_decoder_create(VOICE_SAMPRATE, 1 , &err);
|
||
|
if (opusdec == (OpusDecoder*)NULL || err != OPUS_OK)
|
||
|
{
|
||
|
printf("opus_encoder_create failed\n");
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void close_voiceproc()
|
||
|
{
|
||
|
if(opusenc != (OpusEncoder*)NULL)
|
||
|
opus_encoder_destroy(opusenc);
|
||
|
opusenc = (OpusEncoder*)NULL;
|
||
|
|
||
|
if (opusdec != (OpusDecoder*)NULL)
|
||
|
opus_decoder_destroy(opusdec);
|
||
|
opusdec = (OpusDecoder*)NULL;
|
||
|
}
|
||
|
|
||
|
#define ENC_FRAMESIZE 2880
|
||
|
void encode(float f)
|
||
|
{
|
||
|
if (codec == 1)
|
||
|
{
|
||
|
encode_codec2(f);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (opusenc == (OpusEncoder*)NULL) return;
|
||
|
|
||
|
static float farr[ENC_FRAMESIZE];
|
||
|
static int farridx = 0;
|
||
|
uint8_t opusdata[10000];
|
||
|
|
||
|
// collect samples until we have ENC_FRAMESIZE
|
||
|
farr[farridx] = f;
|
||
|
if (++farridx == ENC_FRAMESIZE)
|
||
|
{
|
||
|
opus_int32 ret = opus_encode_float(opusenc, farr, farridx, opusdata, opusbitrate);
|
||
|
if (ret < 0)
|
||
|
{
|
||
|
printf("opus_encode_float error: %d", ret);
|
||
|
}
|
||
|
|
||
|
// length of an OPUS packet:
|
||
|
// duration[ms] * OutSampRate[kbps] / 8
|
||
|
// line speed 7200: sample rate=6, PacketSize = (ENC_FRAMESIZE*1000/48000) * 6 / 8 = (ENC_FRAMESIZE/48)*6/8 = 45
|
||
|
|
||
|
//measure_speed_bps(ret*8);
|
||
|
//showbytestring("ENC:", opusdata, ret, ret);
|
||
|
|
||
|
// send Codec data to modulator
|
||
|
if (VoiceAudioMode == VOICEMODE_DV_FULLDUPLEX)
|
||
|
{
|
||
|
memmove(opusdata + 1, opusdata, ret);
|
||
|
opusdata[0] = 0xff; // start of opus packet marker
|
||
|
sendCodecToModulator(opusdata, ret+1);
|
||
|
}
|
||
|
|
||
|
// decode and send samples to loadspeaker
|
||
|
if (VoiceAudioMode == VOICEMODE_CODECLOOP)
|
||
|
{
|
||
|
static float fresult[ENC_FRAMESIZE];
|
||
|
int r = opus_decode_float(opusdec, opusdata, ret, fresult, ENC_FRAMESIZE, 0);
|
||
|
if (r < 0)
|
||
|
{
|
||
|
printf("opus_decode_float error: %d", r);
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < r; i++)
|
||
|
pb_write_fifo_voice(fresult[i]);
|
||
|
}
|
||
|
|
||
|
farridx = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// void toGR_sendData(uint8_t* data, int type, int status)
|
||
|
// this function expects "data" with length: payloadlen
|
||
|
// so we have to collect samples until one payload is filled
|
||
|
// this may take too long, in this case send the frame immediately
|
||
|
// the first byte in the payload is the marker 0xff
|
||
|
// type=6 ... voice payload
|
||
|
// minfo=1 ... voice data available
|
||
|
// minfo=0 ... filler payload, just ignore
|
||
|
|
||
|
void sendCodecToModulator(uint8_t *pdata, int len)
|
||
|
{
|
||
|
static uint8_t payload[PAYLOADLEN];
|
||
|
static int vdidx = 0;
|
||
|
|
||
|
// fill the new voice data "pdata" into the buffer "voicedata"
|
||
|
for (int i = 0; i < len; i++)
|
||
|
{
|
||
|
if (vdidx >= PAYLOADLEN) printf("vdidx too high: %d", vdidx);
|
||
|
payload[vdidx++] = pdata[i];
|
||
|
|
||
|
// if the voicedata buffer is full, send it to the modem
|
||
|
if (vdidx == PAYLOADLEN)
|
||
|
{
|
||
|
vdidx = 0;
|
||
|
toGR_sendData(payload, 6, 1); // 6 ... voice data, 1 ... valid voice data
|
||
|
}
|
||
|
|
||
|
while (1)
|
||
|
{
|
||
|
// we have to check if the TX fifo has enough data. In case of an underrun the Q(8A)PSK signal will be distorted
|
||
|
int us = pb_fifo_usedspace();
|
||
|
if (us < 20000)
|
||
|
{
|
||
|
//printf("tx filler\n");
|
||
|
// not enough samples in the TX buffer
|
||
|
// send a dummy frame, a frame with 0 voice data
|
||
|
uint8_t dummy[PAYLOADLEN];
|
||
|
memset(dummy, 0, PAYLOADLEN);
|
||
|
toGR_sendData(dummy, 6, 0);
|
||
|
}
|
||
|
else
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// we get the received data stream with length: PAYLOADLEN
|
||
|
// find opus packets marked with 0xff and a length of opusPacketSize
|
||
|
// send send these chunks to the codec_decoder
|
||
|
#define CHUNKSIZE 200
|
||
|
|
||
|
void toCodecDecoder(uint8_t *pdata, int len)
|
||
|
{
|
||
|
if (codec == 1)
|
||
|
{
|
||
|
toCodecDecoder_codec2(pdata, len);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
static uint8_t chunk[CHUNKSIZE];
|
||
|
int opusPacketSize = ((ENC_FRAMESIZE / 48) * (opusbitrate/100)) / 80;
|
||
|
|
||
|
if (opusPacketSize > 45)
|
||
|
{
|
||
|
printf("wrong opusPacketSize: %d\n", opusPacketSize);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// go through all data bytes
|
||
|
for (int vd = 0; vd < len; vd++)
|
||
|
{
|
||
|
// shift the data through the chunk buffer
|
||
|
for (int i = 0; i < CHUNKSIZE - 1; i++)
|
||
|
chunk[i] = chunk[i + 1];
|
||
|
chunk[CHUNKSIZE - 1] = pdata[vd];
|
||
|
|
||
|
// an OPUS packet has max length of 45 Byte.
|
||
|
// in the chunk size of 200 fit minimum 4 chunks
|
||
|
// so lets test if 4 chunks are there, by looking for the marker
|
||
|
// at distance opusPacketSize
|
||
|
int mfound = 1;
|
||
|
for (int m = 0; m < 4; m++)
|
||
|
{
|
||
|
if (chunk[(opusPacketSize+1) * m] != 0xff)
|
||
|
{
|
||
|
mfound = 0;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (mfound)
|
||
|
{
|
||
|
//showbytestring("OPUS:", chunk + 1, opusPacketSize, opusPacketSize);
|
||
|
|
||
|
static float fresult[ENC_FRAMESIZE];
|
||
|
int r = opus_decode_float(opusdec, chunk + 1, opusPacketSize, fresult, ENC_FRAMESIZE, 0);
|
||
|
if (r < 0)
|
||
|
{
|
||
|
printf("opus_decode_float error: %d\n", r);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//measure_speed_bps(r);
|
||
|
for (int j = 0; j < r; j++)
|
||
|
pb_write_fifo_voice(fresult[j]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|