SSB_HighSpeed_Modem/hsmodem/voiceprocessor.cpp
2021-02-22 15:58:19 +01:00

297 lines
8.7 KiB
C++
Executable File

/*
* 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);
}
kmaudio_playsamples(voice_pbidx, fresult, r,lsvol);
}
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 ,0); // 6 ... voice data, 1 ... valid voice data
}
while (keeprunning)
{
// 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 = io_fifo_usedspace(io_pbidx);
if (us > 20000) break; // too many data in playback fifo, do nothing
if (us < 5000)
{
//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, 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);
kmaudio_playsamples(voice_pbidx, fresult, r,lsvol);
}
}
}
}
void io_saveStream(float *fa, int anz)
{
static FILE* fw = NULL;
static int old_VoiceAudioMode = 0;
if (VoiceAudioMode != old_VoiceAudioMode)
{
if (old_VoiceAudioMode == VOICEMODE_OFF && VoiceAudioMode == VOICEMODE_RECORD)
{
char fn[500];
snprintf(fn, 499, "%s/oscardata/intro/intro.pcm", homepath);
fn[499] = 0;
// audio was switched on, open file to save PCM stream
if (fw) fclose(fw);
fw = fopen(fn, "wb");
if (!fw) printf("cannot open pcm file:%s\n", fn);
else printf("AUDIO RECORDING:%s ...\n Speak, then switch off\n", fn);
}
if (VoiceAudioMode == VOICEMODE_OFF)
{
// audio was switched off, stop recording
if (fw)
{
fclose(fw);
printf("AUDIO RECORDING off\n");
}
fw = NULL;
}
old_VoiceAudioMode = VoiceAudioMode;
}
if (fw)
{
for (int n = 0; n < anz; n++)
{
float f = fa[n];
if (f > 1) f = 1;
if (f < -1) f = -1;
int16_t sh = (int16_t)(f * 32768.0f);
fwrite(&sh, sizeof(int16_t), 1, fw);
}
}
}