mirror of
https://github.com/dj0abr/SSB_HighSpeed_Modem.git
synced 2026-06-03 06:24:51 -04:00
update
This commit is contained in:
+3
-3
@@ -1,8 +1,8 @@
|
||||
# makefile for dv_serial
|
||||
# makefile for hsmodem
|
||||
|
||||
CXXFLAGS = -Wall -O3 -std=c++0x -Wno-write-strings -Wno-narrowing
|
||||
LDFLAGS = -lpthread -lrt -lsndfile -lasound -lm -lbass -lbassflac -lfftw3 -lfftw3_threads -lliquid
|
||||
OBJ = hsmodem.o constellation.o crc16.o frame_packer.o main_helper.o scrambler.o speed.o fec.o audio.o udp.o fft.o liquid_if.o symboltracker.o
|
||||
LDFLAGS = -lpthread -lrt -lsndfile -lasound -lm -lopus -lbassopus -lbass -lbassenc_opus -lbassenc -lfftw3 -lfftw3_threads -lliquid -lcodec2
|
||||
OBJ = hsmodem.o constellation.o crc16.o frame_packer.o main_helper.o scrambler.o speed.o fec.o audio.o udp.o fft.o liquid_if.o symboltracker.o audio_voice.o voiceprocessor.o codec2.o
|
||||
|
||||
default: $(OBJ)
|
||||
g++ $(CXXFLAGS) -o ../LinuxRelease/hsmodem $(OBJ) $(LDFLAGS)
|
||||
|
||||
+154
-70
@@ -38,7 +38,6 @@
|
||||
BOOL CALLBACK RecordingCallback(HRECORD handle, const void *buffer, DWORD length, void *user);
|
||||
DWORD CALLBACK WriteStream(HSTREAM handle, float *buffer, DWORD length, void *user);
|
||||
int pb_read_fifo(float *data, int elements);
|
||||
void close_audio();
|
||||
void cap_write_fifo(float sample);
|
||||
int pb_fifo_freespace(int nolock);
|
||||
void init_pipes();
|
||||
@@ -96,12 +95,6 @@ uint8_t devstring[MAXDEVSTRLEN +100];
|
||||
char PBdevs[100][256]; // stores the device names, just for diagnosis, has no real fuction
|
||||
char CAPdevs[100][256];
|
||||
|
||||
// audio device description table
|
||||
typedef struct {
|
||||
int bassdev; // bass (basswasapi) dev no
|
||||
char name[256]; // DEV name
|
||||
} AUDIODEVS;
|
||||
|
||||
// index is enumerated number, 0=default
|
||||
AUDIODEVS audioPBdevs[100];
|
||||
AUDIODEVS audioCAPdevs[100];
|
||||
@@ -111,6 +104,8 @@ int pbanz = 0, capanz = 0;
|
||||
void readAudioDevs()
|
||||
{
|
||||
int a;
|
||||
pbanz = 0;
|
||||
capanz = 0;
|
||||
|
||||
// enter default device manually
|
||||
audioPBdevs[pbanz].bassdev = -1;
|
||||
@@ -125,7 +120,7 @@ void readAudioDevs()
|
||||
BASS_DEVICEINFO info;
|
||||
for (a = 1; BASS_GetDeviceInfo(a, &info); a++)
|
||||
{
|
||||
showDeviceInfo(info);
|
||||
//showDeviceInfo(info);
|
||||
if (info.flags & BASS_DEVICE_ENABLED && !(info.flags & BASS_DEVICE_LOOPBACK))
|
||||
{
|
||||
if (!strstr(info.name, "efault"))
|
||||
@@ -133,6 +128,7 @@ void readAudioDevs()
|
||||
audioPBdevs[pbanz].bassdev = a;
|
||||
strncpy(audioPBdevs[pbanz].name, info.name, 255);
|
||||
audioPBdevs[pbanz].name[255] = 0;
|
||||
strncpy(audioPBdevs[pbanz].id, info.driver, 255);
|
||||
pbanz++;
|
||||
}
|
||||
}
|
||||
@@ -148,6 +144,7 @@ void readAudioDevs()
|
||||
audioCAPdevs[capanz].bassdev = a;
|
||||
strncpy(audioCAPdevs[capanz].name, info.name, 255);
|
||||
audioCAPdevs[capanz].name[255] = 0;
|
||||
strncpy(audioCAPdevs[capanz].id, info.driver, 255);
|
||||
capanz++;
|
||||
}
|
||||
}
|
||||
@@ -166,6 +163,7 @@ void readAudioDevs()
|
||||
audioPBdevs[pbanz].bassdev = a;
|
||||
strncpy(audioPBdevs[pbanz].name, info.name, 255);
|
||||
audioPBdevs[pbanz].name[255] = 0;
|
||||
strncpy(audioPBdevs[pbanz].id, info.id, 255);
|
||||
pbanz++;
|
||||
}
|
||||
}
|
||||
@@ -177,6 +175,7 @@ void readAudioDevs()
|
||||
audioCAPdevs[capanz].bassdev = a;
|
||||
strncpy(audioCAPdevs[capanz].name, info.name, 255);
|
||||
audioCAPdevs[capanz].name[255] = 0;
|
||||
strncpy(audioCAPdevs[capanz].id, info.id, 255);
|
||||
capanz++;
|
||||
}
|
||||
}
|
||||
@@ -188,11 +187,11 @@ void printAudioDevs()
|
||||
{
|
||||
printf("PB devices:\n");
|
||||
for (int i = 0; i < pbanz; i++)
|
||||
printf("idx:%d bass:%d name:%s\n", i, audioPBdevs[i].bassdev, audioPBdevs[i].name);
|
||||
printf("idx:%d ID:%s bass:%d name:%s\n", i, audioPBdevs[i].id, audioPBdevs[i].bassdev, audioPBdevs[i].name);
|
||||
|
||||
printf("CAP devices:\n");
|
||||
for (int i = 0; i < capanz; i++)
|
||||
printf("idx:%d bass:%d name:%s\n", i, audioCAPdevs[i].bassdev, audioCAPdevs[i].name);
|
||||
printf("idx:%d ID:%s bass:%d name:%s\n", i, audioCAPdevs[i].id, audioCAPdevs[i].bassdev, audioCAPdevs[i].name);
|
||||
}
|
||||
|
||||
// build string of audio device name, to be sent to application as response to Broadcast search
|
||||
@@ -202,6 +201,11 @@ void buildUdpAudioList()
|
||||
{
|
||||
memset(devstring, 0, sizeof(devstring));
|
||||
devstring[0] = ' '; // placeholder for ID for this UDP message
|
||||
devstring[1] = '0' + init_audio_result;
|
||||
devstring[2] = '0' + init_voice_result;
|
||||
|
||||
//printf("init_voice_result:%d\n", devstring[2]);
|
||||
|
||||
|
||||
// playback devices
|
||||
for (int i = 0; i < pbanz; i++)
|
||||
@@ -226,22 +230,34 @@ void buildUdpAudioList()
|
||||
|
||||
uint8_t* getAudioDevicelist(int *len)
|
||||
{
|
||||
// update Status
|
||||
devstring[1] = '0' + init_audio_result;
|
||||
devstring[2] = '0' + init_voice_result;
|
||||
|
||||
*len = strlen((char*)(devstring+1))+1;
|
||||
return devstring;
|
||||
}
|
||||
|
||||
// pbdev, capdev: -1=default device
|
||||
// read audio device list at program start, or if something went wrong
|
||||
void readAudioDevices()
|
||||
{
|
||||
readAudioDevs();
|
||||
//printAudioDevs();
|
||||
buildUdpAudioList();
|
||||
return;
|
||||
}
|
||||
|
||||
// ret: 0=ok, -1=system error, 1=pb error 2=cap error 3=pb+cap error
|
||||
int init_audio(int setpbdev, int setcapdev)
|
||||
{
|
||||
static int f = 1;
|
||||
int ret = 0;
|
||||
|
||||
if (f == 1)
|
||||
{
|
||||
// do only once after program start
|
||||
f = 0;
|
||||
readAudioDevs();
|
||||
printAudioDevs();
|
||||
buildUdpAudioList();
|
||||
readAudioDevices();
|
||||
init_pipes();
|
||||
}
|
||||
|
||||
@@ -262,7 +278,7 @@ static int f = 1;
|
||||
if (HIWORD(BASS_GetVersion()) != BASSVERSION)
|
||||
{
|
||||
printf("An incorrect version of BASS was loaded\n");
|
||||
return -1;
|
||||
return 3;
|
||||
}
|
||||
|
||||
#ifdef _WIN32_
|
||||
@@ -279,25 +295,31 @@ static int f = 1;
|
||||
if (!BASS_Init(pbdev, caprate, 0, NULL, NULL))
|
||||
{
|
||||
printf("Can't initialize output device: %d err:%d\n", pbdev, BASS_ErrorGetCode());
|
||||
return -1;
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
// read real device number
|
||||
int ret = BASS_GetDevice();
|
||||
if (ret == -1)
|
||||
else
|
||||
{
|
||||
printf("BASS_GetDevice: %d err:%d\n", pbdev, BASS_ErrorGetCode());
|
||||
return -1;
|
||||
}
|
||||
pbdev = ret;
|
||||
openpbdev = pbdev;
|
||||
printf("real BASS PB Device No: %d\n", pbdev);
|
||||
|
||||
// set play callback
|
||||
BASS_GetInfo(&info);
|
||||
stream = BASS_StreamCreate(info.freq, CHANNELS, BASS_SAMPLE_FLOAT, (STREAMPROC*)WriteStream, 0); // sample: 32 bit float
|
||||
BASS_ChannelSetAttribute(stream, BASS_ATTRIB_BUFFER, 0); // no buffering for minimum latency
|
||||
BASS_ChannelPlay(stream, FALSE); // start it
|
||||
// read real device number
|
||||
int device = BASS_GetDevice();
|
||||
if (device == -1)
|
||||
{
|
||||
printf("BASS_GetDevice: %d err:%d\n", pbdev, BASS_ErrorGetCode());
|
||||
ret = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
pbdev = device;
|
||||
openpbdev = pbdev;
|
||||
printf("real BASS PB Device No: %d\n", pbdev);
|
||||
|
||||
// set play callback
|
||||
BASS_GetInfo(&info);
|
||||
stream = BASS_StreamCreate(info.freq, CHANNELS, BASS_SAMPLE_FLOAT, (STREAMPROC*)WriteStream, 0); // sample: 32 bit float
|
||||
BASS_ChannelSetAttribute(stream, BASS_ATTRIB_BUFFER, 0); // no buffering for minimum latency
|
||||
BASS_ChannelPlay(stream, FALSE); // start it
|
||||
}
|
||||
}
|
||||
|
||||
// ===== CAPTURE ====
|
||||
|
||||
@@ -305,51 +327,56 @@ static int f = 1;
|
||||
if (!BASS_RecordInit(capdev))
|
||||
{
|
||||
printf("Can't initialize recording device: %d err:%d\n", capdev, BASS_ErrorGetCode());
|
||||
return -1;
|
||||
ret |= 2;
|
||||
}
|
||||
|
||||
// read real device number
|
||||
ret = BASS_GetDevice();
|
||||
if (ret == -1)
|
||||
else
|
||||
{
|
||||
printf("BASS_GetDevice: %d err:%d\n", capdev, BASS_ErrorGetCode());
|
||||
return -1;
|
||||
}
|
||||
capdev = ret;
|
||||
printf("real BASS CAP Device No: %d\n", capdev);
|
||||
|
||||
// set capture callback
|
||||
rchan = BASS_RecordStart(caprate, CHANNELS, BASS_SAMPLE_FLOAT, RecordingCallback, 0);
|
||||
if (!rchan) {
|
||||
printf("Can't start capturing: %d\n", BASS_ErrorGetCode());
|
||||
return -1;
|
||||
// read real device number
|
||||
int device = BASS_RecordGetDevice();
|
||||
if (device == -1)
|
||||
{
|
||||
printf("BASS_GetDevice: %d err:%d\n", capdev, BASS_ErrorGetCode());
|
||||
ret |= 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
capdev = device;
|
||||
printf("real BASS CAP Device No: %d\n", capdev);
|
||||
|
||||
// set capture callback
|
||||
if (rchan) BASS_ChannelStop(rchan);
|
||||
rchan = BASS_RecordStart(caprate, CHANNELS, BASS_SAMPLE_FLOAT, RecordingCallback, 0);
|
||||
if (!rchan)
|
||||
{
|
||||
printf("Can't start capturing: %d\n", BASS_ErrorGetCode());
|
||||
ret |= 2;
|
||||
}
|
||||
else
|
||||
opencapdev = capdev;
|
||||
}
|
||||
}
|
||||
|
||||
printf("audio initialized\n");
|
||||
if(ret == 0)
|
||||
printf("audio started successfully for PBdev:%d and CAPdev:%d\n", openpbdev, opencapdev);
|
||||
else
|
||||
{
|
||||
opencapdev = -1;
|
||||
openpbdev = -1;
|
||||
readAudioDevices();
|
||||
}
|
||||
if (ret == 1)
|
||||
printf("audio initialized: PBerror CapOK\n");
|
||||
if (ret == 2)
|
||||
printf("audio initialized: PBOK CapERROR\n");
|
||||
if (ret == 3)
|
||||
printf("audio initialized: PBerror CapERROR\n");
|
||||
|
||||
opencapdev = capdev;
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _LINUX_
|
||||
void close_audio()
|
||||
{
|
||||
if(stream != 0)
|
||||
{
|
||||
printf("close Audio Devices\n");
|
||||
BASS_ChannelStop(rchan);
|
||||
int rr = BASS_RecordFree();
|
||||
if (!rr) printf("Bass_RecordFree error: %d\n", BASS_ErrorGetCode());
|
||||
|
||||
BASS_StreamFree(stream);
|
||||
int r = BASS_Free();
|
||||
if(!r) printf("Bass_Free error: %d\n", BASS_ErrorGetCode());
|
||||
stream = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void selectPBdevice()
|
||||
{
|
||||
if (!BASS_SetDevice(openpbdev))
|
||||
@@ -362,6 +389,24 @@ void selectCAPdevice()
|
||||
printf("BASS_SetDevice: %d err:%d\n", opencapdev, BASS_ErrorGetCode());
|
||||
}
|
||||
|
||||
void close_audio()
|
||||
{
|
||||
if(stream != 0)
|
||||
{
|
||||
printf("close Audio Devices\n");
|
||||
selectCAPdevice();
|
||||
BASS_ChannelStop(rchan);
|
||||
int rr = BASS_RecordFree();
|
||||
if (!rr) printf("Bass_RecordFree error: %d\n", BASS_ErrorGetCode());
|
||||
|
||||
selectPBdevice();
|
||||
BASS_StreamFree(stream);
|
||||
int r = BASS_Free();
|
||||
if(!r) printf("Bass_Free error: %d\n", BASS_ErrorGetCode());
|
||||
stream = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void setPBvolume(int v)
|
||||
{
|
||||
// the volume comes in % 0..99
|
||||
@@ -429,7 +474,7 @@ void setVolume(int pbcap, int v)
|
||||
{
|
||||
if (pbcap == 0) setPBvolume(v);
|
||||
else setCAPvolume(v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ================ thread safe fifo for audio callback routines ===============
|
||||
@@ -460,9 +505,8 @@ void CAP_UNLOCK() { pthread_mutex_unlock(&cap_crit_sec); }
|
||||
void PB_UNLOCK() { pthread_mutex_unlock(&pb_crit_sec); }
|
||||
#endif
|
||||
|
||||
#define AUDIO_BUFFERMAXTIME 2 // fifo can buffer this time in [s]
|
||||
#define AUDIO_PLAYBACK_BUFLEN (48000 * 10) // space for 10 seconds of samples
|
||||
#define AUDIO_CAPTURE_BUFLEN (48000) // space for 1s
|
||||
#define AUDIO_CAPTURE_BUFLEN 24000 // space for 0.5s
|
||||
|
||||
int cap_wridx=0;
|
||||
int cap_rdidx=0;
|
||||
@@ -496,6 +540,10 @@ void cap_write_fifo(float sample)
|
||||
cap_buffer[cap_wridx] = sample;
|
||||
if(++cap_wridx >= AUDIO_CAPTURE_BUFLEN) cap_wridx = 0;
|
||||
CAP_UNLOCK();
|
||||
|
||||
// if monitoring is activated then write it also to the voice fifo
|
||||
if (VoiceAudioMode == VOICEMODE_LISTENAUDIOIN)
|
||||
toVoice(sample);
|
||||
}
|
||||
|
||||
int cap_read_fifo(float *data)
|
||||
@@ -516,6 +564,35 @@ int cap_read_fifo(float *data)
|
||||
return 1;
|
||||
}
|
||||
|
||||
void cap_write_fifo_clear()
|
||||
{
|
||||
cap_wridx = cap_rdidx = 0;
|
||||
}
|
||||
|
||||
int cap_fifo_freespace()
|
||||
{
|
||||
int freebuf = 0;
|
||||
|
||||
CAP_LOCK;
|
||||
|
||||
int elemInFifo = (cap_wridx + AUDIO_CAPTURE_BUFLEN - cap_rdidx) % AUDIO_CAPTURE_BUFLEN;
|
||||
freebuf = AUDIO_CAPTURE_BUFLEN - elemInFifo;
|
||||
|
||||
CAP_UNLOCK();
|
||||
|
||||
return freebuf;
|
||||
}
|
||||
|
||||
int cap_fifo_usedPercent()
|
||||
{
|
||||
int fs = cap_fifo_freespace();
|
||||
int used = AUDIO_CAPTURE_BUFLEN - fs;
|
||||
used = (used * 100) / AUDIO_CAPTURE_BUFLEN;
|
||||
if (used < 5) printf("used:%d\n", used);
|
||||
return used;
|
||||
}
|
||||
|
||||
|
||||
void pb_write_fifo(float sample)
|
||||
{
|
||||
PB_LOCK;
|
||||
@@ -580,6 +657,7 @@ int pb_read_fifo(float *data, int elements)
|
||||
{
|
||||
// Fifo empty, no data available
|
||||
PB_UNLOCK();
|
||||
//printf("pb fifo empty: TX underrun\n");
|
||||
//printf("pb fifo empty, need:%d have:%d size:%d\n",elements,e,AUDIO_PLAYBACK_BUFLEN);
|
||||
return 0;
|
||||
}
|
||||
@@ -595,7 +673,13 @@ int pb_read_fifo(float *data, int elements)
|
||||
return 1;
|
||||
}
|
||||
|
||||
// ================ Play FLAC Audio File ===========================
|
||||
void clear_audio_fifos()
|
||||
{
|
||||
pb_write_fifo_clear();
|
||||
cap_write_fifo_clear();
|
||||
}
|
||||
|
||||
// ================ Play PCM Audio File ===========================
|
||||
|
||||
typedef struct _AUDIOFILES_ {
|
||||
char fn[256];
|
||||
|
||||
Binary file not shown.
Executable
+500
@@ -0,0 +1,500 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* audio.c ... very similar to audio.c but is used for Microphone / Loudspeaker
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hsmodem.h"
|
||||
|
||||
void init_pipes_voice();
|
||||
BOOL CALLBACK RecordingCallback_voice(HRECORD handle, const void* buffer, DWORD length, void* user);
|
||||
DWORD CALLBACK WriteStream_voice(HSTREAM handle, float* buffer, DWORD length, void* user);
|
||||
//void CALLBACK EncodeProc(HENCODE handle, DWORD channel, const void* buffer, DWORD length, void* user);
|
||||
void cap_write_fifo_voice(float sample);
|
||||
int pb_read_fifo_voice(float* data, int elements);
|
||||
void setLSvolume(int v);
|
||||
void setMICvolume(int v);
|
||||
|
||||
extern AUDIODEVS audioPBdevs[100];
|
||||
extern AUDIODEVS audioCAPdevs[100];
|
||||
extern int pbanz;
|
||||
extern int capanz;
|
||||
|
||||
|
||||
HRECORD rchan_voice = 0; // recording channel
|
||||
BASS_INFO info_voice;
|
||||
HSTREAM stream_voice = 0;
|
||||
|
||||
int openpbdev_voice = -1;
|
||||
int opencapdev_voice = -1;
|
||||
int caprate_voice = VOICE_SAMPRATE;
|
||||
|
||||
int initialLSvol = -1;
|
||||
int initialMICvol = -1;
|
||||
|
||||
float softwareCAPvolume_voice = 1;
|
||||
|
||||
// pbdev, capdev: -1=default device
|
||||
int init_audio_voice(int setpbdev, int setcapdev)
|
||||
{
|
||||
static int f = 1;
|
||||
int ret = 0;
|
||||
|
||||
if (f == 1)
|
||||
{
|
||||
// do only once after program start
|
||||
f = 0;
|
||||
init_pipes_voice();
|
||||
}
|
||||
|
||||
// translate requested device numbers to bass device numbers
|
||||
if (setpbdev < 0 || setpbdev >= pbanz) setpbdev = 0;
|
||||
if (setcapdev < 0 || setcapdev >= capanz) setcapdev = 0;
|
||||
|
||||
int pbdev = -1;
|
||||
if (setpbdev >= 0 && setpbdev < pbanz) pbdev = audioPBdevs[setpbdev].bassdev;
|
||||
int capdev = -2;
|
||||
if (setcapdev >= 0 && setcapdev < capanz) capdev = audioCAPdevs[setcapdev].bassdev;
|
||||
|
||||
printf("voice: init audio_voice, caprate:%d\n", caprate_voice);
|
||||
printf("voice: requested LS device: %d bassno:%d name:%s\n", setpbdev, pbdev, audioPBdevs[setpbdev].name);
|
||||
printf("voice: requested MIC device: %d bassno:%d name:%s\n", setcapdev, capdev, audioCAPdevs[setcapdev].name);
|
||||
|
||||
#ifdef _WIN32_
|
||||
// use WASAPI for Windows to get exclusive access to sound card
|
||||
return init_wasapi_voice(pbdev, capdev);
|
||||
#endif
|
||||
|
||||
#ifdef _LINUX_
|
||||
close_audio_voice();
|
||||
|
||||
if (VoiceAudioMode == 0) return 0; // Voice off
|
||||
|
||||
// ===== PLAYBACK ======
|
||||
|
||||
// initialize default output device
|
||||
if (!BASS_Init(pbdev, caprate_voice, 0, NULL, NULL))
|
||||
{
|
||||
printf("voice: Can't initialize output device: %d err:%d\n", pbdev, BASS_ErrorGetCode());
|
||||
close_audio_voice();
|
||||
ret = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// read real device number
|
||||
int device = BASS_GetDevice();
|
||||
if (device == -1)
|
||||
{
|
||||
printf("voice: BASS_GetDevice: %d err:%d\n", pbdev, BASS_ErrorGetCode());
|
||||
close_audio_voice();
|
||||
ret = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
pbdev = device;
|
||||
openpbdev_voice = pbdev;
|
||||
printf("voice: real BASS PB Device No: %d\n", pbdev);
|
||||
|
||||
// set play callback
|
||||
BASS_GetInfo(&info_voice);
|
||||
stream_voice = BASS_StreamCreate(info_voice.freq, 2, BASS_SAMPLE_FLOAT, (STREAMPROC*)WriteStream_voice, 0); // sample: 32 bit float
|
||||
BASS_ChannelSetAttribute(stream_voice, BASS_ATTRIB_BUFFER, 0); // no buffering for minimum latency
|
||||
BASS_ChannelPlay(stream_voice, FALSE); // start it
|
||||
|
||||
setLSvolume(initialLSvol);
|
||||
}
|
||||
}
|
||||
|
||||
// ===== CAPTURE ====
|
||||
|
||||
// initalize default recording device
|
||||
if (!BASS_RecordInit(capdev))
|
||||
{
|
||||
printf("voice: Can't initialize recording device: %d err:%d\n", capdev, BASS_ErrorGetCode());
|
||||
close_audio_voice();
|
||||
ret |= 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// read real device number
|
||||
int device = BASS_RecordGetDevice();
|
||||
if (device == -1)
|
||||
{
|
||||
printf("voice: BASS_GetDevice: %d err:%d\n", capdev, BASS_ErrorGetCode());
|
||||
close_audio_voice();
|
||||
ret |= 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
capdev = device;
|
||||
printf("voice: real BASS CAP Device No: %d\n", capdev);
|
||||
|
||||
// set capture callback
|
||||
rchan_voice = BASS_RecordStart(caprate_voice, 2, BASS_SAMPLE_FLOAT, RecordingCallback_voice, 0);
|
||||
if (!rchan_voice) {
|
||||
printf("voice: Can't start capturing: %d\n", BASS_ErrorGetCode());
|
||||
close_audio_voice();
|
||||
ret |= 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
opencapdev_voice = capdev;
|
||||
setMICvolume(initialMICvol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
printf("voice started successfully for PBdev:%d and CAPdev:%d\n", openpbdev_voice, opencapdev_voice);
|
||||
else
|
||||
{
|
||||
opencapdev_voice = -1;
|
||||
openpbdev_voice = -1;
|
||||
readAudioDevices();
|
||||
}
|
||||
if (ret == 1)
|
||||
printf("voice initialized: PBerror CapOK\n");
|
||||
if (ret == 2)
|
||||
printf("voice initialized: PBOK CapERROR\n");
|
||||
if (ret == 3)
|
||||
printf("voice initialized: PBerror CapERROR\n");
|
||||
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef _LINUX_
|
||||
void selectPBdevice_voice()
|
||||
{
|
||||
if (!BASS_SetDevice(openpbdev_voice))
|
||||
printf("BASS_SetDevice: %d err:%d\n", openpbdev_voice, BASS_ErrorGetCode());
|
||||
}
|
||||
|
||||
void selectCAPdevice_voice()
|
||||
{
|
||||
if (!BASS_SetDevice(opencapdev_voice))
|
||||
printf("BASS_SetDevice: %d err:%d\n", opencapdev_voice, BASS_ErrorGetCode());
|
||||
}
|
||||
|
||||
void close_audio_voice()
|
||||
{
|
||||
printf("voice: close Audio Devices\n");
|
||||
if (stream_voice > 0)
|
||||
BASS_StreamFree(stream_voice);
|
||||
|
||||
if (openpbdev_voice != -1)
|
||||
{
|
||||
selectPBdevice_voice();
|
||||
int r = BASS_Free();
|
||||
if (!r) printf("voice: Bass_Free error: %d\n", BASS_ErrorGetCode());
|
||||
}
|
||||
|
||||
if (rchan_voice > 0)
|
||||
BASS_ChannelStop(rchan_voice);
|
||||
|
||||
if (opencapdev_voice != -1)
|
||||
{
|
||||
selectCAPdevice_voice();
|
||||
int rr = BASS_RecordFree();
|
||||
if (!rr) printf("voice: Bass_RecordFree error: %d\n", BASS_ErrorGetCode());
|
||||
}
|
||||
|
||||
openpbdev_voice = -1;
|
||||
opencapdev_voice = -1;
|
||||
rchan_voice = 0;
|
||||
stream_voice = 0;
|
||||
}
|
||||
|
||||
void setLSvolume(int v)
|
||||
{
|
||||
if (v < 0 || v>100) return;
|
||||
|
||||
// the volume comes in % 0..99
|
||||
// map to 0..1
|
||||
float vf = v;
|
||||
vf /= 100;
|
||||
|
||||
//printf("set PB volume to:%d / %f [0..1]\n", v, vf );
|
||||
|
||||
selectPBdevice_voice();
|
||||
if (!BASS_SetVolume(vf))
|
||||
printf("setPBvolume: %d err:%d\n", openpbdev_voice, BASS_ErrorGetCode());
|
||||
}
|
||||
|
||||
void setMICvolume(int v)
|
||||
{
|
||||
if (v < 0 || v>100) return;
|
||||
|
||||
// the volume comes in % 0..99
|
||||
// map to min/maxPBvol
|
||||
float vf = v;
|
||||
vf /= 100;
|
||||
|
||||
//printf("set CAP volume to:%d / %f [0..1]\n", v, vf);
|
||||
|
||||
selectCAPdevice_voice();
|
||||
if (!BASS_RecordSetInput(-1, BASS_INPUT_ON, vf))
|
||||
{
|
||||
printf("setCAPvolume: %d err:%d, using software level\n", opencapdev_voice, BASS_ErrorGetCode());
|
||||
softwareCAPvolume_voice = ((float)v / 100);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// capture callback
|
||||
BOOL CALLBACK RecordingCallback_voice(HRECORD handle, const void* buffer, DWORD length, void* user)
|
||||
{
|
||||
//printf("captured %ld samples, channels:%d\n",length/sizeof(float),2);
|
||||
//measure_speed(length/sizeof(float));
|
||||
|
||||
float* fbuffer = (float*)buffer;
|
||||
//showbytestringf((char*)"cap:", fbuffer, 10);
|
||||
//printf("w:%ld ",length/sizeof(float));
|
||||
for (unsigned int i = 0; i < (length / sizeof(float)); i += 2)
|
||||
{
|
||||
//printf("%f\n",fbuffer[i]);
|
||||
cap_write_fifo_voice(fbuffer[i]);
|
||||
}
|
||||
|
||||
return TRUE; // continue recording
|
||||
}
|
||||
|
||||
// play callback
|
||||
// length: bytes. float=4byte, 2channels, so it requests samples*8
|
||||
DWORD CALLBACK WriteStream_voice(HSTREAM handle, float* buffer, DWORD length, void* user)
|
||||
{
|
||||
//printf("requested %ld samples\n", length / sizeof(float));
|
||||
int ret = pb_read_fifo_voice(buffer, length / sizeof(float));
|
||||
if (ret == 0)
|
||||
{
|
||||
// fifo empty, send 00
|
||||
memset(buffer, 0, length);
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// set volume
|
||||
void setVolume_voice(int pbcap, int v)
|
||||
{
|
||||
if (pbcap == 0) setLSvolume(v);
|
||||
else setMICvolume(v);
|
||||
}
|
||||
|
||||
// ================= resampling and other tasks for voice audio =================
|
||||
|
||||
// samples come from the data-audio capture with a speed of caprate
|
||||
// resample (if required) to VOICE_SAMPRATE, which is the voice-audio rate
|
||||
void toVoice(float sample)
|
||||
{
|
||||
if (caprate == VOICE_SAMPRATE)
|
||||
{
|
||||
// resampling not required, just put in LS fifo
|
||||
pb_write_fifo_voice(sample);
|
||||
}
|
||||
else
|
||||
{
|
||||
pb_write_fifo_voice(sample);
|
||||
// samprate of incoming signal is 44100, voice needs 48000
|
||||
// we have 44100 samples/s, so we ar missing 3900 S/s.
|
||||
// if we insert an additional sample every 11 samples
|
||||
// this results in a rate of 48109 S/s
|
||||
static int cnt = 0;
|
||||
if (++cnt >= 11)
|
||||
{
|
||||
cnt = 0;
|
||||
pb_write_fifo_voice(sample);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ================= VOICE FIFOs ===================
|
||||
|
||||
#ifdef _WIN32_
|
||||
CRITICAL_SECTION cap_crit_sec_voice;
|
||||
CRITICAL_SECTION pb_crit_sec_voice;
|
||||
#define CAP_LOCK_VOICE EnterCriticalSection(&cap_crit_sec_voice)
|
||||
#define PB_LOCK_VOICE EnterCriticalSection(&pb_crit_sec_voice)
|
||||
void CAP_UNLOCK_VOICE()
|
||||
{
|
||||
if (&cap_crit_sec_voice != NULL)
|
||||
LeaveCriticalSection(&cap_crit_sec_voice);
|
||||
}
|
||||
void PB_UNLOCK_VOICE()
|
||||
{
|
||||
if (&pb_crit_sec_voice != NULL)
|
||||
LeaveCriticalSection(&pb_crit_sec_voice);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _LINUX_
|
||||
pthread_mutex_t cap_crit_sec_voice;
|
||||
pthread_mutex_t pb_crit_sec_voice;
|
||||
#define CAP_LOCK_VOICE pthread_mutex_lock(&cap_crit_sec_voice)
|
||||
void CAP_UNLOCK_VOICE() { pthread_mutex_unlock(&cap_crit_sec_voice); }
|
||||
#define PB_LOCK_VOICE pthread_mutex_lock(&pb_crit_sec_voice)
|
||||
void PB_UNLOCK_VOICE() { pthread_mutex_unlock(&pb_crit_sec_voice); }
|
||||
#endif
|
||||
|
||||
#define AUDIO_PLAYBACK_BUFLEN_VOICE (48000)
|
||||
#define AUDIO_CAPTURE_BUFLEN_VOICE (48000)
|
||||
|
||||
int cap_wridx_voice = 0;
|
||||
int cap_rdidx_voice = 0;
|
||||
float cap_buffer_voice[AUDIO_CAPTURE_BUFLEN_VOICE];
|
||||
|
||||
int pb_wridx_voice = 0;
|
||||
int pb_rdidx_voice = 0;
|
||||
float pb_buffer_voice[AUDIO_PLAYBACK_BUFLEN_VOICE];
|
||||
|
||||
void init_pipes_voice()
|
||||
{
|
||||
#ifdef _WIN32_
|
||||
if (&cap_crit_sec_voice != NULL) DeleteCriticalSection(&cap_crit_sec_voice);
|
||||
InitializeCriticalSection(&cap_crit_sec_voice);
|
||||
|
||||
if (&pb_crit_sec_voice != NULL) DeleteCriticalSection(&pb_crit_sec_voice);
|
||||
InitializeCriticalSection(&pb_crit_sec_voice);
|
||||
#endif
|
||||
}
|
||||
|
||||
// write one sample into the fifo
|
||||
// overwrite old data if the fifo is full
|
||||
void cap_write_fifo_voice(float sample)
|
||||
{
|
||||
if (((cap_wridx_voice + 1) % AUDIO_CAPTURE_BUFLEN_VOICE) == cap_rdidx_voice)
|
||||
{
|
||||
//printf("cap_voice fifo full\n");
|
||||
CAP_UNLOCK_VOICE();
|
||||
return;
|
||||
}
|
||||
|
||||
CAP_LOCK_VOICE;
|
||||
cap_buffer_voice[cap_wridx_voice] = sample;
|
||||
if (++cap_wridx_voice >= AUDIO_CAPTURE_BUFLEN_VOICE) cap_wridx_voice = 0;
|
||||
CAP_UNLOCK_VOICE();
|
||||
}
|
||||
|
||||
int cap_read_fifo_voice(float* data)
|
||||
{
|
||||
CAP_LOCK_VOICE;
|
||||
|
||||
if (cap_rdidx_voice == cap_wridx_voice)
|
||||
{
|
||||
// Fifo empty, no data available
|
||||
CAP_UNLOCK_VOICE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
*data = cap_buffer_voice[cap_rdidx_voice];
|
||||
if (++cap_rdidx_voice >= AUDIO_CAPTURE_BUFLEN_VOICE) cap_rdidx_voice = 0;
|
||||
CAP_UNLOCK_VOICE();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void cap_write_fifo_clear_voice()
|
||||
{
|
||||
cap_wridx_voice = cap_rdidx_voice = 0;
|
||||
}
|
||||
|
||||
|
||||
void pb_write_fifo_clear_voice()
|
||||
{
|
||||
pb_wridx_voice = pb_rdidx_voice = 0;
|
||||
}
|
||||
|
||||
int pb_fifo_freespace_voice(int nolock)
|
||||
{
|
||||
int freebuf = 0;
|
||||
|
||||
if (nolock == 0) PB_LOCK_VOICE;
|
||||
|
||||
int elemInFifo = (pb_wridx_voice + AUDIO_PLAYBACK_BUFLEN_VOICE - pb_rdidx_voice) % AUDIO_PLAYBACK_BUFLEN_VOICE;
|
||||
freebuf = AUDIO_PLAYBACK_BUFLEN_VOICE - elemInFifo;
|
||||
|
||||
if (nolock == 0) PB_UNLOCK_VOICE();
|
||||
|
||||
return freebuf;
|
||||
}
|
||||
|
||||
void pb_write_fifo_voice(float sample)
|
||||
{
|
||||
PB_LOCK_VOICE;
|
||||
|
||||
// check if there is free space in fifo
|
||||
if (pb_fifo_freespace_voice(1) == 0)
|
||||
{
|
||||
PB_UNLOCK_VOICE();
|
||||
//printf("************* pb fifo_voice full\n");
|
||||
return;
|
||||
}
|
||||
|
||||
pb_buffer_voice[pb_wridx_voice] = sample;
|
||||
if (++pb_wridx_voice >= AUDIO_PLAYBACK_BUFLEN_VOICE) pb_wridx_voice = 0;
|
||||
PB_UNLOCK_VOICE();
|
||||
}
|
||||
|
||||
int pb_fifo_usedspace_voice()
|
||||
{
|
||||
int anz = pb_fifo_freespace_voice(0);
|
||||
return AUDIO_PLAYBACK_BUFLEN_VOICE - anz;
|
||||
}
|
||||
|
||||
// read elements floats from fifo or return 0 if not enough floats are available
|
||||
int pb_read_fifo_voice(float* data, int elements)
|
||||
{
|
||||
//printf("pb read fifo_voice: %d\n",elements);
|
||||
PB_LOCK_VOICE;
|
||||
|
||||
int e = AUDIO_PLAYBACK_BUFLEN_VOICE - pb_fifo_freespace_voice(1);
|
||||
if (e < elements)
|
||||
{
|
||||
// Fifo empty, no data available
|
||||
PB_UNLOCK_VOICE();
|
||||
//printf("pb fifo empty, need:%d have:%d size:%d\n",elements,e,AUDIO_PLAYBACK_BUFLEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < elements; i+=2)
|
||||
{
|
||||
// channel1 and the same for channel 2
|
||||
data[i] = pb_buffer_voice[pb_rdidx_voice];
|
||||
data[i+1] = pb_buffer_voice[pb_rdidx_voice];
|
||||
if (++pb_rdidx_voice >= AUDIO_PLAYBACK_BUFLEN_VOICE) pb_rdidx_voice = 0;
|
||||
}
|
||||
//printf("read %d floats\n",elements);
|
||||
|
||||
PB_UNLOCK_VOICE();
|
||||
return 1;
|
||||
}
|
||||
|
||||
void clear_voice_fifos()
|
||||
{
|
||||
pb_write_fifo_clear_voice();
|
||||
cap_write_fifo_clear_voice();
|
||||
}
|
||||
Executable
+270
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* audio.c ... very similar to audio.c but is used for Microphone / Loudspeaker
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hsmodem.h"
|
||||
|
||||
void cap_write_fifo(float sample);
|
||||
int pb_read_fifo(float* data, int elements);
|
||||
void close_wasapi_voice();
|
||||
DWORD CALLBACK PBcallback_wasapi_voice(void* buffer, DWORD length, void* user);
|
||||
DWORD CALLBACK CAPcallback_wasapi_voice(void* buffer, DWORD length, void* user);
|
||||
void cap_write_fifo_voice(float sample);
|
||||
int pb_read_fifo_voice(float* data, int elements);
|
||||
|
||||
#define WASAPI_CHANNELS_VOICE 2
|
||||
|
||||
float minPBvol_voice = 0;
|
||||
float maxPBvol_voice = 99;
|
||||
float minCAPvol_voice = 0;
|
||||
float maxCAPvol_voice = 99;
|
||||
|
||||
extern int openpbdev_voice;
|
||||
extern int opencapdev_voice;
|
||||
|
||||
extern float softwareCAPvolume_voice;
|
||||
|
||||
int mic_channel_num = 2;
|
||||
|
||||
#ifdef _WIN32_
|
||||
|
||||
int init_wasapi_voice(int pbdev, int capdev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
close_wasapi_voice();
|
||||
|
||||
if (VoiceAudioMode == VOICEMODE_OFF) return 0; // Voice off
|
||||
|
||||
// ======= init PLAYBACK device ========
|
||||
|
||||
// initialize default output device
|
||||
if (!BASS_WASAPI_Init(pbdev, VOICE_SAMPRATE, WASAPI_CHANNELS_VOICE, BASS_WASAPI_EXCLUSIVE, 0.1f, 0, PBcallback_wasapi_voice, NULL))
|
||||
{
|
||||
printf("Can't initialize wasapi voice output device: %d err:%d\n", pbdev, BASS_ErrorGetCode());
|
||||
ret = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// read real device number since a -1 cannot be started
|
||||
int device = BASS_WASAPI_GetDevice();
|
||||
if (device == -1)
|
||||
{
|
||||
printf("BASS_WASAPI_GetDevice_voice: %d err:%d\n", pbdev, BASS_ErrorGetCode());
|
||||
ret = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
pbdev = device;
|
||||
|
||||
// read the possible volume settings
|
||||
BASS_WASAPI_INFO info;
|
||||
if (!BASS_WASAPI_GetInfo(&info))
|
||||
{
|
||||
printf("BASS_WASAPI_GetInfo: %d err:%d\n", pbdev, BASS_ErrorGetCode());
|
||||
ret = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
minPBvol_voice = info.volmin;
|
||||
maxPBvol_voice = info.volmax;
|
||||
|
||||
// start playback
|
||||
if (!BASS_WASAPI_Start())
|
||||
{
|
||||
printf("BASS_WASAPI_Start voice: %d err:%d\n", pbdev, BASS_ErrorGetCode());
|
||||
ret = 1;
|
||||
}
|
||||
else
|
||||
openpbdev_voice = pbdev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ======= init CAPTURE device ========
|
||||
|
||||
// initalize default recording device
|
||||
if (capdev == -1) capdev = -2; // cap: -2 is the default device for input
|
||||
|
||||
BOOL micret = false;
|
||||
mic_channel_num = 2;
|
||||
micret = BASS_WASAPI_Init(capdev, VOICE_SAMPRATE, mic_channel_num, BASS_WASAPI_EXCLUSIVE, 0.1f, 0, CAPcallback_wasapi_voice, NULL);
|
||||
if (!micret)
|
||||
{
|
||||
micret = BASS_WASAPI_Init(capdev, VOICE_SAMPRATE, 1, BASS_WASAPI_EXCLUSIVE, 0.1f, 0, CAPcallback_wasapi_voice, NULL);
|
||||
if (!micret)
|
||||
{
|
||||
printf("Can't initialize wasapi voice recording device: %d err:%d\n", capdev, BASS_ErrorGetCode());
|
||||
ret |= 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
mic_channel_num = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(micret)
|
||||
{
|
||||
printf("mic opened with %d channels\n", mic_channel_num);
|
||||
// read real device number since a -2 cannot be started
|
||||
int device = BASS_WASAPI_GetDevice();
|
||||
if (device == -1)
|
||||
{
|
||||
printf("BASS_WASAPI_GetDevice: voice: %d err:%d\n", capdev, BASS_ErrorGetCode());
|
||||
ret |= 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
capdev = device;
|
||||
|
||||
// read the possible volume settings
|
||||
BASS_WASAPI_INFO info;
|
||||
if (!BASS_WASAPI_GetInfo(&info))
|
||||
{
|
||||
printf("BASS_WASAPI_GetInfo: %d err:%d\n", pbdev, BASS_ErrorGetCode());
|
||||
ret |= 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
minCAPvol_voice = info.volmin;
|
||||
maxCAPvol_voice = info.volmax;
|
||||
|
||||
// start recording
|
||||
if (!BASS_WASAPI_Start())
|
||||
{
|
||||
printf("BASS_WASAPI_Start voice: %d err:%d\n", capdev, BASS_ErrorGetCode());
|
||||
ret |= 2;
|
||||
}
|
||||
else
|
||||
opencapdev_voice = capdev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
printf("wasapi voice started successfully for PBdev:%d and CAPdev:%d\n", openpbdev_voice, opencapdev_voice);
|
||||
else
|
||||
{
|
||||
opencapdev_voice = -1;
|
||||
openpbdev_voice = -1;
|
||||
readAudioDevices();
|
||||
}
|
||||
if (ret == 1)
|
||||
printf("wasapi voice initialized: PBerror CapOK\n");
|
||||
if (ret == 2)
|
||||
printf("wasapi voice initialized: PBOK CapERROR\n");
|
||||
if (ret == 3)
|
||||
printf("wasapi voice initialized: PBerror CapERROR\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int selectPBdevice_wasapi_voice()
|
||||
{
|
||||
if (!BASS_WASAPI_SetDevice(openpbdev_voice))
|
||||
{
|
||||
printf("BASS_WASAPI_SetDevice VOICE: %d err:%d\n", openpbdev_voice, BASS_ErrorGetCode());
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int selectCAPdevice_wasapi_voice()
|
||||
{
|
||||
if (!BASS_WASAPI_SetDevice(opencapdev_voice))
|
||||
{
|
||||
printf("BASS_WASAPI_SetDevice VOICE: %d err:%d\n", opencapdev_voice, BASS_ErrorGetCode());
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void close_wasapi_voice()
|
||||
{
|
||||
printf("close WASAPI Voice Devices\n");
|
||||
|
||||
if (openpbdev_voice != -1)
|
||||
{
|
||||
if(selectPBdevice_wasapi_voice())
|
||||
if (!BASS_WASAPI_Free()) printf("BASS_WASAPI_Free voice: dev:%d err:%d\n", openpbdev_voice, BASS_ErrorGetCode());
|
||||
}
|
||||
|
||||
if (opencapdev_voice != -1)
|
||||
{
|
||||
if(selectCAPdevice_wasapi_voice())
|
||||
if (!BASS_WASAPI_Free()) printf("BASS_WASAPI_Free voice: dev:%d err:%d\n", opencapdev_voice, BASS_ErrorGetCode());
|
||||
}
|
||||
printf("closed WASAPI Voice Devices\n");
|
||||
}
|
||||
|
||||
void setLSvolume(int v)
|
||||
{
|
||||
// the volume comes in % 0..99
|
||||
// map to min/maxPBvol
|
||||
float vf = v * (maxPBvol_voice - minPBvol_voice) / 100 + minPBvol_voice;
|
||||
|
||||
if (vf < minPBvol_voice) vf = minPBvol_voice;
|
||||
if (vf > maxPBvol_voice) vf = maxPBvol_voice;
|
||||
|
||||
//printf("set PB volume to:%d / %f [%f..%f]\n", v, vf, minPBvol_voice, maxPBvol_voice);
|
||||
|
||||
if(selectPBdevice_wasapi_voice())
|
||||
if (!BASS_WASAPI_SetVolume(BASS_WASAPI_CURVE_DB, vf))
|
||||
printf("setPBvolume: %d err:%d\n", openpbdev_voice, BASS_ErrorGetCode());
|
||||
}
|
||||
|
||||
void setMICvolume(int v)
|
||||
{
|
||||
// non of the BASS input level functions are working in WASAPI exclusive mode
|
||||
// so we adjust the input level by software
|
||||
softwareCAPvolume_voice = (float)v;
|
||||
softwareCAPvolume_voice /= 50;
|
||||
}
|
||||
|
||||
DWORD CALLBACK PBcallback_wasapi_voice(void* buffer, DWORD length, void* user)
|
||||
{
|
||||
float* fbuffer = (float*)buffer;
|
||||
int ret = pb_read_fifo_voice(fbuffer, length / sizeof(float));
|
||||
if (ret == 0)
|
||||
{
|
||||
// fifo empty, send 00
|
||||
memset(buffer, 0, length);
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
DWORD CALLBACK CAPcallback_wasapi_voice(void* buffer, DWORD length, void* user)
|
||||
{
|
||||
float* fbuffer = (float*)buffer;
|
||||
for (unsigned int i = 0; i < (length / sizeof(float)); i += mic_channel_num)
|
||||
{
|
||||
cap_write_fifo_voice(fbuffer[i]);
|
||||
}
|
||||
|
||||
return TRUE; // continue recording
|
||||
}
|
||||
|
||||
#endif
|
||||
+106
-106
@@ -24,14 +24,12 @@
|
||||
* wasapi is needed because we need exclusive access to the sound card which is not provided for Windows with the normal bass.lib
|
||||
*/
|
||||
|
||||
|
||||
#include "hsmodem.h"
|
||||
|
||||
#ifdef _WIN32_
|
||||
|
||||
#define WASAPI_CHANNELS 2 // wasapi works with 2 only
|
||||
|
||||
void init_pipes();
|
||||
void cap_write_fifo(float sample);
|
||||
int pb_read_fifo(float* data, int elements);
|
||||
void close_wasapi();
|
||||
@@ -48,104 +46,143 @@ extern int opencapdev;
|
||||
|
||||
float softwareCAPvolume = 0.5;
|
||||
|
||||
int use_wasapi = -1;
|
||||
|
||||
|
||||
int init_wasapi(int pbdev, int capdev)
|
||||
{
|
||||
close_wasapi();
|
||||
int ret = 0;
|
||||
|
||||
use_wasapi = -1;
|
||||
close_wasapi();
|
||||
|
||||
// ======= init PLAYBACK device ========
|
||||
|
||||
// initialize default output device
|
||||
if (!BASS_WASAPI_Init(pbdev, caprate, WASAPI_CHANNELS, BASS_WASAPI_EXCLUSIVE, 0.1f/*buffer in seconds*/, 0, PBcallback_wasapi, NULL))
|
||||
if (!BASS_WASAPI_Init(pbdev, caprate, WASAPI_CHANNELS, BASS_WASAPI_EXCLUSIVE, 0.1f, 0, PBcallback_wasapi, NULL))
|
||||
{
|
||||
printf("Can't initialize output device: %d err:%d\n", pbdev, BASS_ErrorGetCode());
|
||||
return -1;
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
// read real device number since a -1 cannot be started
|
||||
int ret = BASS_WASAPI_GetDevice();
|
||||
if (ret == -1)
|
||||
else
|
||||
{
|
||||
printf("BASS_WASAPI_GetDevice: %d err:%d\n", pbdev, BASS_ErrorGetCode());
|
||||
return -1;
|
||||
}
|
||||
pbdev = ret;
|
||||
|
||||
// read the possible volume settings
|
||||
BASS_WASAPI_INFO info;
|
||||
if (!BASS_WASAPI_GetInfo(&info))
|
||||
{
|
||||
printf("BASS_WASAPI_GetInfo: %d err:%d\n", pbdev, BASS_ErrorGetCode());
|
||||
return -1;
|
||||
}
|
||||
minPBvol = info.volmin;
|
||||
maxPBvol = info.volmax;
|
||||
// read real device number since a -1 cannot be started
|
||||
int device = BASS_WASAPI_GetDevice();
|
||||
if (device == -1)
|
||||
{
|
||||
printf("BASS_WASAPI_GetDevice: %d err:%d\n", pbdev, BASS_ErrorGetCode());
|
||||
ret = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
pbdev = device;
|
||||
|
||||
// start playback
|
||||
if (!BASS_WASAPI_Start())
|
||||
{
|
||||
printf("BASS_WASAPI_Start: %d err:%d\n", pbdev, BASS_ErrorGetCode());
|
||||
return -1;
|
||||
// read the possible volume settings
|
||||
BASS_WASAPI_INFO info;
|
||||
if (!BASS_WASAPI_GetInfo(&info))
|
||||
{
|
||||
printf("BASS_WASAPI_GetInfo: %d err:%d\n", pbdev, BASS_ErrorGetCode());
|
||||
ret = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
minPBvol = info.volmin;
|
||||
maxPBvol = info.volmax;
|
||||
|
||||
// start playback
|
||||
if (!BASS_WASAPI_Start())
|
||||
{
|
||||
printf("BASS_WASAPI_Start: %d err:%d\n", pbdev, BASS_ErrorGetCode());
|
||||
ret = 1;
|
||||
}
|
||||
else
|
||||
openpbdev = pbdev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ======= init CAPTURE device ========
|
||||
|
||||
// initalize default recording device
|
||||
if (capdev == -1) capdev = -2; // cap: -2 is the default device for input
|
||||
if (!BASS_WASAPI_Init(capdev, caprate, WASAPI_CHANNELS, BASS_WASAPI_EXCLUSIVE, 0.1f/*buffer in seconds*/, 0, CAPcallback_wasapi, NULL))
|
||||
if (!BASS_WASAPI_Init(capdev, caprate, WASAPI_CHANNELS, BASS_WASAPI_EXCLUSIVE, 0.1f, 0, CAPcallback_wasapi, NULL))
|
||||
{
|
||||
printf("Can't initialize recording device: %d err:%d\n", capdev, BASS_ErrorGetCode());
|
||||
return -1;
|
||||
ret |= 2;
|
||||
}
|
||||
|
||||
// read real device number since a -2 cannot be started
|
||||
ret = BASS_WASAPI_GetDevice();
|
||||
if (ret == -1)
|
||||
else
|
||||
{
|
||||
printf("BASS_WASAPI_GetDevice: %d err:%d\n", capdev, BASS_ErrorGetCode());
|
||||
return -1;
|
||||
}
|
||||
capdev = ret;
|
||||
|
||||
// read the possible volume settings
|
||||
if (!BASS_WASAPI_GetInfo(&info))
|
||||
// read real device number since a -2 cannot be started
|
||||
int device = BASS_WASAPI_GetDevice();
|
||||
if (device == -1)
|
||||
{
|
||||
printf("BASS_WASAPI_GetDevice: %d err:%d\n", capdev, BASS_ErrorGetCode());
|
||||
ret |= 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
capdev = device;
|
||||
|
||||
// read the possible volume settings
|
||||
BASS_WASAPI_INFO info;
|
||||
if (!BASS_WASAPI_GetInfo(&info))
|
||||
{
|
||||
printf("BASS_WASAPI_GetInfo: %d err:%d\n", pbdev, BASS_ErrorGetCode());
|
||||
ret |= 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
minCAPvol = info.volmin;
|
||||
maxCAPvol = info.volmax;
|
||||
|
||||
// start recording
|
||||
if (!BASS_WASAPI_Start())
|
||||
{
|
||||
printf("BASS_WASAPI_Start: %d err:%d\n", capdev, BASS_ErrorGetCode());
|
||||
ret |= 2;
|
||||
}
|
||||
else
|
||||
opencapdev = capdev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
printf("WASAPI started successfully for PBdev:%d and CAPdev:%d\n", openpbdev, opencapdev);
|
||||
else
|
||||
{
|
||||
printf("BASS_WASAPI_GetInfo: %d err:%d\n", pbdev, BASS_ErrorGetCode());
|
||||
return -1;
|
||||
opencapdev = -1;
|
||||
openpbdev = -1;
|
||||
readAudioDevices();
|
||||
}
|
||||
minCAPvol = info.volmin;
|
||||
maxCAPvol = info.volmax;
|
||||
if (ret == 1)
|
||||
printf("wasapi audio initialized: PBerror CapOK\n");
|
||||
if (ret == 2)
|
||||
printf("wasapi audio initialized: PBOK CapERROR\n");
|
||||
if (ret == 3)
|
||||
printf("wasapi audio initialized: PBerror CapERROR\n");
|
||||
|
||||
// start recording
|
||||
if (!BASS_WASAPI_Start())
|
||||
{
|
||||
printf("BASS_WASAPI_Start: %d err:%d\n", capdev, BASS_ErrorGetCode());
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("WASAPI started successfully for PBdev:%d and CAPdev:%d\n", pbdev, capdev);
|
||||
|
||||
openpbdev = pbdev;
|
||||
opencapdev = capdev;
|
||||
|
||||
use_wasapi = 0;
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void selectPBdevice_wasapi()
|
||||
int selectPBdevice_wasapi()
|
||||
{
|
||||
if (!BASS_WASAPI_SetDevice(openpbdev))
|
||||
{
|
||||
printf("BASS_WASAPI_SetDevice: %d err:%d\n", openpbdev, BASS_ErrorGetCode());
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void selectCAPdevice_wasapi()
|
||||
int selectCAPdevice_wasapi()
|
||||
{
|
||||
if (!BASS_WASAPI_SetDevice(opencapdev))
|
||||
{
|
||||
printf("BASS_WASAPI_SetDevice: %d err:%d\n", opencapdev, BASS_ErrorGetCode());
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void setPBvolume(int v)
|
||||
@@ -159,9 +196,9 @@ void setPBvolume(int v)
|
||||
|
||||
//printf("set PB volume to:%d / %f [%f..%f]\n", v, vf, minPBvol, maxPBvol);
|
||||
|
||||
selectPBdevice_wasapi();
|
||||
if (!BASS_WASAPI_SetVolume(BASS_WASAPI_CURVE_DB, vf))
|
||||
printf("setPBvolume: %d err:%d\n", openpbdev, BASS_ErrorGetCode());
|
||||
if(selectPBdevice_wasapi())
|
||||
if (!BASS_WASAPI_SetVolume(BASS_WASAPI_CURVE_DB, vf))
|
||||
printf("setPBvolume: %d err:%d\n", openpbdev, BASS_ErrorGetCode());
|
||||
}
|
||||
|
||||
void setCAPvolume(int v)
|
||||
@@ -178,14 +215,14 @@ void close_wasapi()
|
||||
|
||||
if (openpbdev != -1)
|
||||
{
|
||||
selectPBdevice_wasapi();
|
||||
if (!BASS_WASAPI_Free()) printf("BASS_WASAPI_Free: dev:%d err:%d\n", openpbdev, BASS_ErrorGetCode());
|
||||
if(selectPBdevice_wasapi())
|
||||
if (!BASS_WASAPI_Free()) printf("BASS_WASAPI_Free: dev:%d err:%d\n", openpbdev, BASS_ErrorGetCode());
|
||||
}
|
||||
|
||||
if (opencapdev != -1)
|
||||
{
|
||||
selectCAPdevice_wasapi();
|
||||
if (!BASS_WASAPI_Free()) printf("BASS_WASAPI_Free: dev:%d err:%d\n", opencapdev, BASS_ErrorGetCode());
|
||||
if(selectCAPdevice_wasapi())
|
||||
if (!BASS_WASAPI_Free()) printf("BASS_WASAPI_Free: dev:%d err:%d\n", opencapdev, BASS_ErrorGetCode());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,44 +253,7 @@ DWORD CALLBACK PBcallback_wasapi(void* buffer, DWORD length, void* user)
|
||||
free(fdata);
|
||||
return length;
|
||||
}
|
||||
/*
|
||||
#define MCHECK 10
|
||||
void nullChecker(float fv, float *pbuf, DWORD len)
|
||||
{
|
||||
static float farr[MCHECK];
|
||||
static int idx = 0;
|
||||
static int f = 1;
|
||||
static int anz = 0;
|
||||
|
||||
if (f)
|
||||
{
|
||||
f = 0;
|
||||
for (int i = 0; i < MCHECK; i++)
|
||||
farr[i] = 1;
|
||||
}
|
||||
|
||||
farr[idx] = fv;
|
||||
idx++;
|
||||
if (idx == MCHECK) idx = 0;
|
||||
|
||||
float nu = 0;
|
||||
for (int i = 0; i < MCHECK; i++)
|
||||
{
|
||||
nu += farr[i];
|
||||
}
|
||||
|
||||
if (nu == 0)
|
||||
{
|
||||
// how many 00s ar in the current buffer
|
||||
int a = 0;
|
||||
for (unsigned int i = 0; i < len-1; i++)
|
||||
{
|
||||
if (pbuf[i] == 0 && pbuf[i+1] == 0) a++;
|
||||
}
|
||||
printf("=============== null sequence detected: %d len:%d nullanz:%d\n",anz++,len,a);
|
||||
}
|
||||
}
|
||||
*/
|
||||
DWORD CALLBACK CAPcallback_wasapi(void* buffer, DWORD length, void* user)
|
||||
{
|
||||
//printf("CAP callback, len:%d\n",length);
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
BASSenc 2.4 C/C++ header file
|
||||
Copyright (c) 2003-2018 Un4seen Developments Ltd.
|
||||
|
||||
See the BASSENC.CHM file for more detailed documentation
|
||||
*/
|
||||
|
||||
#ifndef BASSENC_H
|
||||
#define BASSENC_H
|
||||
|
||||
#include "bass.h"
|
||||
|
||||
#if BASSVERSION!=0x204
|
||||
#error conflicting BASS and BASSenc versions
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef BASSENCDEF
|
||||
#define BASSENCDEF(f) WINAPI f
|
||||
#endif
|
||||
|
||||
typedef DWORD HENCODE; // encoder handle
|
||||
|
||||
// Additional error codes returned by BASS_ErrorGetCode
|
||||
#define BASS_ERROR_ACM_CANCEL 2000 // ACM codec selection cancelled
|
||||
#define BASS_ERROR_CAST_DENIED 2100 // access denied (invalid password)
|
||||
|
||||
// Additional BASS_SetConfig options
|
||||
#define BASS_CONFIG_ENCODE_PRIORITY 0x10300
|
||||
#define BASS_CONFIG_ENCODE_QUEUE 0x10301
|
||||
#define BASS_CONFIG_ENCODE_CAST_TIMEOUT 0x10310
|
||||
|
||||
// Additional BASS_SetConfigPtr options
|
||||
#define BASS_CONFIG_ENCODE_ACM_LOAD 0x10302
|
||||
#define BASS_CONFIG_ENCODE_CAST_PROXY 0x10311
|
||||
|
||||
// BASS_Encode_Start flags
|
||||
#define BASS_ENCODE_NOHEAD 1 // don't send a WAV header to the encoder
|
||||
#define BASS_ENCODE_FP_8BIT 2 // convert floating-point sample data to 8-bit integer
|
||||
#define BASS_ENCODE_FP_16BIT 4 // convert floating-point sample data to 16-bit integer
|
||||
#define BASS_ENCODE_FP_24BIT 6 // convert floating-point sample data to 24-bit integer
|
||||
#define BASS_ENCODE_FP_32BIT 8 // convert floating-point sample data to 32-bit integer
|
||||
#define BASS_ENCODE_FP_AUTO 14 // convert floating-point sample data back to channel's format
|
||||
#define BASS_ENCODE_BIGEND 16 // big-endian sample data
|
||||
#define BASS_ENCODE_PAUSE 32 // start encording paused
|
||||
#define BASS_ENCODE_PCM 64 // write PCM sample data (no encoder)
|
||||
#define BASS_ENCODE_RF64 128 // send an RF64 header
|
||||
#define BASS_ENCODE_MONO 0x100 // convert to mono (if not already)
|
||||
#define BASS_ENCODE_QUEUE 0x200 // queue data to feed encoder asynchronously
|
||||
#define BASS_ENCODE_WFEXT 0x400 // WAVEFORMATEXTENSIBLE "fmt" chunk
|
||||
#define BASS_ENCODE_CAST_NOLIMIT 0x1000 // don't limit casting data rate
|
||||
#define BASS_ENCODE_LIMIT 0x2000 // limit data rate to real-time
|
||||
#define BASS_ENCODE_AIFF 0x4000 // send an AIFF header rather than WAV
|
||||
#define BASS_ENCODE_DITHER 0x8000 // apply dither when converting floating-point sample data to integer
|
||||
#define BASS_ENCODE_AUTOFREE 0x40000 // free the encoder when the channel is freed
|
||||
|
||||
// BASS_Encode_GetACMFormat flags
|
||||
#define BASS_ACM_DEFAULT 1 // use the format as default selection
|
||||
#define BASS_ACM_RATE 2 // only list formats with same sample rate as the source channel
|
||||
#define BASS_ACM_CHANS 4 // only list formats with same number of channels (eg. mono/stereo)
|
||||
#define BASS_ACM_SUGGEST 8 // suggest a format (HIWORD=format tag)
|
||||
|
||||
// BASS_Encode_GetCount counts
|
||||
#define BASS_ENCODE_COUNT_IN 0 // sent to encoder
|
||||
#define BASS_ENCODE_COUNT_OUT 1 // received from encoder
|
||||
#define BASS_ENCODE_COUNT_CAST 2 // sent to cast server
|
||||
#define BASS_ENCODE_COUNT_QUEUE 3 // queued
|
||||
#define BASS_ENCODE_COUNT_QUEUE_LIMIT 4 // queue limit
|
||||
#define BASS_ENCODE_COUNT_QUEUE_FAIL 5 // failed to queue
|
||||
|
||||
// BASS_Encode_CastInit content MIME types
|
||||
#define BASS_ENCODE_TYPE_MP3 "audio/mpeg"
|
||||
#define BASS_ENCODE_TYPE_OGG "audio/ogg"
|
||||
#define BASS_ENCODE_TYPE_AAC "audio/aacp"
|
||||
|
||||
// BASS_Encode_CastGetStats types
|
||||
#define BASS_ENCODE_STATS_SHOUT 0 // Shoutcast stats
|
||||
#define BASS_ENCODE_STATS_ICE 1 // Icecast mount-point stats
|
||||
#define BASS_ENCODE_STATS_ICESERV 2 // Icecast server stats
|
||||
|
||||
typedef void (CALLBACK ENCODEPROC)(HENCODE handle, DWORD channel, const void *buffer, DWORD length, void *user);
|
||||
/* Encoding callback function.
|
||||
handle : The encoder
|
||||
channel: The channel handle
|
||||
buffer : Buffer containing the encoded data
|
||||
length : Number of bytes
|
||||
user : The 'user' parameter value given when starting the encoder */
|
||||
|
||||
typedef void (CALLBACK ENCODEPROCEX)(HENCODE handle, DWORD channel, const void *buffer, DWORD length, QWORD offset, void *user);
|
||||
/* Encoding callback function with offset info.
|
||||
handle : The encoder
|
||||
channel: The channel handle
|
||||
buffer : Buffer containing the encoded data
|
||||
length : Number of bytes
|
||||
offset : File offset of the data
|
||||
user : The 'user' parameter value given when starting the encoder */
|
||||
|
||||
typedef DWORD (CALLBACK ENCODERPROC)(HENCODE handle, DWORD channel, void *buffer, DWORD length, DWORD maxout, void *user);
|
||||
/* Encoder callback function.
|
||||
handle : The encoder
|
||||
channel: The channel handle
|
||||
buffer : Buffer containing the PCM data (input) and receiving the encoded data (output)
|
||||
length : Number of bytes in (-1=closing)
|
||||
maxout : Maximum number of bytes out
|
||||
user : The 'user' parameter value given when calling BASS_Encode_StartUser
|
||||
RETURN : The amount of encoded data (-1=stop) */
|
||||
|
||||
typedef BOOL (CALLBACK ENCODECLIENTPROC)(HENCODE handle, BOOL connect, const char *client, char *headers, void *user);
|
||||
/* Client connection notification callback function.
|
||||
handle : The encoder
|
||||
connect: TRUE/FALSE=client is connecting/disconnecting
|
||||
client : The client's address (xxx.xxx.xxx.xxx:port)
|
||||
headers: Request headers (optionally response headers on return)
|
||||
user : The 'user' parameter value given when calling BASS_Encode_ServerInit
|
||||
RETURN : TRUE/FALSE=accept/reject connection (ignored if connect=FALSE) */
|
||||
|
||||
typedef void (CALLBACK ENCODENOTIFYPROC)(HENCODE handle, DWORD status, void *user);
|
||||
/* Encoder death notification callback function.
|
||||
handle : The encoder
|
||||
status : Notification (BASS_ENCODE_NOTIFY_xxx)
|
||||
user : The 'user' parameter value given when calling BASS_Encode_SetNotify */
|
||||
|
||||
// Encoder notifications
|
||||
#define BASS_ENCODE_NOTIFY_ENCODER 1 // encoder died
|
||||
#define BASS_ENCODE_NOTIFY_CAST 2 // cast server connection died
|
||||
#define BASS_ENCODE_NOTIFY_CAST_TIMEOUT 0x10000 // cast timeout
|
||||
#define BASS_ENCODE_NOTIFY_QUEUE_FULL 0x10001 // queue is out of space
|
||||
#define BASS_ENCODE_NOTIFY_FREE 0x10002 // encoder has been freed
|
||||
|
||||
// BASS_Encode_ServerInit flags
|
||||
#define BASS_ENCODE_SERVER_NOHTTP 1 // no HTTP headers
|
||||
#define BASS_ENCODE_SERVER_META 2 // Shoutcast metadata
|
||||
|
||||
DWORD BASSENCDEF(BASS_Encode_GetVersion)();
|
||||
|
||||
HENCODE BASSENCDEF(BASS_Encode_Start)(DWORD handle, const char *cmdline, DWORD flags, ENCODEPROC *proc, void *user);
|
||||
HENCODE BASSENCDEF(BASS_Encode_StartLimit)(DWORD handle, const char *cmdline, DWORD flags, ENCODEPROC *proc, void *user, DWORD limit);
|
||||
HENCODE BASSENCDEF(BASS_Encode_StartUser)(DWORD handle, const char *filename, DWORD flags, ENCODERPROC *proc, void *user);
|
||||
BOOL BASSENCDEF(BASS_Encode_AddChunk)(HENCODE handle, const char *id, const void *buffer, DWORD length);
|
||||
DWORD BASSENCDEF(BASS_Encode_IsActive)(DWORD handle);
|
||||
BOOL BASSENCDEF(BASS_Encode_Stop)(DWORD handle);
|
||||
BOOL BASSENCDEF(BASS_Encode_StopEx)(DWORD handle, BOOL queue);
|
||||
BOOL BASSENCDEF(BASS_Encode_SetPaused)(DWORD handle, BOOL paused);
|
||||
BOOL BASSENCDEF(BASS_Encode_Write)(DWORD handle, const void *buffer, DWORD length);
|
||||
BOOL BASSENCDEF(BASS_Encode_SetNotify)(DWORD handle, ENCODENOTIFYPROC *proc, void *user);
|
||||
QWORD BASSENCDEF(BASS_Encode_GetCount)(DWORD handle, DWORD count);
|
||||
BOOL BASSENCDEF(BASS_Encode_SetChannel)(DWORD handle, DWORD channel);
|
||||
DWORD BASSENCDEF(BASS_Encode_GetChannel)(HENCODE handle);
|
||||
BOOL BASSENCDEF(BASS_Encode_UserOutput)(DWORD handle, QWORD offset, const void *buffer, DWORD length);
|
||||
|
||||
#ifdef _WIN32
|
||||
DWORD BASSENCDEF(BASS_Encode_GetACMFormat)(DWORD handle, void *form, DWORD formlen, const char *title, DWORD flags);
|
||||
HENCODE BASSENCDEF(BASS_Encode_StartACM)(DWORD handle, const void *form, DWORD flags, ENCODEPROC *proc, void *user);
|
||||
HENCODE BASSENCDEF(BASS_Encode_StartACMFile)(DWORD handle, const void *form, DWORD flags, const char *filename);
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
HENCODE BASSENCDEF(BASS_Encode_StartCA)(DWORD handle, DWORD ftype, DWORD atype, DWORD flags, DWORD bitrate, ENCODEPROCEX *proc, void *user);
|
||||
HENCODE BASSENCDEF(BASS_Encode_StartCAFile)(DWORD handle, DWORD ftype, DWORD atype, DWORD flags, DWORD bitrate, const char *filename);
|
||||
void *BASSENCDEF(BASS_Encode_GetCARef)(DWORD handle);
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32_WCE
|
||||
BOOL BASSENCDEF(BASS_Encode_CastInit)(HENCODE handle, const char *server, const char *pass, const char *content, const char *name, const char *url, const char *genre, const char *desc, const char *headers, DWORD bitrate, BOOL pub);
|
||||
BOOL BASSENCDEF(BASS_Encode_CastSetTitle)(HENCODE handle, const char *title, const char *url);
|
||||
BOOL BASSENCDEF(BASS_Encode_CastSendMeta)(HENCODE handle, DWORD type, const void *data, DWORD length);
|
||||
const char *BASSENCDEF(BASS_Encode_CastGetStats)(HENCODE handle, DWORD type, const char *pass);
|
||||
|
||||
DWORD BASSENCDEF(BASS_Encode_ServerInit)(HENCODE handle, const char *port, DWORD buffer, DWORD burst, DWORD flags, ENCODECLIENTPROC *proc, void *user);
|
||||
BOOL BASSENCDEF(BASS_Encode_ServerKick)(HENCODE handle, const char *client);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static inline HENCODE BASS_Encode_Start(DWORD handle, const WCHAR *cmdline, DWORD flags, ENCODEPROC *proc, void *user)
|
||||
{
|
||||
return BASS_Encode_Start(handle, (const char*)cmdline, flags|BASS_UNICODE, proc, user);
|
||||
}
|
||||
|
||||
static inline HENCODE BASS_Encode_StartLimit(DWORD handle, const WCHAR *cmdline, DWORD flags, ENCODEPROC *proc, void *user, DWORD limit)
|
||||
{
|
||||
return BASS_Encode_StartLimit(handle, (const char *)cmdline, flags|BASS_UNICODE, proc, user, limit);
|
||||
}
|
||||
|
||||
static inline HENCODE BASS_Encode_StartUser(DWORD handle, const WCHAR *filename, DWORD flags, ENCODERPROC *proc, void *user)
|
||||
{
|
||||
return BASS_Encode_StartUser(handle, (const char *)filename, flags|BASS_UNICODE, proc, user);
|
||||
}
|
||||
|
||||
static inline DWORD BASS_Encode_GetACMFormat(DWORD handle, void *form, DWORD formlen, const WCHAR *title, DWORD flags)
|
||||
{
|
||||
return BASS_Encode_GetACMFormat(handle, form, formlen, (const char *)title, flags|BASS_UNICODE);
|
||||
}
|
||||
|
||||
static inline HENCODE BASS_Encode_StartACMFile(DWORD handle, const void *form, DWORD flags, const WCHAR *filename)
|
||||
{
|
||||
return BASS_Encode_StartACMFile(handle, form, flags|BASS_UNICODE, (const char *)filename);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
BASSenc_OPUS 2.4 C/C++ header file
|
||||
Copyright (c) 2016 Un4seen Developments Ltd.
|
||||
|
||||
See the BASSENC_OPUS.CHM file for more detailed documentation
|
||||
*/
|
||||
|
||||
#ifndef BASSENC_OPUS_H
|
||||
#define BASSENC_OPUS_H
|
||||
|
||||
#include "bassenc.h"
|
||||
|
||||
#if BASSVERSION!=0x204
|
||||
#error conflicting BASS and BASSenc_OPUS versions
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef BASSENCOPUSDEF
|
||||
#define BASSENCOPUSDEF(f) WINAPI f
|
||||
#endif
|
||||
|
||||
DWORD BASSENCOPUSDEF(BASS_Encode_OPUS_GetVersion)();
|
||||
|
||||
HENCODE BASSENCOPUSDEF(BASS_Encode_OPUS_Start)(DWORD handle, const char *options, DWORD flags, ENCODEPROC *proc, void *user);
|
||||
HENCODE BASSENCOPUSDEF(BASS_Encode_OPUS_StartFile)(DWORD handle, const char *options, DWORD flags, const char *filename);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static inline HENCODE BASS_Encode_OPUS_Start(DWORD handle, const WCHAR *options, DWORD flags, ENCODEPROC *proc, void *user)
|
||||
{
|
||||
return BASS_Encode_OPUS_Start(handle, (const char*)options, flags|BASS_UNICODE, proc, user);
|
||||
}
|
||||
|
||||
static inline HENCODE BASS_Encode_OPUS_StartFile(DWORD handle, const WCHAR *options, DWORD flags, const WCHAR *filename)
|
||||
{
|
||||
return BASS_Encode_OPUS_StartFile(handle, (const char*)options, flags|BASS_UNICODE, (const char*)filename);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Executable
+166
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* codec2.c ... function to handle voice transfer via codec2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hsmodem.h"
|
||||
|
||||
void sendCodecToModulator(uint8_t* pdata, int len);
|
||||
|
||||
struct CODEC2 *pc2 = NULL;
|
||||
int samplesPerPacket = 160;
|
||||
int bytesPerPacket = 8;
|
||||
|
||||
void init_codec2()
|
||||
{
|
||||
close_codec2();
|
||||
|
||||
if (speedmode == 0)
|
||||
pc2 = codec2_create(CODEC2_MODE_1600);
|
||||
else if(speedmode == 1)
|
||||
pc2 = codec2_create(CODEC2_MODE_2400);
|
||||
else
|
||||
pc2 = codec2_create(CODEC2_MODE_3200);
|
||||
|
||||
if (pc2 == NULL)
|
||||
{
|
||||
printf("cannot create CODEC2\n");
|
||||
}
|
||||
codec2_set_natural_or_gray(pc2, 0);
|
||||
bytesPerPacket = codec2_bits_per_frame(pc2) / 8;
|
||||
samplesPerPacket = codec2_samples_per_frame(pc2);
|
||||
printf("Codec2: BytesPerFrame:%d SamplesPerFrame:%d\n", bytesPerPacket, samplesPerPacket);
|
||||
}
|
||||
|
||||
void close_codec2()
|
||||
{
|
||||
if (pc2 != NULL)
|
||||
{
|
||||
codec2_destroy(pc2);
|
||||
}
|
||||
pc2 = NULL;
|
||||
}
|
||||
|
||||
// encode 160 voice samples (8kS/s) into 64 bits output
|
||||
void encode_codec2(float f)
|
||||
{
|
||||
static int decim = 0;
|
||||
static int16_t sbuf[500]; // this is easily more than "samplesPerPacket" in any cases
|
||||
static int fbuf_idx = 0;
|
||||
uint8_t outbuf[50]; // this is easily more than "bytesPerPacket" in any cases
|
||||
|
||||
if (pc2 == NULL) return;
|
||||
|
||||
// this encoder is called with a sound card sample rate of 48000
|
||||
// codec2 needs 8 kS/s, so we have to decimate by 6
|
||||
if (++decim >= 6)
|
||||
{
|
||||
decim = 0;
|
||||
|
||||
// here we have a sample rate of 8 kS/s
|
||||
// one encoding call needs 160 samples
|
||||
sbuf[fbuf_idx] = (int16_t)(f * 32768); // convert to short
|
||||
if (++fbuf_idx >= samplesPerPacket)
|
||||
{
|
||||
fbuf_idx = 0;
|
||||
|
||||
// we have 160 samples in fbuf, encode them now
|
||||
codec2_encode(pc2, outbuf, sbuf);
|
||||
|
||||
// outbuf is 64bit = 8byte long
|
||||
|
||||
// send Codec data to modulator
|
||||
if (VoiceAudioMode == VOICEMODE_DV_FULLDUPLEX)
|
||||
{
|
||||
memmove(outbuf + 1, outbuf, bytesPerPacket);
|
||||
outbuf[0] = 0xff; // start of codec2 packet marker
|
||||
sendCodecToModulator(outbuf, bytesPerPacket+1);
|
||||
}
|
||||
|
||||
if (VoiceAudioMode == VOICEMODE_CODECLOOP)
|
||||
{
|
||||
// codec loop mode: decode and play it
|
||||
int16_t spbbuf[500];
|
||||
codec2_decode(pc2, spbbuf, outbuf);
|
||||
|
||||
for (int i = 0; i < samplesPerPacket; i++)
|
||||
{
|
||||
float f = (float)spbbuf[i];
|
||||
f /= 32768;
|
||||
// here we have 8kS/s, need to interpolate to 48 kS/s
|
||||
for(int x=0; x<6; x++)
|
||||
pb_write_fifo_voice(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define CHUNKSIZE_VOICE 40
|
||||
|
||||
void toCodecDecoder_codec2(uint8_t* pdata, int len)
|
||||
{
|
||||
static uint8_t chunk[50];
|
||||
|
||||
// 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_VOICE - 1; i++)
|
||||
chunk[i] = chunk[i + 1];
|
||||
chunk[CHUNKSIZE_VOICE - 1] = pdata[vd];
|
||||
|
||||
// an Codec-2 packet has max length of max 8 Byte
|
||||
// in the chunk size of 40 fit minimum 4 chunks
|
||||
// so lets test if 4 chunks are there, by looking for the marker
|
||||
// at distance bytesPerPacket
|
||||
int mfound = 1;
|
||||
for (int m = 0; m < 4; m++)
|
||||
{
|
||||
if (chunk[(bytesPerPacket + 1) * m] != 0xff)
|
||||
{
|
||||
mfound = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mfound)
|
||||
{
|
||||
//showbytestring("OPUS:", chunk + 1, opusPacketSize, opusPacketSize);
|
||||
|
||||
// codec loop mode: decode and play it
|
||||
int16_t spbbuf[500];
|
||||
codec2_decode(pc2, spbbuf, chunk+1);
|
||||
|
||||
for (int i = 0; i < samplesPerPacket; i++)
|
||||
{
|
||||
float f = (float)spbbuf[i];
|
||||
f /= 32768;
|
||||
// here we have 8kS/s, need to interpolate to 48 kS/s
|
||||
for (int x = 0; x < 6; x++)
|
||||
pb_write_fifo_voice(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+122
@@ -0,0 +1,122 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
|
||||
FILE........: codec2.h
|
||||
AUTHOR......: David Rowe
|
||||
DATE CREATED: 21 August 2010
|
||||
|
||||
Codec 2 fully quantised encoder and decoder functions. If you want use
|
||||
Codec 2, these are the functions you need to call.
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
Copyright (C) 2010 David Rowe
|
||||
|
||||
All rights reserved.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License version 2.1, as
|
||||
published by the Free Software Foundation. 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 Lesser General Public License
|
||||
along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __CODEC2__
|
||||
#define __CODEC2__
|
||||
|
||||
#include "version.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define CODEC2_MODE_3200 0
|
||||
#define CODEC2_MODE_2400 1
|
||||
#define CODEC2_MODE_1600 2
|
||||
#define CODEC2_MODE_1400 3
|
||||
#define CODEC2_MODE_1300 4
|
||||
#define CODEC2_MODE_1200 5
|
||||
#define CODEC2_MODE_700C 8
|
||||
#define CODEC2_MODE_450 10
|
||||
#define CODEC2_MODE_450PWB 11
|
||||
|
||||
#ifndef CODEC2_MODE_EN_DEFAULT
|
||||
#define CODEC2_MODE_EN_DEFAULT 1
|
||||
#endif
|
||||
|
||||
// by default we enable all modes
|
||||
// disable during compile time with -DCODEC2_MODE_1600_EN=0
|
||||
// all but CODEC2 1600 are enabled then
|
||||
|
||||
//or the other way round
|
||||
// -DCODEC2_MODE_EN_DEFAULT=0 -DCODEC2_MODE_1600_EN=1
|
||||
// only CODEC2 Mode 1600
|
||||
|
||||
#if !defined(CODEC2_MODE_3200_EN)
|
||||
#define CODEC2_MODE_3200_EN CODEC2_MODE_EN_DEFAULT
|
||||
#endif
|
||||
#if !defined(CODEC2_MODE_2400_EN)
|
||||
#define CODEC2_MODE_2400_EN CODEC2_MODE_EN_DEFAULT
|
||||
#endif
|
||||
#if !defined(CODEC2_MODE_1600_EN)
|
||||
#define CODEC2_MODE_1600_EN CODEC2_MODE_EN_DEFAULT
|
||||
#endif
|
||||
#if !defined(CODEC2_MODE_1400_EN)
|
||||
#define CODEC2_MODE_1400_EN CODEC2_MODE_EN_DEFAULT
|
||||
#endif
|
||||
#if !defined(CODEC2_MODE_1300_EN)
|
||||
#define CODEC2_MODE_1300_EN CODEC2_MODE_EN_DEFAULT
|
||||
#endif
|
||||
#if !defined(CODEC2_MODE_1200_EN)
|
||||
#define CODEC2_MODE_1200_EN CODEC2_MODE_EN_DEFAULT
|
||||
#endif
|
||||
#if !defined(CODEC2_MODE_700C_EN)
|
||||
#define CODEC2_MODE_700C_EN CODEC2_MODE_EN_DEFAULT
|
||||
#endif
|
||||
#if !defined(CODEC2_MODE_450_EN)
|
||||
#define CODEC2_MODE_450_EN CODEC2_MODE_EN_DEFAULT
|
||||
#endif
|
||||
#if !defined(CODEC2_MODE_450PWB_EN)
|
||||
#define CODEC2_MODE_450PWB_EN CODEC2_MODE_EN_DEFAULT
|
||||
#endif
|
||||
|
||||
#define CODEC2_MODE_ACTIVE(mode_name, var) ((mode_name##_EN) == 0 ? 0: (var) == mode_name)
|
||||
|
||||
struct CODEC2;
|
||||
|
||||
struct CODEC2 * codec2_create(int mode);
|
||||
void codec2_destroy(struct CODEC2 *codec2_state);
|
||||
void codec2_encode(struct CODEC2 *codec2_state, unsigned char * bits, short speech_in[]);
|
||||
void codec2_decode(struct CODEC2 *codec2_state, short speech_out[], const unsigned char *bits);
|
||||
void codec2_decode_ber(struct CODEC2 *codec2_state, short speech_out[], const unsigned char *bits, float ber_est);
|
||||
int codec2_samples_per_frame(struct CODEC2 *codec2_state);
|
||||
int codec2_bits_per_frame(struct CODEC2 *codec2_state);
|
||||
|
||||
void codec2_set_lpc_post_filter(struct CODEC2 *codec2_state, int enable, int bass_boost, float beta, float gamma);
|
||||
int codec2_get_spare_bit_index(struct CODEC2 *codec2_state);
|
||||
int codec2_rebuild_spare_bit(struct CODEC2 *codec2_state, char unpacked_bits[]);
|
||||
void codec2_set_natural_or_gray(struct CODEC2 *codec2_state, int gray);
|
||||
void codec2_set_softdec(struct CODEC2 *c2, float *softdec);
|
||||
float codec2_get_energy(struct CODEC2 *codec2_state, const unsigned char *bits);
|
||||
|
||||
// support for ML and VQ experiments
|
||||
void codec2_open_mlfeat(struct CODEC2 *codec2_state, char *feat_filename, char *model_filename);
|
||||
void codec2_load_codebook(struct CODEC2 *codec2_state, int num, char *filename);
|
||||
float codec2_get_var(struct CODEC2 *codec2_state);
|
||||
float *codec2_enable_user_ratek(struct CODEC2 *codec2_state, int *K);
|
||||
|
||||
// 700C post filter and equaliser
|
||||
void codec2_700c_post_filter(struct CODEC2 *codec2_state, int en);
|
||||
void codec2_700c_eq(struct CODEC2 *codec2_state, int en);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
+5
-4
@@ -104,18 +104,19 @@ uint16_t *make_waterfall(float fre, int *retlen)
|
||||
|
||||
void init_fft()
|
||||
{
|
||||
/*
|
||||
char fn[300];
|
||||
* storing to a file in the working directory may be a problem under Windows, so we do not use wisdom files
|
||||
sprintf(fn, "capture_fft_%d", fft_rate); // wisdom file for each capture rate
|
||||
|
||||
sprintf(fn, "capture_fft_%d", fft_rate); // wisdom file for each capture rate
|
||||
|
||||
fftw_import_wisdom_from_filename(fn);
|
||||
fftw_import_wisdom_from_filename(fn);*/
|
||||
|
||||
din = (double *)fftw_malloc(sizeof(double) * fft_rate);
|
||||
cpout = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * fft_rate);
|
||||
|
||||
plan = fftw_plan_dft_r2c_1d(fft_rate, din, cpout, FFTW_MEASURE);
|
||||
|
||||
fftw_export_wisdom_to_filename(fn);
|
||||
//fftw_export_wisdom_to_filename(fn);
|
||||
}
|
||||
|
||||
void exit_fft()
|
||||
|
||||
@@ -254,14 +254,12 @@ uint8_t *unpack_data(uint8_t *rxd, int len)
|
||||
{
|
||||
memcpy(payload,pl, PAYLOADLEN+10);
|
||||
framerdy = 1;
|
||||
if(symnum != 688)
|
||||
printf("Header found, rotation: %d at symbol no.: %d result: OK\n", rotations, symnum);
|
||||
//if(symnum != 688) printf("Header found, rotation: %d at symbol no.: %d result: OK\n", rotations, symnum);
|
||||
symnum = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if((symnum % ((UDPBLOCKLEN * 8) / bitsPerSymbol)) == 0)
|
||||
printf("Header found, rotation: %d at symbol no.: %d result: %d\n", rotations, symnum, getPayload_error);
|
||||
//if((symnum % ((UDPBLOCKLEN * 8) / bitsPerSymbol)) == 0) printf("Header found, rotation: %d at symbol no.: %d result: %d\n", rotations, symnum, getPayload_error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+126
-39
@@ -73,7 +73,7 @@ int keeprunning = 1;
|
||||
// UDP I/O
|
||||
int BC_sock_AppToModem = -1;
|
||||
int DATA_sock_AppToModem = -1;
|
||||
int DATA_sock_from_GR = -1;
|
||||
//int DATA_sock_from_GR = -1;
|
||||
int DATA_sock_FFT_from_GR = -1;
|
||||
int DATA_sock_I_Q_from_GR = -1;
|
||||
|
||||
@@ -81,17 +81,11 @@ int UdpBCport_AppToModem = 40131;
|
||||
int UdpDataPort_AppToModem = 40132;
|
||||
int UdpDataPort_ModemToApp = 40133;
|
||||
|
||||
int UdpDataPort_toGR = 40134;
|
||||
int UdpDataPort_fromGR = 40135;
|
||||
int UdpDataPort_fromGR_FFT = 40136;
|
||||
int UdpDataPort_fromGR_I_Q = 40137;
|
||||
|
||||
// op mode depending values
|
||||
// default mode if not set by the app
|
||||
int speedmode = 2;
|
||||
int bitsPerSymbol = 2; // QPSK=2, 8PSK=3
|
||||
int constellationSize = 4; // QPSK=4, 8PSK=8
|
||||
int psk8mode=0; // 0=APSK8, 1=PSK8
|
||||
|
||||
char localIP[] = { "127.0.0.1" };
|
||||
char ownfilename[] = { "hsmodem" };
|
||||
@@ -106,9 +100,16 @@ int linespeed = 4410;
|
||||
|
||||
int captureDeviceNo = -1;
|
||||
int playbackDeviceNo = -1;
|
||||
int MicDeviceNo = -1;
|
||||
int LSDeviceNo = -1;
|
||||
int initialPBvol = -1;
|
||||
int initialCAPvol = -1;
|
||||
int announcement = 0;
|
||||
int VoiceAudioMode = VOICEMODE_OFF;
|
||||
int codec = 1; // 0=opus, 1=codec2
|
||||
|
||||
int init_audio_result = 0;
|
||||
int init_voice_result = 0;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
@@ -169,17 +170,19 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
init_packer();
|
||||
|
||||
initFEC();
|
||||
init_fft();
|
||||
int ar = init_audio(playbackDeviceNo, captureDeviceNo);
|
||||
init_voiceproc();
|
||||
|
||||
/*int ar = init_audio(playbackDeviceNo, captureDeviceNo);
|
||||
if (ar == -1)
|
||||
{
|
||||
keeprunning = 0;
|
||||
exit(0);
|
||||
}
|
||||
}*/
|
||||
|
||||
// start udp RX to listen for broadcast search message from Application
|
||||
UdpRxInit(&BC_sock_AppToModem, UdpBCport_AppToModem, &bc_rxdata, &keeprunning);
|
||||
@@ -188,7 +191,7 @@ int main(int argc, char* argv[])
|
||||
UdpRxInit(&DATA_sock_AppToModem, UdpDataPort_AppToModem, &appdata_rxdata, &keeprunning);
|
||||
|
||||
// start udp RX to listen for data from GR Receiver
|
||||
UdpRxInit(&DATA_sock_from_GR, UdpDataPort_fromGR, &GRdata_rxdata, &keeprunning);
|
||||
//UdpRxInit(&DATA_sock_from_GR, UdpDataPort_fromGR, &GRdata_rxdata, &keeprunning);
|
||||
|
||||
printf("QO100modem initialised and running\n");
|
||||
|
||||
@@ -201,9 +204,46 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
|
||||
//doArraySend();
|
||||
if (VoiceAudioMode == VOICEMODE_INTERNALLOOP)
|
||||
{
|
||||
// loop voice mic to LS
|
||||
float f;
|
||||
if (cap_read_fifo_voice(&f))
|
||||
{
|
||||
if(softwareCAPvolume_voice >= 0)
|
||||
f *= softwareCAPvolume_voice;
|
||||
pb_write_fifo_voice(f);
|
||||
}
|
||||
}
|
||||
|
||||
if (demodulator() == 0)
|
||||
sleep_ms(10);
|
||||
if (VoiceAudioMode == VOICEMODE_CODECLOOP || VoiceAudioMode == VOICEMODE_DV_FULLDUPLEX)
|
||||
{
|
||||
// send mic to codec
|
||||
float f;
|
||||
if (cap_read_fifo_voice(&f))
|
||||
{
|
||||
if (softwareCAPvolume_voice >= 0)
|
||||
f *= softwareCAPvolume_voice;
|
||||
encode(f);
|
||||
}
|
||||
}
|
||||
|
||||
// demodulate incoming audio data stream
|
||||
static int old_tm = 0;
|
||||
int tm = getus();
|
||||
if (tm >= (old_tm + 1000000))
|
||||
{
|
||||
// read Audio device list every 1s
|
||||
readAudioDevices();
|
||||
old_tm = tm;
|
||||
}
|
||||
|
||||
int dret = demodulator();
|
||||
|
||||
#ifdef _LINUX_
|
||||
if(dret == 0)
|
||||
usleep(1);
|
||||
#endif
|
||||
}
|
||||
printf("stopped: %d\n", keeprunning);
|
||||
|
||||
@@ -225,32 +265,26 @@ typedef struct {
|
||||
int rx;
|
||||
int bpsym;
|
||||
int linespeed;
|
||||
int codecrate;
|
||||
} SPEEDRATE;
|
||||
|
||||
// AudioRate, TX-Resampler, RX-Resampler/4, bit/symbol, Codec-Rate
|
||||
SPEEDRATE sr[8] = {
|
||||
// QPSK modes
|
||||
{48000, 32, 8, 2, 3000}, // AudioRate, TX-Resampler, RX-Resampler/4, bit/symbol, see samprate.ods
|
||||
{48000, 24, 6, 2, 4000},
|
||||
{44100, 20, 5, 2, 4410},
|
||||
{48000, 20, 5, 2, 4800},
|
||||
{48000, 32, 8, 2, 3000, 2400},
|
||||
{48000, 24, 6, 2, 4000, 3200},
|
||||
{44100, 20, 5, 2, 4410, 3600},
|
||||
{48000, 20, 5, 2, 4800, 4000},
|
||||
|
||||
// 8PSK modes
|
||||
{44100, 24, 6, 3, 5500},
|
||||
{48000, 24, 6, 3, 6000},
|
||||
{44100, 20, 5, 3, 6600},
|
||||
{48000, 20, 5, 3, 7200}
|
||||
{44100, 24, 6, 3, 5500, 4400},
|
||||
{48000, 24, 6, 3, 6000, 4800},
|
||||
{44100, 20, 5, 3, 6600, 5200},
|
||||
{48000, 20, 5, 3, 7200, 6000}
|
||||
};
|
||||
|
||||
void startModem()
|
||||
{
|
||||
if (speedmode >= 8)
|
||||
{
|
||||
speedmode = speedmode - 4;
|
||||
psk8mode = 1;
|
||||
}
|
||||
else
|
||||
psk8mode = 0;
|
||||
|
||||
bitsPerSymbol = sr[speedmode].bpsym;
|
||||
constellationSize = (1 << bitsPerSymbol); // QPSK=4, 8PSK=8
|
||||
|
||||
@@ -258,16 +292,17 @@ void startModem()
|
||||
txinterpolfactor = sr[speedmode].tx;
|
||||
rxPreInterpolfactor = sr[speedmode].rx;
|
||||
linespeed = sr[speedmode].linespeed;
|
||||
opusbitrate = sr[speedmode].codecrate;
|
||||
|
||||
// int TX audio and modulator
|
||||
close_dsp();
|
||||
init_audio(playbackDeviceNo, captureDeviceNo);
|
||||
init_audio_result = init_audio(playbackDeviceNo, captureDeviceNo);
|
||||
setPBvolume(initialPBvol);
|
||||
setCAPvolume(initialCAPvol);
|
||||
init_dsp();
|
||||
}
|
||||
|
||||
void setAudioDevices(int pb, int cap, int pbvol, int capvol, int announce)
|
||||
void setAudioDevices(int pb, int cap, int pbvol, int capvol, int announce, int pbls, int pbmic)
|
||||
{
|
||||
//printf("%d %d\n", pb, cap);
|
||||
|
||||
@@ -278,6 +313,8 @@ void setAudioDevices(int pb, int cap, int pbvol, int capvol, int announce)
|
||||
captureDeviceNo = cap;
|
||||
initialPBvol = pbvol;
|
||||
initialCAPvol = capvol;
|
||||
initialLSvol = pbls;
|
||||
initialMICvol = pbmic;
|
||||
}
|
||||
|
||||
announcement = announce;
|
||||
@@ -288,7 +325,7 @@ void bc_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
|
||||
{
|
||||
if (len > 0 && pdata[0] == 0x3c)
|
||||
{
|
||||
setAudioDevices(pdata[1], pdata[2], pdata[3], pdata[4], pdata[5]);
|
||||
setAudioDevices(pdata[1], pdata[2], pdata[3], pdata[4], pdata[5], pdata[6], pdata[7]);
|
||||
|
||||
char rxip[20];
|
||||
strcpy(rxip, inet_ntoa(rxsock->sin_addr));
|
||||
@@ -335,6 +372,11 @@ void appdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
|
||||
if (type == 16)
|
||||
{
|
||||
// Byte 1 contains the resampler ratio for TX and RX modem
|
||||
if (pdata[1] >= 8)
|
||||
{
|
||||
printf("wrong speedmode %d, ignoring\n", pdata[1]);
|
||||
return;
|
||||
}
|
||||
speedmode = pdata[1];
|
||||
printf("set speedmode to %d\n", speedmode);
|
||||
restart_modems = 1;
|
||||
@@ -394,6 +436,42 @@ void appdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == 23)
|
||||
{
|
||||
// set playback volume (in % 0..100)
|
||||
setVolume_voice(0, minfo);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == 24)
|
||||
{
|
||||
// set capture volume (in % 0..100)
|
||||
setVolume_voice(1, minfo);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == 25)
|
||||
{
|
||||
//printf("%d %d %d %d %d\n", pdata[0], pdata[1], pdata[2], pdata[3], pdata[4]);
|
||||
LSDeviceNo = pdata[1];
|
||||
MicDeviceNo = pdata[2];
|
||||
VoiceAudioMode = pdata[3];
|
||||
codec = pdata[4];
|
||||
|
||||
// init voice audio
|
||||
init_voice_result = init_audio_voice(LSDeviceNo, MicDeviceNo);
|
||||
init_voiceproc();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == 26)
|
||||
{
|
||||
// GUI requests termination of this hsmodem
|
||||
printf("shut down hsmodem\n");
|
||||
closeAllandTerminate();
|
||||
}
|
||||
|
||||
if (len != (PAYLOADLEN + 2))
|
||||
{
|
||||
printf("data from app: wrong length:%d (should be %d)\n", len - 2, PAYLOADLEN);
|
||||
@@ -451,12 +529,21 @@ void GRdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
|
||||
uint8_t* pl = unpack_data(pdata, len);
|
||||
if (pl != NULL)
|
||||
{
|
||||
// complete frame received
|
||||
// send payload to app
|
||||
uint8_t txpl[PAYLOADLEN + 10 + 1];
|
||||
memcpy(txpl + 1, pl, PAYLOADLEN + 10);
|
||||
txpl[0] = 1; // type 1: payload data follows
|
||||
sendUDP(appIP, UdpDataPort_ModemToApp, txpl, PAYLOADLEN + 10 + 1);
|
||||
if (VoiceAudioMode != VOICEMODE_DV_FULLDUPLEX)
|
||||
{
|
||||
// complete frame received
|
||||
// send payload to app
|
||||
uint8_t txpl[PAYLOADLEN + 10 + 1];
|
||||
memcpy(txpl + 1, pl, PAYLOADLEN + 10);
|
||||
txpl[0] = 1; // type 1: payload data follows
|
||||
sendUDP(appIP, UdpDataPort_ModemToApp, txpl, PAYLOADLEN + 10 + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// send to Codec decoder
|
||||
if (*(pl + 3) != 0) // minfo=0 ... just a filler, ignore
|
||||
toCodecDecoder(pl + 10, PAYLOADLEN);
|
||||
}
|
||||
fnd = 0;
|
||||
}
|
||||
else
|
||||
@@ -464,7 +551,7 @@ void GRdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
|
||||
// no frame found
|
||||
// if longer ws seconds nothing found, reset liquid RX modem
|
||||
// comes here with symbol rate, i.e. 4000 S/s
|
||||
int ws = 4;
|
||||
int ws = 4;
|
||||
int wt = sr[speedmode].audio / sr[speedmode].tx;
|
||||
if (++fnd >= (wt * ws))
|
||||
{
|
||||
|
||||
+67
-2
@@ -30,6 +30,7 @@
|
||||
#include <Tlhelp32.h>
|
||||
#include <winbase.h>
|
||||
#include <Shlobj.h>
|
||||
#include "opus.h"
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
|
||||
@@ -37,6 +38,8 @@
|
||||
#pragma comment(lib, "basswasapi.lib")
|
||||
#pragma comment(lib, "libliquid.lib")
|
||||
#pragma comment(lib, "fftw_lib/libfftw3-3.lib")
|
||||
#pragma comment(lib, "opus.lib")
|
||||
#pragma comment(lib, "libcodec2.lib")
|
||||
#endif
|
||||
|
||||
#ifdef _LINUX_
|
||||
@@ -52,6 +55,7 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <pwd.h>
|
||||
#include <math.h>
|
||||
#include <opus/opus.h>
|
||||
#endif
|
||||
|
||||
#include "bass.h"
|
||||
@@ -62,6 +66,8 @@
|
||||
#include "fec.h"
|
||||
#include "udp.h"
|
||||
#include "symboltracker.h"
|
||||
#include "bassenc_opus.h"
|
||||
#include "codec2.h"
|
||||
|
||||
#define jpg_tempfilename "rxdata.jpg"
|
||||
|
||||
@@ -73,6 +79,18 @@
|
||||
#define MAXDEVSTRLEN 2000
|
||||
#define CHANNELS 1 // no of channels used
|
||||
|
||||
// voice audio sampling rate
|
||||
#define VOICE_SAMPRATE 48000 // do NOT change, OPUS works with 48k only
|
||||
|
||||
enum _VOICEMODES_ {
|
||||
VOICEMODE_OFF,
|
||||
VOICEMODE_LISTENAUDIOIN,
|
||||
VOICEMODE_INTERNALLOOP,
|
||||
VOICEMODE_CODECLOOP,
|
||||
VOICEMODE_DV_FULLDUPLEX,
|
||||
VOICEMODE_DV_RXONLY
|
||||
};
|
||||
|
||||
void init_packer();
|
||||
uint8_t* Pack(uint8_t* payload, int type, int status, int* plen);
|
||||
uint8_t* unpack_data(uint8_t* rxd, int len);
|
||||
@@ -96,7 +114,7 @@ void TX_Scramble(uint8_t* data, int len);
|
||||
uint8_t* RX_Scramble(uint8_t* data, int len);
|
||||
uint16_t Crc16_messagecalc(int rxtx, uint8_t* data, int len);
|
||||
|
||||
void showbytestring(char* title, uint8_t* data, int anz);
|
||||
void showbytestring(char* title, uint8_t* data, int totallen, int anz);
|
||||
void measure_speed_syms(int len);
|
||||
void measure_speed_bps(int len);
|
||||
|
||||
@@ -106,6 +124,7 @@ int cfec_Reconstruct(uint8_t* darr, uint8_t* destination);
|
||||
|
||||
int init_audio(int pbdev, int capdev);
|
||||
int pb_fifo_freespace(int nolock);
|
||||
int pb_fifo_usedspace();
|
||||
void pb_write_fifo_clear();
|
||||
void pb_write_fifo(float sample);
|
||||
int cap_read_fifo(float* data);
|
||||
@@ -113,11 +132,17 @@ uint8_t* getAudioDevicelist(int* len);
|
||||
void setPBvolume(int v);
|
||||
void setCAPvolume(int v);
|
||||
void setVolume(int pbcap, int v);
|
||||
void setVolume_voice(int pbcap, int v);
|
||||
int init_wasapi(int pbdev, int capdev);
|
||||
void sendAnnouncement();
|
||||
void readAudioDevices();
|
||||
void clear_audio_fifos();
|
||||
void clear_voice_fifos();
|
||||
|
||||
void sleep_ms(int ms);
|
||||
int getus();
|
||||
void GRdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock);
|
||||
void toGR_sendData(uint8_t* data, int type, int status);
|
||||
|
||||
void modulator(uint8_t sym_in);
|
||||
int pb_fifo_usedBlocks();
|
||||
@@ -131,6 +156,30 @@ void exit_fft();
|
||||
void showbytestringf(char* title, float* data, int anz);
|
||||
uint16_t* make_waterfall(float fre, int* retlen);
|
||||
|
||||
int init_audio_voice(int setpbdev, int setcapdev);
|
||||
void pb_write_fifo_voice(float sample);
|
||||
int cap_read_fifo_voice(float* data);
|
||||
void toVoice(float sample);
|
||||
void toCodecDecoder(uint8_t* pdata, int len);
|
||||
|
||||
void init_voiceproc();
|
||||
void encode(float f);
|
||||
|
||||
int init_wasapi_voice(int pbdev, int capdev);
|
||||
void init_codec2();
|
||||
void encode_codec2(float f);
|
||||
void toCodecDecoder_codec2(uint8_t* pdata, int len);
|
||||
void close_wasapi();
|
||||
void close_wasapi_voice();
|
||||
|
||||
void closeAllandTerminate();
|
||||
void close_voiceproc();
|
||||
void close_codec2();
|
||||
void close_audio();
|
||||
void close_audio_voice();
|
||||
int cap_fifo_usedPercent();
|
||||
|
||||
|
||||
void km_symtrack_cccf_create(int _ftype,
|
||||
unsigned int _k,
|
||||
unsigned int _m,
|
||||
@@ -147,17 +196,33 @@ extern int speed;
|
||||
extern int keeprunning;
|
||||
extern int caprate;
|
||||
extern int BC_sock_AppToModem;
|
||||
extern int DATA_sock_AppToModem;
|
||||
extern int UdpDataPort_ModemToApp;
|
||||
extern int txinterpolfactor;
|
||||
extern int rxPreInterpolfactor;
|
||||
extern char appIP[20];
|
||||
extern float softwareCAPvolume;
|
||||
extern float softwareCAPvolume_voice;
|
||||
extern int announcement;
|
||||
extern int ann_running;
|
||||
extern int transmissions;
|
||||
extern int linespeed;
|
||||
extern uint8_t maxLevel;
|
||||
extern int psk8mode;
|
||||
extern int VoiceAudioMode;
|
||||
extern int opusbitrate;
|
||||
extern int init_audio_result;
|
||||
extern int init_voice_result;
|
||||
extern int initialLSvol;
|
||||
extern int initialMICvol;
|
||||
extern int codec;
|
||||
|
||||
|
||||
// audio device description table
|
||||
typedef struct {
|
||||
int bassdev; // bass (basswasapi) dev no
|
||||
char name[256]; // DEV name
|
||||
char id[256];
|
||||
} AUDIODEVS;
|
||||
|
||||
#ifdef _LINUX_
|
||||
int isRunning(char* prgname);
|
||||
|
||||
+11
-1
@@ -162,6 +162,7 @@
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>wsock32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalOptions>/NODEFAULTLIB:libcmt.lib %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
@@ -220,20 +221,28 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="bass.h" />
|
||||
<ClInclude Include="bassflac.h" />
|
||||
<ClInclude Include="bassenc.h" />
|
||||
<ClInclude Include="bassenc_opus.h" />
|
||||
<ClInclude Include="basswasapi.h" />
|
||||
<ClInclude Include="codec2.h" />
|
||||
<ClInclude Include="fec.h" />
|
||||
<ClInclude Include="fftw3.h" />
|
||||
<ClInclude Include="fftw_lib\fftw3.h" />
|
||||
<ClInclude Include="frameformat.h" />
|
||||
<ClInclude Include="hsmodem.h" />
|
||||
<ClInclude Include="liquid.h" />
|
||||
<ClInclude Include="opus.h" />
|
||||
<ClInclude Include="opus_defines.h" />
|
||||
<ClInclude Include="opus_types.h" />
|
||||
<ClInclude Include="symboltracker.h" />
|
||||
<ClInclude Include="udp.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="audio.cpp" />
|
||||
<ClCompile Include="audio_voice.cpp" />
|
||||
<ClCompile Include="audio_voice_wasapi.cpp" />
|
||||
<ClCompile Include="audio_wasapi.cpp" />
|
||||
<ClCompile Include="codec2.cpp" />
|
||||
<ClCompile Include="constellation.cpp" />
|
||||
<ClCompile Include="crc16.cpp" />
|
||||
<ClCompile Include="fec.cpp" />
|
||||
@@ -246,6 +255,7 @@
|
||||
<ClCompile Include="speed.cpp" />
|
||||
<ClCompile Include="symboltracker.cpp" />
|
||||
<ClCompile Include="udp.cpp" />
|
||||
<ClCompile Include="voiceprocessor.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
||||
@@ -57,6 +57,18 @@
|
||||
<ClCompile Include="symboltracker.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="audio_voice.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="voiceprocessor.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="audio_voice_wasapi.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="codec2.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="hsmodem.h">
|
||||
@@ -86,10 +98,25 @@
|
||||
<ClInclude Include="fec.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="bassflac.h">
|
||||
<ClInclude Include="symboltracker.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="symboltracker.h">
|
||||
<ClInclude Include="bassenc_opus.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="bassenc.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="opus.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="opus_defines.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="opus_types.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="codec2.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
|
||||
Executable
BIN
Binary file not shown.
@@ -53,10 +53,7 @@ modulation_scheme getMod()
|
||||
return LIQUID_MODEM_QPSK;
|
||||
else
|
||||
{
|
||||
if(psk8mode == 0)
|
||||
return LIQUID_MODEM_APSK8;
|
||||
else
|
||||
return LIQUID_MODEM_PSK8;
|
||||
return LIQUID_MODEM_APSK8;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,10 +278,15 @@ void make_FFTdata(float f)
|
||||
|
||||
int bidx = 0;
|
||||
txpl[bidx++] = 4; // type 4: FFT data follows
|
||||
|
||||
int us = pb_fifo_usedBlocks();
|
||||
if (us > 255 || ann_running == 1) us = 255;
|
||||
txpl[bidx++] = us; // usage of TX fifo
|
||||
|
||||
us = cap_fifo_usedPercent();
|
||||
if (us > 255) us = 255;
|
||||
txpl[bidx++] = us; // usage of TX fifo
|
||||
|
||||
for (int i = 0; i < fftlen; i++)
|
||||
{
|
||||
txpl[bidx++] = fft[i] >> 8;
|
||||
|
||||
+29
-2
@@ -69,9 +69,36 @@ void install_signal_handler()
|
||||
}
|
||||
#endif // _LINUX_
|
||||
|
||||
void showbytestring(char *title, uint8_t *data, int anz)
|
||||
void closeAllandTerminate()
|
||||
{
|
||||
printf("%s. Len %d: ",title,anz);
|
||||
// terminate all Threads
|
||||
keeprunning = 0;
|
||||
// close audio
|
||||
#ifdef _LINUX_
|
||||
close_audio();
|
||||
close_audio_voice();
|
||||
#endif
|
||||
#ifdef _WIN32_
|
||||
close_wasapi();
|
||||
close_wasapi_voice();
|
||||
#endif
|
||||
// close fft
|
||||
exit_fft();
|
||||
// close codec2 and opus
|
||||
close_codec2();
|
||||
close_voiceproc();
|
||||
// close liquid-SDR
|
||||
close_dsp();
|
||||
// close network sockets
|
||||
close(BC_sock_AppToModem);
|
||||
close(DATA_sock_AppToModem);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void showbytestring(char *title, uint8_t *data, int totallen, int anz)
|
||||
{
|
||||
printf("%s. Len %d: ",title, totallen);
|
||||
for(int i=0; i<anz; i++)
|
||||
printf("%02X ",data[i]);
|
||||
printf("\n");
|
||||
|
||||
Executable
+981
@@ -0,0 +1,981 @@
|
||||
/* Copyright (c) 2010-2011 Xiph.Org Foundation, Skype Limited
|
||||
Written by Jean-Marc Valin and Koen Vos */
|
||||
/*
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file opus.h
|
||||
* @brief Opus reference implementation API
|
||||
*/
|
||||
|
||||
#ifndef OPUS_H
|
||||
#define OPUS_H
|
||||
|
||||
#include "opus_types.h"
|
||||
#include "opus_defines.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @mainpage Opus
|
||||
*
|
||||
* The Opus codec is designed for interactive speech and audio transmission over the Internet.
|
||||
* It is designed by the IETF Codec Working Group and incorporates technology from
|
||||
* Skype's SILK codec and Xiph.Org's CELT codec.
|
||||
*
|
||||
* The Opus codec is designed to handle a wide range of interactive audio applications,
|
||||
* including Voice over IP, videoconferencing, in-game chat, and even remote live music
|
||||
* performances. It can scale from low bit-rate narrowband speech to very high quality
|
||||
* stereo music. Its main features are:
|
||||
|
||||
* @li Sampling rates from 8 to 48 kHz
|
||||
* @li Bit-rates from 6 kb/s to 510 kb/s
|
||||
* @li Support for both constant bit-rate (CBR) and variable bit-rate (VBR)
|
||||
* @li Audio bandwidth from narrowband to full-band
|
||||
* @li Support for speech and music
|
||||
* @li Support for mono and stereo
|
||||
* @li Support for multichannel (up to 255 channels)
|
||||
* @li Frame sizes from 2.5 ms to 60 ms
|
||||
* @li Good loss robustness and packet loss concealment (PLC)
|
||||
* @li Floating point and fixed-point implementation
|
||||
*
|
||||
* Documentation sections:
|
||||
* @li @ref opus_encoder
|
||||
* @li @ref opus_decoder
|
||||
* @li @ref opus_repacketizer
|
||||
* @li @ref opus_multistream
|
||||
* @li @ref opus_libinfo
|
||||
* @li @ref opus_custom
|
||||
*/
|
||||
|
||||
/** @defgroup opus_encoder Opus Encoder
|
||||
* @{
|
||||
*
|
||||
* @brief This page describes the process and functions used to encode Opus.
|
||||
*
|
||||
* Since Opus is a stateful codec, the encoding process starts with creating an encoder
|
||||
* state. This can be done with:
|
||||
*
|
||||
* @code
|
||||
* int error;
|
||||
* OpusEncoder *enc;
|
||||
* enc = opus_encoder_create(Fs, channels, application, &error);
|
||||
* @endcode
|
||||
*
|
||||
* From this point, @c enc can be used for encoding an audio stream. An encoder state
|
||||
* @b must @b not be used for more than one stream at the same time. Similarly, the encoder
|
||||
* state @b must @b not be re-initialized for each frame.
|
||||
*
|
||||
* While opus_encoder_create() allocates memory for the state, it's also possible
|
||||
* to initialize pre-allocated memory:
|
||||
*
|
||||
* @code
|
||||
* int size;
|
||||
* int error;
|
||||
* OpusEncoder *enc;
|
||||
* size = opus_encoder_get_size(channels);
|
||||
* enc = malloc(size);
|
||||
* error = opus_encoder_init(enc, Fs, channels, application);
|
||||
* @endcode
|
||||
*
|
||||
* where opus_encoder_get_size() returns the required size for the encoder state. Note that
|
||||
* future versions of this code may change the size, so no assuptions should be made about it.
|
||||
*
|
||||
* The encoder state is always continuous in memory and only a shallow copy is sufficient
|
||||
* to copy it (e.g. memcpy())
|
||||
*
|
||||
* It is possible to change some of the encoder's settings using the opus_encoder_ctl()
|
||||
* interface. All these settings already default to the recommended value, so they should
|
||||
* only be changed when necessary. The most common settings one may want to change are:
|
||||
*
|
||||
* @code
|
||||
* opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate));
|
||||
* opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(complexity));
|
||||
* opus_encoder_ctl(enc, OPUS_SET_SIGNAL(signal_type));
|
||||
* @endcode
|
||||
*
|
||||
* where
|
||||
*
|
||||
* @arg bitrate is in bits per second (b/s)
|
||||
* @arg complexity is a value from 1 to 10, where 1 is the lowest complexity and 10 is the highest
|
||||
* @arg signal_type is either OPUS_AUTO (default), OPUS_SIGNAL_VOICE, or OPUS_SIGNAL_MUSIC
|
||||
*
|
||||
* See @ref opus_encoderctls and @ref opus_genericctls for a complete list of parameters that can be set or queried. Most parameters can be set or changed at any time during a stream.
|
||||
*
|
||||
* To encode a frame, opus_encode() or opus_encode_float() must be called with exactly one frame (2.5, 5, 10, 20, 40 or 60 ms) of audio data:
|
||||
* @code
|
||||
* len = opus_encode(enc, audio_frame, frame_size, packet, max_packet);
|
||||
* @endcode
|
||||
*
|
||||
* where
|
||||
* <ul>
|
||||
* <li>audio_frame is the audio data in opus_int16 (or float for opus_encode_float())</li>
|
||||
* <li>frame_size is the duration of the frame in samples (per channel)</li>
|
||||
* <li>packet is the byte array to which the compressed data is written</li>
|
||||
* <li>max_packet is the maximum number of bytes that can be written in the packet (4000 bytes is recommended).
|
||||
* Do not use max_packet to control VBR target bitrate, instead use the #OPUS_SET_BITRATE CTL.</li>
|
||||
* </ul>
|
||||
*
|
||||
* opus_encode() and opus_encode_float() return the number of bytes actually written to the packet.
|
||||
* The return value <b>can be negative</b>, which indicates that an error has occurred. If the return value
|
||||
* is 2 bytes or less, then the packet does not need to be transmitted (DTX).
|
||||
*
|
||||
* Once the encoder state if no longer needed, it can be destroyed with
|
||||
*
|
||||
* @code
|
||||
* opus_encoder_destroy(enc);
|
||||
* @endcode
|
||||
*
|
||||
* If the encoder was created with opus_encoder_init() rather than opus_encoder_create(),
|
||||
* then no action is required aside from potentially freeing the memory that was manually
|
||||
* allocated for it (calling free(enc) for the example above)
|
||||
*
|
||||
*/
|
||||
|
||||
/** Opus encoder state.
|
||||
* This contains the complete state of an Opus encoder.
|
||||
* It is position independent and can be freely copied.
|
||||
* @see opus_encoder_create,opus_encoder_init
|
||||
*/
|
||||
typedef struct OpusEncoder OpusEncoder;
|
||||
|
||||
/** Gets the size of an <code>OpusEncoder</code> structure.
|
||||
* @param[in] channels <tt>int</tt>: Number of channels.
|
||||
* This must be 1 or 2.
|
||||
* @returns The size in bytes.
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_encoder_get_size(int channels);
|
||||
|
||||
/**
|
||||
*/
|
||||
|
||||
/** Allocates and initializes an encoder state.
|
||||
* There are three coding modes:
|
||||
*
|
||||
* @ref OPUS_APPLICATION_VOIP gives best quality at a given bitrate for voice
|
||||
* signals. It enhances the input signal by high-pass filtering and
|
||||
* emphasizing formants and harmonics. Optionally it includes in-band
|
||||
* forward error correction to protect against packet loss. Use this
|
||||
* mode for typical VoIP applications. Because of the enhancement,
|
||||
* even at high bitrates the output may sound different from the input.
|
||||
*
|
||||
* @ref OPUS_APPLICATION_AUDIO gives best quality at a given bitrate for most
|
||||
* non-voice signals like music. Use this mode for music and mixed
|
||||
* (music/voice) content, broadcast, and applications requiring less
|
||||
* than 15 ms of coding delay.
|
||||
*
|
||||
* @ref OPUS_APPLICATION_RESTRICTED_LOWDELAY configures low-delay mode that
|
||||
* disables the speech-optimized mode in exchange for slightly reduced delay.
|
||||
* This mode can only be set on an newly initialized or freshly reset encoder
|
||||
* because it changes the codec delay.
|
||||
*
|
||||
* This is useful when the caller knows that the speech-optimized modes will not be needed (use with caution).
|
||||
* @param [in] Fs <tt>opus_int32</tt>: Sampling rate of input signal (Hz)
|
||||
* This must be one of 8000, 12000, 16000,
|
||||
* 24000, or 48000.
|
||||
* @param [in] channels <tt>int</tt>: Number of channels (1 or 2) in input signal
|
||||
* @param [in] application <tt>int</tt>: Coding mode (@ref OPUS_APPLICATION_VOIP/@ref OPUS_APPLICATION_AUDIO/@ref OPUS_APPLICATION_RESTRICTED_LOWDELAY)
|
||||
* @param [out] error <tt>int*</tt>: @ref opus_errorcodes
|
||||
* @note Regardless of the sampling rate and number channels selected, the Opus encoder
|
||||
* can switch to a lower audio bandwidth or number of channels if the bitrate
|
||||
* selected is too low. This also means that it is safe to always use 48 kHz stereo input
|
||||
* and let the encoder optimize the encoding.
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusEncoder *opus_encoder_create(
|
||||
opus_int32 Fs,
|
||||
int channels,
|
||||
int application,
|
||||
int *error
|
||||
);
|
||||
|
||||
/** Initializes a previously allocated encoder state
|
||||
* The memory pointed to by st must be at least the size returned by opus_encoder_get_size().
|
||||
* This is intended for applications which use their own allocator instead of malloc.
|
||||
* @see opus_encoder_create(),opus_encoder_get_size()
|
||||
* To reset a previously initialized state, use the #OPUS_RESET_STATE CTL.
|
||||
* @param [in] st <tt>OpusEncoder*</tt>: Encoder state
|
||||
* @param [in] Fs <tt>opus_int32</tt>: Sampling rate of input signal (Hz)
|
||||
* This must be one of 8000, 12000, 16000,
|
||||
* 24000, or 48000.
|
||||
* @param [in] channels <tt>int</tt>: Number of channels (1 or 2) in input signal
|
||||
* @param [in] application <tt>int</tt>: Coding mode (OPUS_APPLICATION_VOIP/OPUS_APPLICATION_AUDIO/OPUS_APPLICATION_RESTRICTED_LOWDELAY)
|
||||
* @retval #OPUS_OK Success or @ref opus_errorcodes
|
||||
*/
|
||||
OPUS_EXPORT int opus_encoder_init(
|
||||
OpusEncoder *st,
|
||||
opus_int32 Fs,
|
||||
int channels,
|
||||
int application
|
||||
) OPUS_ARG_NONNULL(1);
|
||||
|
||||
/** Encodes an Opus frame.
|
||||
* @param [in] st <tt>OpusEncoder*</tt>: Encoder state
|
||||
* @param [in] pcm <tt>opus_int16*</tt>: Input signal (interleaved if 2 channels). length is frame_size*channels*sizeof(opus_int16)
|
||||
* @param [in] frame_size <tt>int</tt>: Number of samples per channel in the
|
||||
* input signal.
|
||||
* This must be an Opus frame size for
|
||||
* the encoder's sampling rate.
|
||||
* For example, at 48 kHz the permitted
|
||||
* values are 120, 240, 480, 960, 1920,
|
||||
* and 2880.
|
||||
* Passing in a duration of less than
|
||||
* 10 ms (480 samples at 48 kHz) will
|
||||
* prevent the encoder from using the LPC
|
||||
* or hybrid modes.
|
||||
* @param [out] data <tt>unsigned char*</tt>: Output payload.
|
||||
* This must contain storage for at
|
||||
* least \a max_data_bytes.
|
||||
* @param [in] max_data_bytes <tt>opus_int32</tt>: Size of the allocated
|
||||
* memory for the output
|
||||
* payload. This may be
|
||||
* used to impose an upper limit on
|
||||
* the instant bitrate, but should
|
||||
* not be used as the only bitrate
|
||||
* control. Use #OPUS_SET_BITRATE to
|
||||
* control the bitrate.
|
||||
* @returns The length of the encoded packet (in bytes) on success or a
|
||||
* negative error code (see @ref opus_errorcodes) on failure.
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_encode(
|
||||
OpusEncoder *st,
|
||||
const opus_int16 *pcm,
|
||||
int frame_size,
|
||||
unsigned char *data,
|
||||
opus_int32 max_data_bytes
|
||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4);
|
||||
|
||||
/** Encodes an Opus frame from floating point input.
|
||||
* @param [in] st <tt>OpusEncoder*</tt>: Encoder state
|
||||
* @param [in] pcm <tt>float*</tt>: Input in float format (interleaved if 2 channels), with a normal range of +/-1.0.
|
||||
* Samples with a range beyond +/-1.0 are supported but will
|
||||
* be clipped by decoders using the integer API and should
|
||||
* only be used if it is known that the far end supports
|
||||
* extended dynamic range.
|
||||
* length is frame_size*channels*sizeof(float)
|
||||
* @param [in] frame_size <tt>int</tt>: Number of samples per channel in the
|
||||
* input signal.
|
||||
* This must be an Opus frame size for
|
||||
* the encoder's sampling rate.
|
||||
* For example, at 48 kHz the permitted
|
||||
* values are 120, 240, 480, 960, 1920,
|
||||
* and 2880.
|
||||
* Passing in a duration of less than
|
||||
* 10 ms (480 samples at 48 kHz) will
|
||||
* prevent the encoder from using the LPC
|
||||
* or hybrid modes.
|
||||
* @param [out] data <tt>unsigned char*</tt>: Output payload.
|
||||
* This must contain storage for at
|
||||
* least \a max_data_bytes.
|
||||
* @param [in] max_data_bytes <tt>opus_int32</tt>: Size of the allocated
|
||||
* memory for the output
|
||||
* payload. This may be
|
||||
* used to impose an upper limit on
|
||||
* the instant bitrate, but should
|
||||
* not be used as the only bitrate
|
||||
* control. Use #OPUS_SET_BITRATE to
|
||||
* control the bitrate.
|
||||
* @returns The length of the encoded packet (in bytes) on success or a
|
||||
* negative error code (see @ref opus_errorcodes) on failure.
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_encode_float(
|
||||
OpusEncoder *st,
|
||||
const float *pcm,
|
||||
int frame_size,
|
||||
unsigned char *data,
|
||||
opus_int32 max_data_bytes
|
||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4);
|
||||
|
||||
/** Frees an <code>OpusEncoder</code> allocated by opus_encoder_create().
|
||||
* @param[in] st <tt>OpusEncoder*</tt>: State to be freed.
|
||||
*/
|
||||
OPUS_EXPORT void opus_encoder_destroy(OpusEncoder *st);
|
||||
|
||||
/** Perform a CTL function on an Opus encoder.
|
||||
*
|
||||
* Generally the request and subsequent arguments are generated
|
||||
* by a convenience macro.
|
||||
* @param st <tt>OpusEncoder*</tt>: Encoder state.
|
||||
* @param request This and all remaining parameters should be replaced by one
|
||||
* of the convenience macros in @ref opus_genericctls or
|
||||
* @ref opus_encoderctls.
|
||||
* @see opus_genericctls
|
||||
* @see opus_encoderctls
|
||||
*/
|
||||
OPUS_EXPORT int opus_encoder_ctl(OpusEncoder *st, int request, ...) OPUS_ARG_NONNULL(1);
|
||||
/**@}*/
|
||||
|
||||
/** @defgroup opus_decoder Opus Decoder
|
||||
* @{
|
||||
*
|
||||
* @brief This page describes the process and functions used to decode Opus.
|
||||
*
|
||||
* The decoding process also starts with creating a decoder
|
||||
* state. This can be done with:
|
||||
* @code
|
||||
* int error;
|
||||
* OpusDecoder *dec;
|
||||
* dec = opus_decoder_create(Fs, channels, &error);
|
||||
* @endcode
|
||||
* where
|
||||
* @li Fs is the sampling rate and must be 8000, 12000, 16000, 24000, or 48000
|
||||
* @li channels is the number of channels (1 or 2)
|
||||
* @li error will hold the error code in case of failure (or #OPUS_OK on success)
|
||||
* @li the return value is a newly created decoder state to be used for decoding
|
||||
*
|
||||
* While opus_decoder_create() allocates memory for the state, it's also possible
|
||||
* to initialize pre-allocated memory:
|
||||
* @code
|
||||
* int size;
|
||||
* int error;
|
||||
* OpusDecoder *dec;
|
||||
* size = opus_decoder_get_size(channels);
|
||||
* dec = malloc(size);
|
||||
* error = opus_decoder_init(dec, Fs, channels);
|
||||
* @endcode
|
||||
* where opus_decoder_get_size() returns the required size for the decoder state. Note that
|
||||
* future versions of this code may change the size, so no assuptions should be made about it.
|
||||
*
|
||||
* The decoder state is always continuous in memory and only a shallow copy is sufficient
|
||||
* to copy it (e.g. memcpy())
|
||||
*
|
||||
* To decode a frame, opus_decode() or opus_decode_float() must be called with a packet of compressed audio data:
|
||||
* @code
|
||||
* frame_size = opus_decode(dec, packet, len, decoded, max_size, 0);
|
||||
* @endcode
|
||||
* where
|
||||
*
|
||||
* @li packet is the byte array containing the compressed data
|
||||
* @li len is the exact number of bytes contained in the packet
|
||||
* @li decoded is the decoded audio data in opus_int16 (or float for opus_decode_float())
|
||||
* @li max_size is the max duration of the frame in samples (per channel) that can fit into the decoded_frame array
|
||||
*
|
||||
* opus_decode() and opus_decode_float() return the number of samples (per channel) decoded from the packet.
|
||||
* If that value is negative, then an error has occurred. This can occur if the packet is corrupted or if the audio
|
||||
* buffer is too small to hold the decoded audio.
|
||||
*
|
||||
* Opus is a stateful codec with overlapping blocks and as a result Opus
|
||||
* packets are not coded independently of each other. Packets must be
|
||||
* passed into the decoder serially and in the correct order for a correct
|
||||
* decode. Lost packets can be replaced with loss concealment by calling
|
||||
* the decoder with a null pointer and zero length for the missing packet.
|
||||
*
|
||||
* A single codec state may only be accessed from a single thread at
|
||||
* a time and any required locking must be performed by the caller. Separate
|
||||
* streams must be decoded with separate decoder states and can be decoded
|
||||
* in parallel unless the library was compiled with NONTHREADSAFE_PSEUDOSTACK
|
||||
* defined.
|
||||
*
|
||||
*/
|
||||
|
||||
/** Opus decoder state.
|
||||
* This contains the complete state of an Opus decoder.
|
||||
* It is position independent and can be freely copied.
|
||||
* @see opus_decoder_create,opus_decoder_init
|
||||
*/
|
||||
typedef struct OpusDecoder OpusDecoder;
|
||||
|
||||
/** Gets the size of an <code>OpusDecoder</code> structure.
|
||||
* @param [in] channels <tt>int</tt>: Number of channels.
|
||||
* This must be 1 or 2.
|
||||
* @returns The size in bytes.
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_decoder_get_size(int channels);
|
||||
|
||||
/** Allocates and initializes a decoder state.
|
||||
* @param [in] Fs <tt>opus_int32</tt>: Sample rate to decode at (Hz).
|
||||
* This must be one of 8000, 12000, 16000,
|
||||
* 24000, or 48000.
|
||||
* @param [in] channels <tt>int</tt>: Number of channels (1 or 2) to decode
|
||||
* @param [out] error <tt>int*</tt>: #OPUS_OK Success or @ref opus_errorcodes
|
||||
*
|
||||
* Internally Opus stores data at 48000 Hz, so that should be the default
|
||||
* value for Fs. However, the decoder can efficiently decode to buffers
|
||||
* at 8, 12, 16, and 24 kHz so if for some reason the caller cannot use
|
||||
* data at the full sample rate, or knows the compressed data doesn't
|
||||
* use the full frequency range, it can request decoding at a reduced
|
||||
* rate. Likewise, the decoder is capable of filling in either mono or
|
||||
* interleaved stereo pcm buffers, at the caller's request.
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusDecoder *opus_decoder_create(
|
||||
opus_int32 Fs,
|
||||
int channels,
|
||||
int *error
|
||||
);
|
||||
|
||||
/** Initializes a previously allocated decoder state.
|
||||
* The state must be at least the size returned by opus_decoder_get_size().
|
||||
* This is intended for applications which use their own allocator instead of malloc. @see opus_decoder_create,opus_decoder_get_size
|
||||
* To reset a previously initialized state, use the #OPUS_RESET_STATE CTL.
|
||||
* @param [in] st <tt>OpusDecoder*</tt>: Decoder state.
|
||||
* @param [in] Fs <tt>opus_int32</tt>: Sampling rate to decode to (Hz).
|
||||
* This must be one of 8000, 12000, 16000,
|
||||
* 24000, or 48000.
|
||||
* @param [in] channels <tt>int</tt>: Number of channels (1 or 2) to decode
|
||||
* @retval #OPUS_OK Success or @ref opus_errorcodes
|
||||
*/
|
||||
OPUS_EXPORT int opus_decoder_init(
|
||||
OpusDecoder *st,
|
||||
opus_int32 Fs,
|
||||
int channels
|
||||
) OPUS_ARG_NONNULL(1);
|
||||
|
||||
/** Decode an Opus packet.
|
||||
* @param [in] st <tt>OpusDecoder*</tt>: Decoder state
|
||||
* @param [in] data <tt>char*</tt>: Input payload. Use a NULL pointer to indicate packet loss
|
||||
* @param [in] len <tt>opus_int32</tt>: Number of bytes in payload*
|
||||
* @param [out] pcm <tt>opus_int16*</tt>: Output signal (interleaved if 2 channels). length
|
||||
* is frame_size*channels*sizeof(opus_int16)
|
||||
* @param [in] frame_size Number of samples per channel of available space in \a pcm.
|
||||
* If this is less than the maximum packet duration (120ms; 5760 for 48kHz), this function will
|
||||
* not be capable of decoding some packets. In the case of PLC (data==NULL) or FEC (decode_fec=1),
|
||||
* then frame_size needs to be exactly the duration of audio that is missing, otherwise the
|
||||
* decoder will not be in the optimal state to decode the next incoming packet. For the PLC and
|
||||
* FEC cases, frame_size <b>must</b> be a multiple of 2.5 ms.
|
||||
* @param [in] decode_fec <tt>int</tt>: Flag (0 or 1) to request that any in-band forward error correction data be
|
||||
* decoded. If no such data is available, the frame is decoded as if it were lost.
|
||||
* @returns Number of decoded samples or @ref opus_errorcodes
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_decode(
|
||||
OpusDecoder *st,
|
||||
const unsigned char *data,
|
||||
opus_int32 len,
|
||||
opus_int16 *pcm,
|
||||
int frame_size,
|
||||
int decode_fec
|
||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4);
|
||||
|
||||
/** Decode an Opus packet with floating point output.
|
||||
* @param [in] st <tt>OpusDecoder*</tt>: Decoder state
|
||||
* @param [in] data <tt>char*</tt>: Input payload. Use a NULL pointer to indicate packet loss
|
||||
* @param [in] len <tt>opus_int32</tt>: Number of bytes in payload
|
||||
* @param [out] pcm <tt>float*</tt>: Output signal (interleaved if 2 channels). length
|
||||
* is frame_size*channels*sizeof(float)
|
||||
* @param [in] frame_size Number of samples per channel of available space in \a pcm.
|
||||
* If this is less than the maximum packet duration (120ms; 5760 for 48kHz), this function will
|
||||
* not be capable of decoding some packets. In the case of PLC (data==NULL) or FEC (decode_fec=1),
|
||||
* then frame_size needs to be exactly the duration of audio that is missing, otherwise the
|
||||
* decoder will not be in the optimal state to decode the next incoming packet. For the PLC and
|
||||
* FEC cases, frame_size <b>must</b> be a multiple of 2.5 ms.
|
||||
* @param [in] decode_fec <tt>int</tt>: Flag (0 or 1) to request that any in-band forward error correction data be
|
||||
* decoded. If no such data is available the frame is decoded as if it were lost.
|
||||
* @returns Number of decoded samples or @ref opus_errorcodes
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_decode_float(
|
||||
OpusDecoder *st,
|
||||
const unsigned char *data,
|
||||
opus_int32 len,
|
||||
float *pcm,
|
||||
int frame_size,
|
||||
int decode_fec
|
||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4);
|
||||
|
||||
/** Perform a CTL function on an Opus decoder.
|
||||
*
|
||||
* Generally the request and subsequent arguments are generated
|
||||
* by a convenience macro.
|
||||
* @param st <tt>OpusDecoder*</tt>: Decoder state.
|
||||
* @param request This and all remaining parameters should be replaced by one
|
||||
* of the convenience macros in @ref opus_genericctls or
|
||||
* @ref opus_decoderctls.
|
||||
* @see opus_genericctls
|
||||
* @see opus_decoderctls
|
||||
*/
|
||||
OPUS_EXPORT int opus_decoder_ctl(OpusDecoder *st, int request, ...) OPUS_ARG_NONNULL(1);
|
||||
|
||||
/** Frees an <code>OpusDecoder</code> allocated by opus_decoder_create().
|
||||
* @param[in] st <tt>OpusDecoder*</tt>: State to be freed.
|
||||
*/
|
||||
OPUS_EXPORT void opus_decoder_destroy(OpusDecoder *st);
|
||||
|
||||
/** Parse an opus packet into one or more frames.
|
||||
* Opus_decode will perform this operation internally so most applications do
|
||||
* not need to use this function.
|
||||
* This function does not copy the frames, the returned pointers are pointers into
|
||||
* the input packet.
|
||||
* @param [in] data <tt>char*</tt>: Opus packet to be parsed
|
||||
* @param [in] len <tt>opus_int32</tt>: size of data
|
||||
* @param [out] out_toc <tt>char*</tt>: TOC pointer
|
||||
* @param [out] frames <tt>char*[48]</tt> encapsulated frames
|
||||
* @param [out] size <tt>opus_int16[48]</tt> sizes of the encapsulated frames
|
||||
* @param [out] payload_offset <tt>int*</tt>: returns the position of the payload within the packet (in bytes)
|
||||
* @returns number of frames
|
||||
*/
|
||||
OPUS_EXPORT int opus_packet_parse(
|
||||
const unsigned char *data,
|
||||
opus_int32 len,
|
||||
unsigned char *out_toc,
|
||||
const unsigned char *frames[48],
|
||||
opus_int16 size[48],
|
||||
int *payload_offset
|
||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(5);
|
||||
|
||||
/** Gets the bandwidth of an Opus packet.
|
||||
* @param [in] data <tt>char*</tt>: Opus packet
|
||||
* @retval OPUS_BANDWIDTH_NARROWBAND Narrowband (4kHz bandpass)
|
||||
* @retval OPUS_BANDWIDTH_MEDIUMBAND Mediumband (6kHz bandpass)
|
||||
* @retval OPUS_BANDWIDTH_WIDEBAND Wideband (8kHz bandpass)
|
||||
* @retval OPUS_BANDWIDTH_SUPERWIDEBAND Superwideband (12kHz bandpass)
|
||||
* @retval OPUS_BANDWIDTH_FULLBAND Fullband (20kHz bandpass)
|
||||
* @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_bandwidth(const unsigned char *data) OPUS_ARG_NONNULL(1);
|
||||
|
||||
/** Gets the number of samples per frame from an Opus packet.
|
||||
* @param [in] data <tt>char*</tt>: Opus packet.
|
||||
* This must contain at least one byte of
|
||||
* data.
|
||||
* @param [in] Fs <tt>opus_int32</tt>: Sampling rate in Hz.
|
||||
* This must be a multiple of 400, or
|
||||
* inaccurate results will be returned.
|
||||
* @returns Number of samples per frame.
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_samples_per_frame(const unsigned char *data, opus_int32 Fs) OPUS_ARG_NONNULL(1);
|
||||
|
||||
/** Gets the number of channels from an Opus packet.
|
||||
* @param [in] data <tt>char*</tt>: Opus packet
|
||||
* @returns Number of channels
|
||||
* @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_nb_channels(const unsigned char *data) OPUS_ARG_NONNULL(1);
|
||||
|
||||
/** Gets the number of frames in an Opus packet.
|
||||
* @param [in] packet <tt>char*</tt>: Opus packet
|
||||
* @param [in] len <tt>opus_int32</tt>: Length of packet
|
||||
* @returns Number of frames
|
||||
* @retval OPUS_BAD_ARG Insufficient data was passed to the function
|
||||
* @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_nb_frames(const unsigned char packet[], opus_int32 len) OPUS_ARG_NONNULL(1);
|
||||
|
||||
/** Gets the number of samples of an Opus packet.
|
||||
* @param [in] packet <tt>char*</tt>: Opus packet
|
||||
* @param [in] len <tt>opus_int32</tt>: Length of packet
|
||||
* @param [in] Fs <tt>opus_int32</tt>: Sampling rate in Hz.
|
||||
* This must be a multiple of 400, or
|
||||
* inaccurate results will be returned.
|
||||
* @returns Number of samples
|
||||
* @retval OPUS_BAD_ARG Insufficient data was passed to the function
|
||||
* @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_nb_samples(const unsigned char packet[], opus_int32 len, opus_int32 Fs) OPUS_ARG_NONNULL(1);
|
||||
|
||||
/** Gets the number of samples of an Opus packet.
|
||||
* @param [in] dec <tt>OpusDecoder*</tt>: Decoder state
|
||||
* @param [in] packet <tt>char*</tt>: Opus packet
|
||||
* @param [in] len <tt>opus_int32</tt>: Length of packet
|
||||
* @returns Number of samples
|
||||
* @retval OPUS_BAD_ARG Insufficient data was passed to the function
|
||||
* @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_decoder_get_nb_samples(const OpusDecoder *dec, const unsigned char packet[], opus_int32 len) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2);
|
||||
|
||||
/** Applies soft-clipping to bring a float signal within the [-1,1] range. If
|
||||
* the signal is already in that range, nothing is done. If there are values
|
||||
* outside of [-1,1], then the signal is clipped as smoothly as possible to
|
||||
* both fit in the range and avoid creating excessive distortion in the
|
||||
* process.
|
||||
* @param [in,out] pcm <tt>float*</tt>: Input PCM and modified PCM
|
||||
* @param [in] frame_size <tt>int</tt> Number of samples per channel to process
|
||||
* @param [in] channels <tt>int</tt>: Number of channels
|
||||
* @param [in,out] softclip_mem <tt>float*</tt>: State memory for the soft clipping process (one float per channel, initialized to zero)
|
||||
*/
|
||||
OPUS_EXPORT void opus_pcm_soft_clip(float *pcm, int frame_size, int channels, float *softclip_mem);
|
||||
|
||||
|
||||
/**@}*/
|
||||
|
||||
/** @defgroup opus_repacketizer Repacketizer
|
||||
* @{
|
||||
*
|
||||
* The repacketizer can be used to merge multiple Opus packets into a single
|
||||
* packet or alternatively to split Opus packets that have previously been
|
||||
* merged. Splitting valid Opus packets is always guaranteed to succeed,
|
||||
* whereas merging valid packets only succeeds if all frames have the same
|
||||
* mode, bandwidth, and frame size, and when the total duration of the merged
|
||||
* packet is no more than 120 ms. The 120 ms limit comes from the
|
||||
* specification and limits decoder memory requirements at a point where
|
||||
* framing overhead becomes negligible.
|
||||
*
|
||||
* The repacketizer currently only operates on elementary Opus
|
||||
* streams. It will not manipualte multistream packets successfully, except in
|
||||
* the degenerate case where they consist of data from a single stream.
|
||||
*
|
||||
* The repacketizing process starts with creating a repacketizer state, either
|
||||
* by calling opus_repacketizer_create() or by allocating the memory yourself,
|
||||
* e.g.,
|
||||
* @code
|
||||
* OpusRepacketizer *rp;
|
||||
* rp = (OpusRepacketizer*)malloc(opus_repacketizer_get_size());
|
||||
* if (rp != NULL)
|
||||
* opus_repacketizer_init(rp);
|
||||
* @endcode
|
||||
*
|
||||
* Then the application should submit packets with opus_repacketizer_cat(),
|
||||
* extract new packets with opus_repacketizer_out() or
|
||||
* opus_repacketizer_out_range(), and then reset the state for the next set of
|
||||
* input packets via opus_repacketizer_init().
|
||||
*
|
||||
* For example, to split a sequence of packets into individual frames:
|
||||
* @code
|
||||
* unsigned char *data;
|
||||
* int len;
|
||||
* while (get_next_packet(&data, &len))
|
||||
* {
|
||||
* unsigned char out[1276];
|
||||
* opus_int32 out_len;
|
||||
* int nb_frames;
|
||||
* int err;
|
||||
* int i;
|
||||
* err = opus_repacketizer_cat(rp, data, len);
|
||||
* if (err != OPUS_OK)
|
||||
* {
|
||||
* release_packet(data);
|
||||
* return err;
|
||||
* }
|
||||
* nb_frames = opus_repacketizer_get_nb_frames(rp);
|
||||
* for (i = 0; i < nb_frames; i++)
|
||||
* {
|
||||
* out_len = opus_repacketizer_out_range(rp, i, i+1, out, sizeof(out));
|
||||
* if (out_len < 0)
|
||||
* {
|
||||
* release_packet(data);
|
||||
* return (int)out_len;
|
||||
* }
|
||||
* output_next_packet(out, out_len);
|
||||
* }
|
||||
* opus_repacketizer_init(rp);
|
||||
* release_packet(data);
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* Alternatively, to combine a sequence of frames into packets that each
|
||||
* contain up to <code>TARGET_DURATION_MS</code> milliseconds of data:
|
||||
* @code
|
||||
* // The maximum number of packets with duration TARGET_DURATION_MS occurs
|
||||
* // when the frame size is 2.5 ms, for a total of (TARGET_DURATION_MS*2/5)
|
||||
* // packets.
|
||||
* unsigned char *data[(TARGET_DURATION_MS*2/5)+1];
|
||||
* opus_int32 len[(TARGET_DURATION_MS*2/5)+1];
|
||||
* int nb_packets;
|
||||
* unsigned char out[1277*(TARGET_DURATION_MS*2/2)];
|
||||
* opus_int32 out_len;
|
||||
* int prev_toc;
|
||||
* nb_packets = 0;
|
||||
* while (get_next_packet(data+nb_packets, len+nb_packets))
|
||||
* {
|
||||
* int nb_frames;
|
||||
* int err;
|
||||
* nb_frames = opus_packet_get_nb_frames(data[nb_packets], len[nb_packets]);
|
||||
* if (nb_frames < 1)
|
||||
* {
|
||||
* release_packets(data, nb_packets+1);
|
||||
* return nb_frames;
|
||||
* }
|
||||
* nb_frames += opus_repacketizer_get_nb_frames(rp);
|
||||
* // If adding the next packet would exceed our target, or it has an
|
||||
* // incompatible TOC sequence, output the packets we already have before
|
||||
* // submitting it.
|
||||
* // N.B., The nb_packets > 0 check ensures we've submitted at least one
|
||||
* // packet since the last call to opus_repacketizer_init(). Otherwise a
|
||||
* // single packet longer than TARGET_DURATION_MS would cause us to try to
|
||||
* // output an (invalid) empty packet. It also ensures that prev_toc has
|
||||
* // been set to a valid value. Additionally, len[nb_packets] > 0 is
|
||||
* // guaranteed by the call to opus_packet_get_nb_frames() above, so the
|
||||
* // reference to data[nb_packets][0] should be valid.
|
||||
* if (nb_packets > 0 && (
|
||||
* ((prev_toc & 0xFC) != (data[nb_packets][0] & 0xFC)) ||
|
||||
* opus_packet_get_samples_per_frame(data[nb_packets], 48000)*nb_frames >
|
||||
* TARGET_DURATION_MS*48))
|
||||
* {
|
||||
* out_len = opus_repacketizer_out(rp, out, sizeof(out));
|
||||
* if (out_len < 0)
|
||||
* {
|
||||
* release_packets(data, nb_packets+1);
|
||||
* return (int)out_len;
|
||||
* }
|
||||
* output_next_packet(out, out_len);
|
||||
* opus_repacketizer_init(rp);
|
||||
* release_packets(data, nb_packets);
|
||||
* data[0] = data[nb_packets];
|
||||
* len[0] = len[nb_packets];
|
||||
* nb_packets = 0;
|
||||
* }
|
||||
* err = opus_repacketizer_cat(rp, data[nb_packets], len[nb_packets]);
|
||||
* if (err != OPUS_OK)
|
||||
* {
|
||||
* release_packets(data, nb_packets+1);
|
||||
* return err;
|
||||
* }
|
||||
* prev_toc = data[nb_packets][0];
|
||||
* nb_packets++;
|
||||
* }
|
||||
* // Output the final, partial packet.
|
||||
* if (nb_packets > 0)
|
||||
* {
|
||||
* out_len = opus_repacketizer_out(rp, out, sizeof(out));
|
||||
* release_packets(data, nb_packets);
|
||||
* if (out_len < 0)
|
||||
* return (int)out_len;
|
||||
* output_next_packet(out, out_len);
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* An alternate way of merging packets is to simply call opus_repacketizer_cat()
|
||||
* unconditionally until it fails. At that point, the merged packet can be
|
||||
* obtained with opus_repacketizer_out() and the input packet for which
|
||||
* opus_repacketizer_cat() needs to be re-added to a newly reinitialized
|
||||
* repacketizer state.
|
||||
*/
|
||||
|
||||
typedef struct OpusRepacketizer OpusRepacketizer;
|
||||
|
||||
/** Gets the size of an <code>OpusRepacketizer</code> structure.
|
||||
* @returns The size in bytes.
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_repacketizer_get_size(void);
|
||||
|
||||
/** (Re)initializes a previously allocated repacketizer state.
|
||||
* The state must be at least the size returned by opus_repacketizer_get_size().
|
||||
* This can be used for applications which use their own allocator instead of
|
||||
* malloc().
|
||||
* It must also be called to reset the queue of packets waiting to be
|
||||
* repacketized, which is necessary if the maximum packet duration of 120 ms
|
||||
* is reached or if you wish to submit packets with a different Opus
|
||||
* configuration (coding mode, audio bandwidth, frame size, or channel count).
|
||||
* Failure to do so will prevent a new packet from being added with
|
||||
* opus_repacketizer_cat().
|
||||
* @see opus_repacketizer_create
|
||||
* @see opus_repacketizer_get_size
|
||||
* @see opus_repacketizer_cat
|
||||
* @param rp <tt>OpusRepacketizer*</tt>: The repacketizer state to
|
||||
* (re)initialize.
|
||||
* @returns A pointer to the same repacketizer state that was passed in.
|
||||
*/
|
||||
OPUS_EXPORT OpusRepacketizer *opus_repacketizer_init(OpusRepacketizer *rp) OPUS_ARG_NONNULL(1);
|
||||
|
||||
/** Allocates memory and initializes the new repacketizer with
|
||||
* opus_repacketizer_init().
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusRepacketizer *opus_repacketizer_create(void);
|
||||
|
||||
/** Frees an <code>OpusRepacketizer</code> allocated by
|
||||
* opus_repacketizer_create().
|
||||
* @param[in] rp <tt>OpusRepacketizer*</tt>: State to be freed.
|
||||
*/
|
||||
OPUS_EXPORT void opus_repacketizer_destroy(OpusRepacketizer *rp);
|
||||
|
||||
/** Add a packet to the current repacketizer state.
|
||||
* This packet must match the configuration of any packets already submitted
|
||||
* for repacketization since the last call to opus_repacketizer_init().
|
||||
* This means that it must have the same coding mode, audio bandwidth, frame
|
||||
* size, and channel count.
|
||||
* This can be checked in advance by examining the top 6 bits of the first
|
||||
* byte of the packet, and ensuring they match the top 6 bits of the first
|
||||
* byte of any previously submitted packet.
|
||||
* The total duration of audio in the repacketizer state also must not exceed
|
||||
* 120 ms, the maximum duration of a single packet, after adding this packet.
|
||||
*
|
||||
* The contents of the current repacketizer state can be extracted into new
|
||||
* packets using opus_repacketizer_out() or opus_repacketizer_out_range().
|
||||
*
|
||||
* In order to add a packet with a different configuration or to add more
|
||||
* audio beyond 120 ms, you must clear the repacketizer state by calling
|
||||
* opus_repacketizer_init().
|
||||
* If a packet is too large to add to the current repacketizer state, no part
|
||||
* of it is added, even if it contains multiple frames, some of which might
|
||||
* fit.
|
||||
* If you wish to be able to add parts of such packets, you should first use
|
||||
* another repacketizer to split the packet into pieces and add them
|
||||
* individually.
|
||||
* @see opus_repacketizer_out_range
|
||||
* @see opus_repacketizer_out
|
||||
* @see opus_repacketizer_init
|
||||
* @param rp <tt>OpusRepacketizer*</tt>: The repacketizer state to which to
|
||||
* add the packet.
|
||||
* @param[in] data <tt>const unsigned char*</tt>: The packet data.
|
||||
* The application must ensure
|
||||
* this pointer remains valid
|
||||
* until the next call to
|
||||
* opus_repacketizer_init() or
|
||||
* opus_repacketizer_destroy().
|
||||
* @param len <tt>opus_int32</tt>: The number of bytes in the packet data.
|
||||
* @returns An error code indicating whether or not the operation succeeded.
|
||||
* @retval #OPUS_OK The packet's contents have been added to the repacketizer
|
||||
* state.
|
||||
* @retval #OPUS_INVALID_PACKET The packet did not have a valid TOC sequence,
|
||||
* the packet's TOC sequence was not compatible
|
||||
* with previously submitted packets (because
|
||||
* the coding mode, audio bandwidth, frame size,
|
||||
* or channel count did not match), or adding
|
||||
* this packet would increase the total amount of
|
||||
* audio stored in the repacketizer state to more
|
||||
* than 120 ms.
|
||||
*/
|
||||
OPUS_EXPORT int opus_repacketizer_cat(OpusRepacketizer *rp, const unsigned char *data, opus_int32 len) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2);
|
||||
|
||||
|
||||
/** Construct a new packet from data previously submitted to the repacketizer
|
||||
* state via opus_repacketizer_cat().
|
||||
* @param rp <tt>OpusRepacketizer*</tt>: The repacketizer state from which to
|
||||
* construct the new packet.
|
||||
* @param begin <tt>int</tt>: The index of the first frame in the current
|
||||
* repacketizer state to include in the output.
|
||||
* @param end <tt>int</tt>: One past the index of the last frame in the
|
||||
* current repacketizer state to include in the
|
||||
* output.
|
||||
* @param[out] data <tt>const unsigned char*</tt>: The buffer in which to
|
||||
* store the output packet.
|
||||
* @param maxlen <tt>opus_int32</tt>: The maximum number of bytes to store in
|
||||
* the output buffer. In order to guarantee
|
||||
* success, this should be at least
|
||||
* <code>1276</code> for a single frame,
|
||||
* or for multiple frames,
|
||||
* <code>1277*(end-begin)</code>.
|
||||
* However, <code>1*(end-begin)</code> plus
|
||||
* the size of all packet data submitted to
|
||||
* the repacketizer since the last call to
|
||||
* opus_repacketizer_init() or
|
||||
* opus_repacketizer_create() is also
|
||||
* sufficient, and possibly much smaller.
|
||||
* @returns The total size of the output packet on success, or an error code
|
||||
* on failure.
|
||||
* @retval #OPUS_BAD_ARG <code>[begin,end)</code> was an invalid range of
|
||||
* frames (begin < 0, begin >= end, or end >
|
||||
* opus_repacketizer_get_nb_frames()).
|
||||
* @retval #OPUS_BUFFER_TOO_SMALL \a maxlen was insufficient to contain the
|
||||
* complete output packet.
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_repacketizer_out_range(OpusRepacketizer *rp, int begin, int end, unsigned char *data, opus_int32 maxlen) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4);
|
||||
|
||||
/** Return the total number of frames contained in packet data submitted to
|
||||
* the repacketizer state so far via opus_repacketizer_cat() since the last
|
||||
* call to opus_repacketizer_init() or opus_repacketizer_create().
|
||||
* This defines the valid range of packets that can be extracted with
|
||||
* opus_repacketizer_out_range() or opus_repacketizer_out().
|
||||
* @param rp <tt>OpusRepacketizer*</tt>: The repacketizer state containing the
|
||||
* frames.
|
||||
* @returns The total number of frames contained in the packet data submitted
|
||||
* to the repacketizer state.
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_repacketizer_get_nb_frames(OpusRepacketizer *rp) OPUS_ARG_NONNULL(1);
|
||||
|
||||
/** Construct a new packet from data previously submitted to the repacketizer
|
||||
* state via opus_repacketizer_cat().
|
||||
* This is a convenience routine that returns all the data submitted so far
|
||||
* in a single packet.
|
||||
* It is equivalent to calling
|
||||
* @code
|
||||
* opus_repacketizer_out_range(rp, 0, opus_repacketizer_get_nb_frames(rp),
|
||||
* data, maxlen)
|
||||
* @endcode
|
||||
* @param rp <tt>OpusRepacketizer*</tt>: The repacketizer state from which to
|
||||
* construct the new packet.
|
||||
* @param[out] data <tt>const unsigned char*</tt>: The buffer in which to
|
||||
* store the output packet.
|
||||
* @param maxlen <tt>opus_int32</tt>: The maximum number of bytes to store in
|
||||
* the output buffer. In order to guarantee
|
||||
* success, this should be at least
|
||||
* <code>1277*opus_repacketizer_get_nb_frames(rp)</code>.
|
||||
* However,
|
||||
* <code>1*opus_repacketizer_get_nb_frames(rp)</code>
|
||||
* plus the size of all packet data
|
||||
* submitted to the repacketizer since the
|
||||
* last call to opus_repacketizer_init() or
|
||||
* opus_repacketizer_create() is also
|
||||
* sufficient, and possibly much smaller.
|
||||
* @returns The total size of the output packet on success, or an error code
|
||||
* on failure.
|
||||
* @retval #OPUS_BUFFER_TOO_SMALL \a maxlen was insufficient to contain the
|
||||
* complete output packet.
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_repacketizer_out(OpusRepacketizer *rp, unsigned char *data, opus_int32 maxlen) OPUS_ARG_NONNULL(1);
|
||||
|
||||
/** Pads a given Opus packet to a larger size (possibly changing the TOC sequence).
|
||||
* @param[in,out] data <tt>const unsigned char*</tt>: The buffer containing the
|
||||
* packet to pad.
|
||||
* @param len <tt>opus_int32</tt>: The size of the packet.
|
||||
* This must be at least 1.
|
||||
* @param new_len <tt>opus_int32</tt>: The desired size of the packet after padding.
|
||||
* This must be at least as large as len.
|
||||
* @returns an error code
|
||||
* @retval #OPUS_OK \a on success.
|
||||
* @retval #OPUS_BAD_ARG \a len was less than 1 or new_len was less than len.
|
||||
* @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet.
|
||||
*/
|
||||
OPUS_EXPORT int opus_packet_pad(unsigned char *data, opus_int32 len, opus_int32 new_len);
|
||||
|
||||
/** Remove all padding from a given Opus packet and rewrite the TOC sequence to
|
||||
* minimize space usage.
|
||||
* @param[in,out] data <tt>const unsigned char*</tt>: The buffer containing the
|
||||
* packet to strip.
|
||||
* @param len <tt>opus_int32</tt>: The size of the packet.
|
||||
* This must be at least 1.
|
||||
* @returns The new size of the output packet on success, or an error code
|
||||
* on failure.
|
||||
* @retval #OPUS_BAD_ARG \a len was less than 1.
|
||||
* @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet.
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_packet_unpad(unsigned char *data, opus_int32 len);
|
||||
|
||||
/** Pads a given Opus multi-stream packet to a larger size (possibly changing the TOC sequence).
|
||||
* @param[in,out] data <tt>const unsigned char*</tt>: The buffer containing the
|
||||
* packet to pad.
|
||||
* @param len <tt>opus_int32</tt>: The size of the packet.
|
||||
* This must be at least 1.
|
||||
* @param new_len <tt>opus_int32</tt>: The desired size of the packet after padding.
|
||||
* This must be at least 1.
|
||||
* @param nb_streams <tt>opus_int32</tt>: The number of streams (not channels) in the packet.
|
||||
* This must be at least as large as len.
|
||||
* @returns an error code
|
||||
* @retval #OPUS_OK \a on success.
|
||||
* @retval #OPUS_BAD_ARG \a len was less than 1.
|
||||
* @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet.
|
||||
*/
|
||||
OPUS_EXPORT int opus_multistream_packet_pad(unsigned char *data, opus_int32 len, opus_int32 new_len, int nb_streams);
|
||||
|
||||
/** Remove all padding from a given Opus multi-stream packet and rewrite the TOC sequence to
|
||||
* minimize space usage.
|
||||
* @param[in,out] data <tt>const unsigned char*</tt>: The buffer containing the
|
||||
* packet to strip.
|
||||
* @param len <tt>opus_int32</tt>: The size of the packet.
|
||||
* This must be at least 1.
|
||||
* @param nb_streams <tt>opus_int32</tt>: The number of streams (not channels) in the packet.
|
||||
* This must be at least 1.
|
||||
* @returns The new size of the output packet on success, or an error code
|
||||
* on failure.
|
||||
* @retval #OPUS_BAD_ARG \a len was less than 1 or new_len was less than len.
|
||||
* @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet.
|
||||
*/
|
||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_multistream_packet_unpad(unsigned char *data, opus_int32 len, int nb_streams);
|
||||
|
||||
/**@}*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OPUS_H */
|
||||
Executable
BIN
Binary file not shown.
Executable
+799
@@ -0,0 +1,799 @@
|
||||
/* Copyright (c) 2010-2011 Xiph.Org Foundation, Skype Limited
|
||||
Written by Jean-Marc Valin and Koen Vos */
|
||||
/*
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file opus_defines.h
|
||||
* @brief Opus reference implementation constants
|
||||
*/
|
||||
|
||||
#ifndef OPUS_DEFINES_H
|
||||
#define OPUS_DEFINES_H
|
||||
|
||||
#include "opus_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @defgroup opus_errorcodes Error codes
|
||||
* @{
|
||||
*/
|
||||
/** No error @hideinitializer*/
|
||||
#define OPUS_OK 0
|
||||
/** One or more invalid/out of range arguments @hideinitializer*/
|
||||
#define OPUS_BAD_ARG -1
|
||||
/** Not enough bytes allocated in the buffer @hideinitializer*/
|
||||
#define OPUS_BUFFER_TOO_SMALL -2
|
||||
/** An internal error was detected @hideinitializer*/
|
||||
#define OPUS_INTERNAL_ERROR -3
|
||||
/** The compressed data passed is corrupted @hideinitializer*/
|
||||
#define OPUS_INVALID_PACKET -4
|
||||
/** Invalid/unsupported request number @hideinitializer*/
|
||||
#define OPUS_UNIMPLEMENTED -5
|
||||
/** An encoder or decoder structure is invalid or already freed @hideinitializer*/
|
||||
#define OPUS_INVALID_STATE -6
|
||||
/** Memory allocation has failed @hideinitializer*/
|
||||
#define OPUS_ALLOC_FAIL -7
|
||||
/**@}*/
|
||||
|
||||
/** @cond OPUS_INTERNAL_DOC */
|
||||
/**Export control for opus functions */
|
||||
|
||||
#ifndef OPUS_EXPORT
|
||||
# if defined(WIN32)
|
||||
# if defined(OPUS_BUILD) && defined(DLL_EXPORT)
|
||||
# define OPUS_EXPORT __declspec(dllexport)
|
||||
# else
|
||||
# define OPUS_EXPORT
|
||||
# endif
|
||||
# elif defined(__GNUC__) && defined(OPUS_BUILD)
|
||||
# define OPUS_EXPORT __attribute__ ((visibility ("default")))
|
||||
# else
|
||||
# define OPUS_EXPORT
|
||||
# endif
|
||||
#endif
|
||||
|
||||
# if !defined(OPUS_GNUC_PREREQ)
|
||||
# if defined(__GNUC__)&&defined(__GNUC_MINOR__)
|
||||
# define OPUS_GNUC_PREREQ(_maj,_min) \
|
||||
((__GNUC__<<16)+__GNUC_MINOR__>=((_maj)<<16)+(_min))
|
||||
# else
|
||||
# define OPUS_GNUC_PREREQ(_maj,_min) 0
|
||||
# endif
|
||||
# endif
|
||||
|
||||
#if (!defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) )
|
||||
# if OPUS_GNUC_PREREQ(3,0)
|
||||
# define OPUS_RESTRICT __restrict__
|
||||
# elif (defined(_MSC_VER) && _MSC_VER >= 1400)
|
||||
# define OPUS_RESTRICT __restrict
|
||||
# else
|
||||
# define OPUS_RESTRICT
|
||||
# endif
|
||||
#else
|
||||
# define OPUS_RESTRICT restrict
|
||||
#endif
|
||||
|
||||
#if (!defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) )
|
||||
# if OPUS_GNUC_PREREQ(2,7)
|
||||
# define OPUS_INLINE __inline__
|
||||
# elif (defined(_MSC_VER))
|
||||
# define OPUS_INLINE __inline
|
||||
# else
|
||||
# define OPUS_INLINE
|
||||
# endif
|
||||
#else
|
||||
# define OPUS_INLINE inline
|
||||
#endif
|
||||
|
||||
/**Warning attributes for opus functions
|
||||
* NONNULL is not used in OPUS_BUILD to avoid the compiler optimizing out
|
||||
* some paranoid null checks. */
|
||||
#if defined(__GNUC__) && OPUS_GNUC_PREREQ(3, 4)
|
||||
# define OPUS_WARN_UNUSED_RESULT __attribute__ ((__warn_unused_result__))
|
||||
#else
|
||||
# define OPUS_WARN_UNUSED_RESULT
|
||||
#endif
|
||||
#if !defined(OPUS_BUILD) && defined(__GNUC__) && OPUS_GNUC_PREREQ(3, 4)
|
||||
# define OPUS_ARG_NONNULL(_x) __attribute__ ((__nonnull__(_x)))
|
||||
#else
|
||||
# define OPUS_ARG_NONNULL(_x)
|
||||
#endif
|
||||
|
||||
/** These are the actual Encoder CTL ID numbers.
|
||||
* They should not be used directly by applications.
|
||||
* In general, SETs should be even and GETs should be odd.*/
|
||||
#define OPUS_SET_APPLICATION_REQUEST 4000
|
||||
#define OPUS_GET_APPLICATION_REQUEST 4001
|
||||
#define OPUS_SET_BITRATE_REQUEST 4002
|
||||
#define OPUS_GET_BITRATE_REQUEST 4003
|
||||
#define OPUS_SET_MAX_BANDWIDTH_REQUEST 4004
|
||||
#define OPUS_GET_MAX_BANDWIDTH_REQUEST 4005
|
||||
#define OPUS_SET_VBR_REQUEST 4006
|
||||
#define OPUS_GET_VBR_REQUEST 4007
|
||||
#define OPUS_SET_BANDWIDTH_REQUEST 4008
|
||||
#define OPUS_GET_BANDWIDTH_REQUEST 4009
|
||||
#define OPUS_SET_COMPLEXITY_REQUEST 4010
|
||||
#define OPUS_GET_COMPLEXITY_REQUEST 4011
|
||||
#define OPUS_SET_INBAND_FEC_REQUEST 4012
|
||||
#define OPUS_GET_INBAND_FEC_REQUEST 4013
|
||||
#define OPUS_SET_PACKET_LOSS_PERC_REQUEST 4014
|
||||
#define OPUS_GET_PACKET_LOSS_PERC_REQUEST 4015
|
||||
#define OPUS_SET_DTX_REQUEST 4016
|
||||
#define OPUS_GET_DTX_REQUEST 4017
|
||||
#define OPUS_SET_VBR_CONSTRAINT_REQUEST 4020
|
||||
#define OPUS_GET_VBR_CONSTRAINT_REQUEST 4021
|
||||
#define OPUS_SET_FORCE_CHANNELS_REQUEST 4022
|
||||
#define OPUS_GET_FORCE_CHANNELS_REQUEST 4023
|
||||
#define OPUS_SET_SIGNAL_REQUEST 4024
|
||||
#define OPUS_GET_SIGNAL_REQUEST 4025
|
||||
#define OPUS_GET_LOOKAHEAD_REQUEST 4027
|
||||
/* #define OPUS_RESET_STATE 4028 */
|
||||
#define OPUS_GET_SAMPLE_RATE_REQUEST 4029
|
||||
#define OPUS_GET_FINAL_RANGE_REQUEST 4031
|
||||
#define OPUS_GET_PITCH_REQUEST 4033
|
||||
#define OPUS_SET_GAIN_REQUEST 4034
|
||||
#define OPUS_GET_GAIN_REQUEST 4045 /* Should have been 4035 */
|
||||
#define OPUS_SET_LSB_DEPTH_REQUEST 4036
|
||||
#define OPUS_GET_LSB_DEPTH_REQUEST 4037
|
||||
#define OPUS_GET_LAST_PACKET_DURATION_REQUEST 4039
|
||||
#define OPUS_SET_EXPERT_FRAME_DURATION_REQUEST 4040
|
||||
#define OPUS_GET_EXPERT_FRAME_DURATION_REQUEST 4041
|
||||
#define OPUS_SET_PREDICTION_DISABLED_REQUEST 4042
|
||||
#define OPUS_GET_PREDICTION_DISABLED_REQUEST 4043
|
||||
/* Don't use 4045, it's already taken by OPUS_GET_GAIN_REQUEST */
|
||||
#define OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST 4046
|
||||
#define OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST 4047
|
||||
#define OPUS_GET_IN_DTX_REQUEST 4049
|
||||
|
||||
/** Defines for the presence of extended APIs. */
|
||||
#define OPUS_HAVE_OPUS_PROJECTION_H
|
||||
|
||||
/* Macros to trigger compilation errors when the wrong types are provided to a CTL */
|
||||
#define __opus_check_int(x) (((void)((x) == (opus_int32)0)), (opus_int32)(x))
|
||||
#define __opus_check_int_ptr(ptr) ((ptr) + ((ptr) - (opus_int32*)(ptr)))
|
||||
#define __opus_check_uint_ptr(ptr) ((ptr) + ((ptr) - (opus_uint32*)(ptr)))
|
||||
#define __opus_check_val16_ptr(ptr) ((ptr) + ((ptr) - (opus_val16*)(ptr)))
|
||||
/** @endcond */
|
||||
|
||||
/** @defgroup opus_ctlvalues Pre-defined values for CTL interface
|
||||
* @see opus_genericctls, opus_encoderctls
|
||||
* @{
|
||||
*/
|
||||
/* Values for the various encoder CTLs */
|
||||
#define OPUS_AUTO -1000 /**<Auto/default setting @hideinitializer*/
|
||||
#define OPUS_BITRATE_MAX -1 /**<Maximum bitrate @hideinitializer*/
|
||||
|
||||
/** Best for most VoIP/videoconference applications where listening quality and intelligibility matter most
|
||||
* @hideinitializer */
|
||||
#define OPUS_APPLICATION_VOIP 2048
|
||||
/** Best for broadcast/high-fidelity application where the decoded audio should be as close as possible to the input
|
||||
* @hideinitializer */
|
||||
#define OPUS_APPLICATION_AUDIO 2049
|
||||
/** Only use when lowest-achievable latency is what matters most. Voice-optimized modes cannot be used.
|
||||
* @hideinitializer */
|
||||
#define OPUS_APPLICATION_RESTRICTED_LOWDELAY 2051
|
||||
|
||||
#define OPUS_SIGNAL_VOICE 3001 /**< Signal being encoded is voice */
|
||||
#define OPUS_SIGNAL_MUSIC 3002 /**< Signal being encoded is music */
|
||||
#define OPUS_BANDWIDTH_NARROWBAND 1101 /**< 4 kHz bandpass @hideinitializer*/
|
||||
#define OPUS_BANDWIDTH_MEDIUMBAND 1102 /**< 6 kHz bandpass @hideinitializer*/
|
||||
#define OPUS_BANDWIDTH_WIDEBAND 1103 /**< 8 kHz bandpass @hideinitializer*/
|
||||
#define OPUS_BANDWIDTH_SUPERWIDEBAND 1104 /**<12 kHz bandpass @hideinitializer*/
|
||||
#define OPUS_BANDWIDTH_FULLBAND 1105 /**<20 kHz bandpass @hideinitializer*/
|
||||
|
||||
#define OPUS_FRAMESIZE_ARG 5000 /**< Select frame size from the argument (default) */
|
||||
#define OPUS_FRAMESIZE_2_5_MS 5001 /**< Use 2.5 ms frames */
|
||||
#define OPUS_FRAMESIZE_5_MS 5002 /**< Use 5 ms frames */
|
||||
#define OPUS_FRAMESIZE_10_MS 5003 /**< Use 10 ms frames */
|
||||
#define OPUS_FRAMESIZE_20_MS 5004 /**< Use 20 ms frames */
|
||||
#define OPUS_FRAMESIZE_40_MS 5005 /**< Use 40 ms frames */
|
||||
#define OPUS_FRAMESIZE_60_MS 5006 /**< Use 60 ms frames */
|
||||
#define OPUS_FRAMESIZE_80_MS 5007 /**< Use 80 ms frames */
|
||||
#define OPUS_FRAMESIZE_100_MS 5008 /**< Use 100 ms frames */
|
||||
#define OPUS_FRAMESIZE_120_MS 5009 /**< Use 120 ms frames */
|
||||
|
||||
/**@}*/
|
||||
|
||||
|
||||
/** @defgroup opus_encoderctls Encoder related CTLs
|
||||
*
|
||||
* These are convenience macros for use with the \c opus_encode_ctl
|
||||
* interface. They are used to generate the appropriate series of
|
||||
* arguments for that call, passing the correct type, size and so
|
||||
* on as expected for each particular request.
|
||||
*
|
||||
* Some usage examples:
|
||||
*
|
||||
* @code
|
||||
* int ret;
|
||||
* ret = opus_encoder_ctl(enc_ctx, OPUS_SET_BANDWIDTH(OPUS_AUTO));
|
||||
* if (ret != OPUS_OK) return ret;
|
||||
*
|
||||
* opus_int32 rate;
|
||||
* opus_encoder_ctl(enc_ctx, OPUS_GET_BANDWIDTH(&rate));
|
||||
*
|
||||
* opus_encoder_ctl(enc_ctx, OPUS_RESET_STATE);
|
||||
* @endcode
|
||||
*
|
||||
* @see opus_genericctls, opus_encoder
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** Configures the encoder's computational complexity.
|
||||
* The supported range is 0-10 inclusive with 10 representing the highest complexity.
|
||||
* @see OPUS_GET_COMPLEXITY
|
||||
* @param[in] x <tt>opus_int32</tt>: Allowed values: 0-10, inclusive.
|
||||
*
|
||||
* @hideinitializer */
|
||||
#define OPUS_SET_COMPLEXITY(x) OPUS_SET_COMPLEXITY_REQUEST, __opus_check_int(x)
|
||||
/** Gets the encoder's complexity configuration.
|
||||
* @see OPUS_SET_COMPLEXITY
|
||||
* @param[out] x <tt>opus_int32 *</tt>: Returns a value in the range 0-10,
|
||||
* inclusive.
|
||||
* @hideinitializer */
|
||||
#define OPUS_GET_COMPLEXITY(x) OPUS_GET_COMPLEXITY_REQUEST, __opus_check_int_ptr(x)
|
||||
|
||||
/** Configures the bitrate in the encoder.
|
||||
* Rates from 500 to 512000 bits per second are meaningful, as well as the
|
||||
* special values #OPUS_AUTO and #OPUS_BITRATE_MAX.
|
||||
* The value #OPUS_BITRATE_MAX can be used to cause the codec to use as much
|
||||
* rate as it can, which is useful for controlling the rate by adjusting the
|
||||
* output buffer size.
|
||||
* @see OPUS_GET_BITRATE
|
||||
* @param[in] x <tt>opus_int32</tt>: Bitrate in bits per second. The default
|
||||
* is determined based on the number of
|
||||
* channels and the input sampling rate.
|
||||
* @hideinitializer */
|
||||
#define OPUS_SET_BITRATE(x) OPUS_SET_BITRATE_REQUEST, __opus_check_int(x)
|
||||
/** Gets the encoder's bitrate configuration.
|
||||
* @see OPUS_SET_BITRATE
|
||||
* @param[out] x <tt>opus_int32 *</tt>: Returns the bitrate in bits per second.
|
||||
* The default is determined based on the
|
||||
* number of channels and the input
|
||||
* sampling rate.
|
||||
* @hideinitializer */
|
||||
#define OPUS_GET_BITRATE(x) OPUS_GET_BITRATE_REQUEST, __opus_check_int_ptr(x)
|
||||
|
||||
/** Enables or disables variable bitrate (VBR) in the encoder.
|
||||
* The configured bitrate may not be met exactly because frames must
|
||||
* be an integer number of bytes in length.
|
||||
* @see OPUS_GET_VBR
|
||||
* @see OPUS_SET_VBR_CONSTRAINT
|
||||
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
||||
* <dl>
|
||||
* <dt>0</dt><dd>Hard CBR. For LPC/hybrid modes at very low bit-rate, this can
|
||||
* cause noticeable quality degradation.</dd>
|
||||
* <dt>1</dt><dd>VBR (default). The exact type of VBR is controlled by
|
||||
* #OPUS_SET_VBR_CONSTRAINT.</dd>
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_SET_VBR(x) OPUS_SET_VBR_REQUEST, __opus_check_int(x)
|
||||
/** Determine if variable bitrate (VBR) is enabled in the encoder.
|
||||
* @see OPUS_SET_VBR
|
||||
* @see OPUS_GET_VBR_CONSTRAINT
|
||||
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
||||
* <dl>
|
||||
* <dt>0</dt><dd>Hard CBR.</dd>
|
||||
* <dt>1</dt><dd>VBR (default). The exact type of VBR may be retrieved via
|
||||
* #OPUS_GET_VBR_CONSTRAINT.</dd>
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_GET_VBR(x) OPUS_GET_VBR_REQUEST, __opus_check_int_ptr(x)
|
||||
|
||||
/** Enables or disables constrained VBR in the encoder.
|
||||
* This setting is ignored when the encoder is in CBR mode.
|
||||
* @warning Only the MDCT mode of Opus currently heeds the constraint.
|
||||
* Speech mode ignores it completely, hybrid mode may fail to obey it
|
||||
* if the LPC layer uses more bitrate than the constraint would have
|
||||
* permitted.
|
||||
* @see OPUS_GET_VBR_CONSTRAINT
|
||||
* @see OPUS_SET_VBR
|
||||
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
||||
* <dl>
|
||||
* <dt>0</dt><dd>Unconstrained VBR.</dd>
|
||||
* <dt>1</dt><dd>Constrained VBR (default). This creates a maximum of one
|
||||
* frame of buffering delay assuming a transport with a
|
||||
* serialization speed of the nominal bitrate.</dd>
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_SET_VBR_CONSTRAINT(x) OPUS_SET_VBR_CONSTRAINT_REQUEST, __opus_check_int(x)
|
||||
/** Determine if constrained VBR is enabled in the encoder.
|
||||
* @see OPUS_SET_VBR_CONSTRAINT
|
||||
* @see OPUS_GET_VBR
|
||||
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
||||
* <dl>
|
||||
* <dt>0</dt><dd>Unconstrained VBR.</dd>
|
||||
* <dt>1</dt><dd>Constrained VBR (default).</dd>
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_GET_VBR_CONSTRAINT(x) OPUS_GET_VBR_CONSTRAINT_REQUEST, __opus_check_int_ptr(x)
|
||||
|
||||
/** Configures mono/stereo forcing in the encoder.
|
||||
* This can force the encoder to produce packets encoded as either mono or
|
||||
* stereo, regardless of the format of the input audio. This is useful when
|
||||
* the caller knows that the input signal is currently a mono source embedded
|
||||
* in a stereo stream.
|
||||
* @see OPUS_GET_FORCE_CHANNELS
|
||||
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
||||
* <dl>
|
||||
* <dt>#OPUS_AUTO</dt><dd>Not forced (default)</dd>
|
||||
* <dt>1</dt> <dd>Forced mono</dd>
|
||||
* <dt>2</dt> <dd>Forced stereo</dd>
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_SET_FORCE_CHANNELS(x) OPUS_SET_FORCE_CHANNELS_REQUEST, __opus_check_int(x)
|
||||
/** Gets the encoder's forced channel configuration.
|
||||
* @see OPUS_SET_FORCE_CHANNELS
|
||||
* @param[out] x <tt>opus_int32 *</tt>:
|
||||
* <dl>
|
||||
* <dt>#OPUS_AUTO</dt><dd>Not forced (default)</dd>
|
||||
* <dt>1</dt> <dd>Forced mono</dd>
|
||||
* <dt>2</dt> <dd>Forced stereo</dd>
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_GET_FORCE_CHANNELS(x) OPUS_GET_FORCE_CHANNELS_REQUEST, __opus_check_int_ptr(x)
|
||||
|
||||
/** Configures the maximum bandpass that the encoder will select automatically.
|
||||
* Applications should normally use this instead of #OPUS_SET_BANDWIDTH
|
||||
* (leaving that set to the default, #OPUS_AUTO). This allows the
|
||||
* application to set an upper bound based on the type of input it is
|
||||
* providing, but still gives the encoder the freedom to reduce the bandpass
|
||||
* when the bitrate becomes too low, for better overall quality.
|
||||
* @see OPUS_GET_MAX_BANDWIDTH
|
||||
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
||||
* <dl>
|
||||
* <dt>OPUS_BANDWIDTH_NARROWBAND</dt> <dd>4 kHz passband</dd>
|
||||
* <dt>OPUS_BANDWIDTH_MEDIUMBAND</dt> <dd>6 kHz passband</dd>
|
||||
* <dt>OPUS_BANDWIDTH_WIDEBAND</dt> <dd>8 kHz passband</dd>
|
||||
* <dt>OPUS_BANDWIDTH_SUPERWIDEBAND</dt><dd>12 kHz passband</dd>
|
||||
* <dt>OPUS_BANDWIDTH_FULLBAND</dt> <dd>20 kHz passband (default)</dd>
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_SET_MAX_BANDWIDTH(x) OPUS_SET_MAX_BANDWIDTH_REQUEST, __opus_check_int(x)
|
||||
|
||||
/** Gets the encoder's configured maximum allowed bandpass.
|
||||
* @see OPUS_SET_MAX_BANDWIDTH
|
||||
* @param[out] x <tt>opus_int32 *</tt>: Allowed values:
|
||||
* <dl>
|
||||
* <dt>#OPUS_BANDWIDTH_NARROWBAND</dt> <dd>4 kHz passband</dd>
|
||||
* <dt>#OPUS_BANDWIDTH_MEDIUMBAND</dt> <dd>6 kHz passband</dd>
|
||||
* <dt>#OPUS_BANDWIDTH_WIDEBAND</dt> <dd>8 kHz passband</dd>
|
||||
* <dt>#OPUS_BANDWIDTH_SUPERWIDEBAND</dt><dd>12 kHz passband</dd>
|
||||
* <dt>#OPUS_BANDWIDTH_FULLBAND</dt> <dd>20 kHz passband (default)</dd>
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_GET_MAX_BANDWIDTH(x) OPUS_GET_MAX_BANDWIDTH_REQUEST, __opus_check_int_ptr(x)
|
||||
|
||||
/** Sets the encoder's bandpass to a specific value.
|
||||
* This prevents the encoder from automatically selecting the bandpass based
|
||||
* on the available bitrate. If an application knows the bandpass of the input
|
||||
* audio it is providing, it should normally use #OPUS_SET_MAX_BANDWIDTH
|
||||
* instead, which still gives the encoder the freedom to reduce the bandpass
|
||||
* when the bitrate becomes too low, for better overall quality.
|
||||
* @see OPUS_GET_BANDWIDTH
|
||||
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
||||
* <dl>
|
||||
* <dt>#OPUS_AUTO</dt> <dd>(default)</dd>
|
||||
* <dt>#OPUS_BANDWIDTH_NARROWBAND</dt> <dd>4 kHz passband</dd>
|
||||
* <dt>#OPUS_BANDWIDTH_MEDIUMBAND</dt> <dd>6 kHz passband</dd>
|
||||
* <dt>#OPUS_BANDWIDTH_WIDEBAND</dt> <dd>8 kHz passband</dd>
|
||||
* <dt>#OPUS_BANDWIDTH_SUPERWIDEBAND</dt><dd>12 kHz passband</dd>
|
||||
* <dt>#OPUS_BANDWIDTH_FULLBAND</dt> <dd>20 kHz passband</dd>
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_SET_BANDWIDTH(x) OPUS_SET_BANDWIDTH_REQUEST, __opus_check_int(x)
|
||||
|
||||
/** Configures the type of signal being encoded.
|
||||
* This is a hint which helps the encoder's mode selection.
|
||||
* @see OPUS_GET_SIGNAL
|
||||
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
||||
* <dl>
|
||||
* <dt>#OPUS_AUTO</dt> <dd>(default)</dd>
|
||||
* <dt>#OPUS_SIGNAL_VOICE</dt><dd>Bias thresholds towards choosing LPC or Hybrid modes.</dd>
|
||||
* <dt>#OPUS_SIGNAL_MUSIC</dt><dd>Bias thresholds towards choosing MDCT modes.</dd>
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_SET_SIGNAL(x) OPUS_SET_SIGNAL_REQUEST, __opus_check_int(x)
|
||||
/** Gets the encoder's configured signal type.
|
||||
* @see OPUS_SET_SIGNAL
|
||||
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
||||
* <dl>
|
||||
* <dt>#OPUS_AUTO</dt> <dd>(default)</dd>
|
||||
* <dt>#OPUS_SIGNAL_VOICE</dt><dd>Bias thresholds towards choosing LPC or Hybrid modes.</dd>
|
||||
* <dt>#OPUS_SIGNAL_MUSIC</dt><dd>Bias thresholds towards choosing MDCT modes.</dd>
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_GET_SIGNAL(x) OPUS_GET_SIGNAL_REQUEST, __opus_check_int_ptr(x)
|
||||
|
||||
|
||||
/** Configures the encoder's intended application.
|
||||
* The initial value is a mandatory argument to the encoder_create function.
|
||||
* @see OPUS_GET_APPLICATION
|
||||
* @param[in] x <tt>opus_int32</tt>: Returns one of the following values:
|
||||
* <dl>
|
||||
* <dt>#OPUS_APPLICATION_VOIP</dt>
|
||||
* <dd>Process signal for improved speech intelligibility.</dd>
|
||||
* <dt>#OPUS_APPLICATION_AUDIO</dt>
|
||||
* <dd>Favor faithfulness to the original input.</dd>
|
||||
* <dt>#OPUS_APPLICATION_RESTRICTED_LOWDELAY</dt>
|
||||
* <dd>Configure the minimum possible coding delay by disabling certain modes
|
||||
* of operation.</dd>
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_SET_APPLICATION(x) OPUS_SET_APPLICATION_REQUEST, __opus_check_int(x)
|
||||
/** Gets the encoder's configured application.
|
||||
* @see OPUS_SET_APPLICATION
|
||||
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
||||
* <dl>
|
||||
* <dt>#OPUS_APPLICATION_VOIP</dt>
|
||||
* <dd>Process signal for improved speech intelligibility.</dd>
|
||||
* <dt>#OPUS_APPLICATION_AUDIO</dt>
|
||||
* <dd>Favor faithfulness to the original input.</dd>
|
||||
* <dt>#OPUS_APPLICATION_RESTRICTED_LOWDELAY</dt>
|
||||
* <dd>Configure the minimum possible coding delay by disabling certain modes
|
||||
* of operation.</dd>
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_GET_APPLICATION(x) OPUS_GET_APPLICATION_REQUEST, __opus_check_int_ptr(x)
|
||||
|
||||
/** Gets the total samples of delay added by the entire codec.
|
||||
* This can be queried by the encoder and then the provided number of samples can be
|
||||
* skipped on from the start of the decoder's output to provide time aligned input
|
||||
* and output. From the perspective of a decoding application the real data begins this many
|
||||
* samples late.
|
||||
*
|
||||
* The decoder contribution to this delay is identical for all decoders, but the
|
||||
* encoder portion of the delay may vary from implementation to implementation,
|
||||
* version to version, or even depend on the encoder's initial configuration.
|
||||
* Applications needing delay compensation should call this CTL rather than
|
||||
* hard-coding a value.
|
||||
* @param[out] x <tt>opus_int32 *</tt>: Number of lookahead samples
|
||||
* @hideinitializer */
|
||||
#define OPUS_GET_LOOKAHEAD(x) OPUS_GET_LOOKAHEAD_REQUEST, __opus_check_int_ptr(x)
|
||||
|
||||
/** Configures the encoder's use of inband forward error correction (FEC).
|
||||
* @note This is only applicable to the LPC layer
|
||||
* @see OPUS_GET_INBAND_FEC
|
||||
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
||||
* <dl>
|
||||
* <dt>0</dt><dd>Disable inband FEC (default).</dd>
|
||||
* <dt>1</dt><dd>Enable inband FEC.</dd>
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_SET_INBAND_FEC(x) OPUS_SET_INBAND_FEC_REQUEST, __opus_check_int(x)
|
||||
/** Gets encoder's configured use of inband forward error correction.
|
||||
* @see OPUS_SET_INBAND_FEC
|
||||
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
||||
* <dl>
|
||||
* <dt>0</dt><dd>Inband FEC disabled (default).</dd>
|
||||
* <dt>1</dt><dd>Inband FEC enabled.</dd>
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_GET_INBAND_FEC(x) OPUS_GET_INBAND_FEC_REQUEST, __opus_check_int_ptr(x)
|
||||
|
||||
/** Configures the encoder's expected packet loss percentage.
|
||||
* Higher values trigger progressively more loss resistant behavior in the encoder
|
||||
* at the expense of quality at a given bitrate in the absence of packet loss, but
|
||||
* greater quality under loss.
|
||||
* @see OPUS_GET_PACKET_LOSS_PERC
|
||||
* @param[in] x <tt>opus_int32</tt>: Loss percentage in the range 0-100, inclusive (default: 0).
|
||||
* @hideinitializer */
|
||||
#define OPUS_SET_PACKET_LOSS_PERC(x) OPUS_SET_PACKET_LOSS_PERC_REQUEST, __opus_check_int(x)
|
||||
/** Gets the encoder's configured packet loss percentage.
|
||||
* @see OPUS_SET_PACKET_LOSS_PERC
|
||||
* @param[out] x <tt>opus_int32 *</tt>: Returns the configured loss percentage
|
||||
* in the range 0-100, inclusive (default: 0).
|
||||
* @hideinitializer */
|
||||
#define OPUS_GET_PACKET_LOSS_PERC(x) OPUS_GET_PACKET_LOSS_PERC_REQUEST, __opus_check_int_ptr(x)
|
||||
|
||||
/** Configures the encoder's use of discontinuous transmission (DTX).
|
||||
* @note This is only applicable to the LPC layer
|
||||
* @see OPUS_GET_DTX
|
||||
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
||||
* <dl>
|
||||
* <dt>0</dt><dd>Disable DTX (default).</dd>
|
||||
* <dt>1</dt><dd>Enabled DTX.</dd>
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_SET_DTX(x) OPUS_SET_DTX_REQUEST, __opus_check_int(x)
|
||||
/** Gets encoder's configured use of discontinuous transmission.
|
||||
* @see OPUS_SET_DTX
|
||||
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
||||
* <dl>
|
||||
* <dt>0</dt><dd>DTX disabled (default).</dd>
|
||||
* <dt>1</dt><dd>DTX enabled.</dd>
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_GET_DTX(x) OPUS_GET_DTX_REQUEST, __opus_check_int_ptr(x)
|
||||
/** Configures the depth of signal being encoded.
|
||||
*
|
||||
* This is a hint which helps the encoder identify silence and near-silence.
|
||||
* It represents the number of significant bits of linear intensity below
|
||||
* which the signal contains ignorable quantization or other noise.
|
||||
*
|
||||
* For example, OPUS_SET_LSB_DEPTH(14) would be an appropriate setting
|
||||
* for G.711 u-law input. OPUS_SET_LSB_DEPTH(16) would be appropriate
|
||||
* for 16-bit linear pcm input with opus_encode_float().
|
||||
*
|
||||
* When using opus_encode() instead of opus_encode_float(), or when libopus
|
||||
* is compiled for fixed-point, the encoder uses the minimum of the value
|
||||
* set here and the value 16.
|
||||
*
|
||||
* @see OPUS_GET_LSB_DEPTH
|
||||
* @param[in] x <tt>opus_int32</tt>: Input precision in bits, between 8 and 24
|
||||
* (default: 24).
|
||||
* @hideinitializer */
|
||||
#define OPUS_SET_LSB_DEPTH(x) OPUS_SET_LSB_DEPTH_REQUEST, __opus_check_int(x)
|
||||
/** Gets the encoder's configured signal depth.
|
||||
* @see OPUS_SET_LSB_DEPTH
|
||||
* @param[out] x <tt>opus_int32 *</tt>: Input precision in bits, between 8 and
|
||||
* 24 (default: 24).
|
||||
* @hideinitializer */
|
||||
#define OPUS_GET_LSB_DEPTH(x) OPUS_GET_LSB_DEPTH_REQUEST, __opus_check_int_ptr(x)
|
||||
|
||||
/** Configures the encoder's use of variable duration frames.
|
||||
* When variable duration is enabled, the encoder is free to use a shorter frame
|
||||
* size than the one requested in the opus_encode*() call.
|
||||
* It is then the user's responsibility
|
||||
* to verify how much audio was encoded by checking the ToC byte of the encoded
|
||||
* packet. The part of the audio that was not encoded needs to be resent to the
|
||||
* encoder for the next call. Do not use this option unless you <b>really</b>
|
||||
* know what you are doing.
|
||||
* @see OPUS_GET_EXPERT_FRAME_DURATION
|
||||
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
||||
* <dl>
|
||||
* <dt>OPUS_FRAMESIZE_ARG</dt><dd>Select frame size from the argument (default).</dd>
|
||||
* <dt>OPUS_FRAMESIZE_2_5_MS</dt><dd>Use 2.5 ms frames.</dd>
|
||||
* <dt>OPUS_FRAMESIZE_5_MS</dt><dd>Use 5 ms frames.</dd>
|
||||
* <dt>OPUS_FRAMESIZE_10_MS</dt><dd>Use 10 ms frames.</dd>
|
||||
* <dt>OPUS_FRAMESIZE_20_MS</dt><dd>Use 20 ms frames.</dd>
|
||||
* <dt>OPUS_FRAMESIZE_40_MS</dt><dd>Use 40 ms frames.</dd>
|
||||
* <dt>OPUS_FRAMESIZE_60_MS</dt><dd>Use 60 ms frames.</dd>
|
||||
* <dt>OPUS_FRAMESIZE_80_MS</dt><dd>Use 80 ms frames.</dd>
|
||||
* <dt>OPUS_FRAMESIZE_100_MS</dt><dd>Use 100 ms frames.</dd>
|
||||
* <dt>OPUS_FRAMESIZE_120_MS</dt><dd>Use 120 ms frames.</dd>
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_SET_EXPERT_FRAME_DURATION(x) OPUS_SET_EXPERT_FRAME_DURATION_REQUEST, __opus_check_int(x)
|
||||
/** Gets the encoder's configured use of variable duration frames.
|
||||
* @see OPUS_SET_EXPERT_FRAME_DURATION
|
||||
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
||||
* <dl>
|
||||
* <dt>OPUS_FRAMESIZE_ARG</dt><dd>Select frame size from the argument (default).</dd>
|
||||
* <dt>OPUS_FRAMESIZE_2_5_MS</dt><dd>Use 2.5 ms frames.</dd>
|
||||
* <dt>OPUS_FRAMESIZE_5_MS</dt><dd>Use 5 ms frames.</dd>
|
||||
* <dt>OPUS_FRAMESIZE_10_MS</dt><dd>Use 10 ms frames.</dd>
|
||||
* <dt>OPUS_FRAMESIZE_20_MS</dt><dd>Use 20 ms frames.</dd>
|
||||
* <dt>OPUS_FRAMESIZE_40_MS</dt><dd>Use 40 ms frames.</dd>
|
||||
* <dt>OPUS_FRAMESIZE_60_MS</dt><dd>Use 60 ms frames.</dd>
|
||||
* <dt>OPUS_FRAMESIZE_80_MS</dt><dd>Use 80 ms frames.</dd>
|
||||
* <dt>OPUS_FRAMESIZE_100_MS</dt><dd>Use 100 ms frames.</dd>
|
||||
* <dt>OPUS_FRAMESIZE_120_MS</dt><dd>Use 120 ms frames.</dd>
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_GET_EXPERT_FRAME_DURATION(x) OPUS_GET_EXPERT_FRAME_DURATION_REQUEST, __opus_check_int_ptr(x)
|
||||
|
||||
/** If set to 1, disables almost all use of prediction, making frames almost
|
||||
* completely independent. This reduces quality.
|
||||
* @see OPUS_GET_PREDICTION_DISABLED
|
||||
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
||||
* <dl>
|
||||
* <dt>0</dt><dd>Enable prediction (default).</dd>
|
||||
* <dt>1</dt><dd>Disable prediction.</dd>
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_SET_PREDICTION_DISABLED(x) OPUS_SET_PREDICTION_DISABLED_REQUEST, __opus_check_int(x)
|
||||
/** Gets the encoder's configured prediction status.
|
||||
* @see OPUS_SET_PREDICTION_DISABLED
|
||||
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
||||
* <dl>
|
||||
* <dt>0</dt><dd>Prediction enabled (default).</dd>
|
||||
* <dt>1</dt><dd>Prediction disabled.</dd>
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_GET_PREDICTION_DISABLED(x) OPUS_GET_PREDICTION_DISABLED_REQUEST, __opus_check_int_ptr(x)
|
||||
|
||||
/**@}*/
|
||||
|
||||
/** @defgroup opus_genericctls Generic CTLs
|
||||
*
|
||||
* These macros are used with the \c opus_decoder_ctl and
|
||||
* \c opus_encoder_ctl calls to generate a particular
|
||||
* request.
|
||||
*
|
||||
* When called on an \c OpusDecoder they apply to that
|
||||
* particular decoder instance. When called on an
|
||||
* \c OpusEncoder they apply to the corresponding setting
|
||||
* on that encoder instance, if present.
|
||||
*
|
||||
* Some usage examples:
|
||||
*
|
||||
* @code
|
||||
* int ret;
|
||||
* opus_int32 pitch;
|
||||
* ret = opus_decoder_ctl(dec_ctx, OPUS_GET_PITCH(&pitch));
|
||||
* if (ret == OPUS_OK) return ret;
|
||||
*
|
||||
* opus_encoder_ctl(enc_ctx, OPUS_RESET_STATE);
|
||||
* opus_decoder_ctl(dec_ctx, OPUS_RESET_STATE);
|
||||
*
|
||||
* opus_int32 enc_bw, dec_bw;
|
||||
* opus_encoder_ctl(enc_ctx, OPUS_GET_BANDWIDTH(&enc_bw));
|
||||
* opus_decoder_ctl(dec_ctx, OPUS_GET_BANDWIDTH(&dec_bw));
|
||||
* if (enc_bw != dec_bw) {
|
||||
* printf("packet bandwidth mismatch!\n");
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @see opus_encoder, opus_decoder_ctl, opus_encoder_ctl, opus_decoderctls, opus_encoderctls
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** Resets the codec state to be equivalent to a freshly initialized state.
|
||||
* This should be called when switching streams in order to prevent
|
||||
* the back to back decoding from giving different results from
|
||||
* one at a time decoding.
|
||||
* @hideinitializer */
|
||||
#define OPUS_RESET_STATE 4028
|
||||
|
||||
/** Gets the final state of the codec's entropy coder.
|
||||
* This is used for testing purposes,
|
||||
* The encoder and decoder state should be identical after coding a payload
|
||||
* (assuming no data corruption or software bugs)
|
||||
*
|
||||
* @param[out] x <tt>opus_uint32 *</tt>: Entropy coder state
|
||||
*
|
||||
* @hideinitializer */
|
||||
#define OPUS_GET_FINAL_RANGE(x) OPUS_GET_FINAL_RANGE_REQUEST, __opus_check_uint_ptr(x)
|
||||
|
||||
/** Gets the encoder's configured bandpass or the decoder's last bandpass.
|
||||
* @see OPUS_SET_BANDWIDTH
|
||||
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
||||
* <dl>
|
||||
* <dt>#OPUS_AUTO</dt> <dd>(default)</dd>
|
||||
* <dt>#OPUS_BANDWIDTH_NARROWBAND</dt> <dd>4 kHz passband</dd>
|
||||
* <dt>#OPUS_BANDWIDTH_MEDIUMBAND</dt> <dd>6 kHz passband</dd>
|
||||
* <dt>#OPUS_BANDWIDTH_WIDEBAND</dt> <dd>8 kHz passband</dd>
|
||||
* <dt>#OPUS_BANDWIDTH_SUPERWIDEBAND</dt><dd>12 kHz passband</dd>
|
||||
* <dt>#OPUS_BANDWIDTH_FULLBAND</dt> <dd>20 kHz passband</dd>
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_GET_BANDWIDTH(x) OPUS_GET_BANDWIDTH_REQUEST, __opus_check_int_ptr(x)
|
||||
|
||||
/** Gets the sampling rate the encoder or decoder was initialized with.
|
||||
* This simply returns the <code>Fs</code> value passed to opus_encoder_init()
|
||||
* or opus_decoder_init().
|
||||
* @param[out] x <tt>opus_int32 *</tt>: Sampling rate of encoder or decoder.
|
||||
* @hideinitializer
|
||||
*/
|
||||
#define OPUS_GET_SAMPLE_RATE(x) OPUS_GET_SAMPLE_RATE_REQUEST, __opus_check_int_ptr(x)
|
||||
|
||||
/** If set to 1, disables the use of phase inversion for intensity stereo,
|
||||
* improving the quality of mono downmixes, but slightly reducing normal
|
||||
* stereo quality. Disabling phase inversion in the decoder does not comply
|
||||
* with RFC 6716, although it does not cause any interoperability issue and
|
||||
* is expected to become part of the Opus standard once RFC 6716 is updated
|
||||
* by draft-ietf-codec-opus-update.
|
||||
* @see OPUS_GET_PHASE_INVERSION_DISABLED
|
||||
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
||||
* <dl>
|
||||
* <dt>0</dt><dd>Enable phase inversion (default).</dd>
|
||||
* <dt>1</dt><dd>Disable phase inversion.</dd>
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_SET_PHASE_INVERSION_DISABLED(x) OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST, __opus_check_int(x)
|
||||
/** Gets the encoder's configured phase inversion status.
|
||||
* @see OPUS_SET_PHASE_INVERSION_DISABLED
|
||||
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
||||
* <dl>
|
||||
* <dt>0</dt><dd>Stereo phase inversion enabled (default).</dd>
|
||||
* <dt>1</dt><dd>Stereo phase inversion disabled.</dd>
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_GET_PHASE_INVERSION_DISABLED(x) OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST, __opus_check_int_ptr(x)
|
||||
/** Gets the DTX state of the encoder.
|
||||
* Returns whether the last encoded frame was either a comfort noise update
|
||||
* during DTX or not encoded because of DTX.
|
||||
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
||||
* <dl>
|
||||
* <dt>0</dt><dd>The encoder is not in DTX.</dd>
|
||||
* <dt>1</dt><dd>The encoder is in DTX.</dd>
|
||||
* </dl>
|
||||
* @hideinitializer */
|
||||
#define OPUS_GET_IN_DTX(x) OPUS_GET_IN_DTX_REQUEST, __opus_check_int_ptr(x)
|
||||
|
||||
/**@}*/
|
||||
|
||||
/** @defgroup opus_decoderctls Decoder related CTLs
|
||||
* @see opus_genericctls, opus_encoderctls, opus_decoder
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** Configures decoder gain adjustment.
|
||||
* Scales the decoded output by a factor specified in Q8 dB units.
|
||||
* This has a maximum range of -32768 to 32767 inclusive, and returns
|
||||
* OPUS_BAD_ARG otherwise. The default is zero indicating no adjustment.
|
||||
* This setting survives decoder reset.
|
||||
*
|
||||
* gain = pow(10, x/(20.0*256))
|
||||
*
|
||||
* @param[in] x <tt>opus_int32</tt>: Amount to scale PCM signal by in Q8 dB units.
|
||||
* @hideinitializer */
|
||||
#define OPUS_SET_GAIN(x) OPUS_SET_GAIN_REQUEST, __opus_check_int(x)
|
||||
/** Gets the decoder's configured gain adjustment. @see OPUS_SET_GAIN
|
||||
*
|
||||
* @param[out] x <tt>opus_int32 *</tt>: Amount to scale PCM signal by in Q8 dB units.
|
||||
* @hideinitializer */
|
||||
#define OPUS_GET_GAIN(x) OPUS_GET_GAIN_REQUEST, __opus_check_int_ptr(x)
|
||||
|
||||
/** Gets the duration (in samples) of the last packet successfully decoded or concealed.
|
||||
* @param[out] x <tt>opus_int32 *</tt>: Number of samples (at current sampling rate).
|
||||
* @hideinitializer */
|
||||
#define OPUS_GET_LAST_PACKET_DURATION(x) OPUS_GET_LAST_PACKET_DURATION_REQUEST, __opus_check_int_ptr(x)
|
||||
|
||||
/** Gets the pitch of the last decoded frame, if available.
|
||||
* This can be used for any post-processing algorithm requiring the use of pitch,
|
||||
* e.g. time stretching/shortening. If the last frame was not voiced, or if the
|
||||
* pitch was not coded in the frame, then zero is returned.
|
||||
*
|
||||
* This CTL is only implemented for decoder instances.
|
||||
*
|
||||
* @param[out] x <tt>opus_int32 *</tt>: pitch period at 48 kHz (or 0 if not available)
|
||||
*
|
||||
* @hideinitializer */
|
||||
#define OPUS_GET_PITCH(x) OPUS_GET_PITCH_REQUEST, __opus_check_int_ptr(x)
|
||||
|
||||
/**@}*/
|
||||
|
||||
/** @defgroup opus_libinfo Opus library information functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** Converts an opus error code into a human readable string.
|
||||
*
|
||||
* @param[in] error <tt>int</tt>: Error number
|
||||
* @returns Error string
|
||||
*/
|
||||
OPUS_EXPORT const char *opus_strerror(int error);
|
||||
|
||||
/** Gets the libopus version string.
|
||||
*
|
||||
* Applications may look for the substring "-fixed" in the version string to
|
||||
* determine whether they have a fixed-point or floating-point build at
|
||||
* runtime.
|
||||
*
|
||||
* @returns Version string
|
||||
*/
|
||||
OPUS_EXPORT const char *opus_get_version_string(void);
|
||||
/**@}*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OPUS_DEFINES_H */
|
||||
Executable
+166
@@ -0,0 +1,166 @@
|
||||
/* (C) COPYRIGHT 1994-2002 Xiph.Org Foundation */
|
||||
/* Modified by Jean-Marc Valin */
|
||||
/*
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
/* opus_types.h based on ogg_types.h from libogg */
|
||||
|
||||
/**
|
||||
@file opus_types.h
|
||||
@brief Opus reference implementation types
|
||||
*/
|
||||
#ifndef OPUS_TYPES_H
|
||||
#define OPUS_TYPES_H
|
||||
|
||||
#define opus_int int /* used for counters etc; at least 16 bits */
|
||||
#define opus_int64 long long
|
||||
#define opus_int8 signed char
|
||||
|
||||
#define opus_uint unsigned int /* used for counters etc; at least 16 bits */
|
||||
#define opus_uint64 unsigned long long
|
||||
#define opus_uint8 unsigned char
|
||||
|
||||
/* Use the real stdint.h if it's there (taken from Paul Hsieh's pstdint.h) */
|
||||
#if (defined(__STDC__) && __STDC__ && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_)) || defined (HAVE_STDINT_H))
|
||||
#include <stdint.h>
|
||||
# undef opus_int64
|
||||
# undef opus_int8
|
||||
# undef opus_uint64
|
||||
# undef opus_uint8
|
||||
typedef int8_t opus_int8;
|
||||
typedef uint8_t opus_uint8;
|
||||
typedef int16_t opus_int16;
|
||||
typedef uint16_t opus_uint16;
|
||||
typedef int32_t opus_int32;
|
||||
typedef uint32_t opus_uint32;
|
||||
typedef int64_t opus_int64;
|
||||
typedef uint64_t opus_uint64;
|
||||
#elif defined(_WIN32)
|
||||
|
||||
# if defined(__CYGWIN__)
|
||||
# include <_G_config.h>
|
||||
typedef _G_int32_t opus_int32;
|
||||
typedef _G_uint32_t opus_uint32;
|
||||
typedef _G_int16 opus_int16;
|
||||
typedef _G_uint16 opus_uint16;
|
||||
# elif defined(__MINGW32__)
|
||||
typedef short opus_int16;
|
||||
typedef unsigned short opus_uint16;
|
||||
typedef int opus_int32;
|
||||
typedef unsigned int opus_uint32;
|
||||
# elif defined(__MWERKS__)
|
||||
typedef int opus_int32;
|
||||
typedef unsigned int opus_uint32;
|
||||
typedef short opus_int16;
|
||||
typedef unsigned short opus_uint16;
|
||||
# else
|
||||
/* MSVC/Borland */
|
||||
typedef __int32 opus_int32;
|
||||
typedef unsigned __int32 opus_uint32;
|
||||
typedef __int16 opus_int16;
|
||||
typedef unsigned __int16 opus_uint16;
|
||||
# endif
|
||||
|
||||
#elif defined(__MACOS__)
|
||||
|
||||
# include <sys/types.h>
|
||||
typedef SInt16 opus_int16;
|
||||
typedef UInt16 opus_uint16;
|
||||
typedef SInt32 opus_int32;
|
||||
typedef UInt32 opus_uint32;
|
||||
|
||||
#elif (defined(__APPLE__) && defined(__MACH__)) /* MacOS X Framework build */
|
||||
|
||||
# include <sys/types.h>
|
||||
typedef int16_t opus_int16;
|
||||
typedef u_int16_t opus_uint16;
|
||||
typedef int32_t opus_int32;
|
||||
typedef u_int32_t opus_uint32;
|
||||
|
||||
#elif defined(__BEOS__)
|
||||
|
||||
/* Be */
|
||||
# include <inttypes.h>
|
||||
typedef int16 opus_int16;
|
||||
typedef u_int16 opus_uint16;
|
||||
typedef int32_t opus_int32;
|
||||
typedef u_int32_t opus_uint32;
|
||||
|
||||
#elif defined (__EMX__)
|
||||
|
||||
/* OS/2 GCC */
|
||||
typedef short opus_int16;
|
||||
typedef unsigned short opus_uint16;
|
||||
typedef int opus_int32;
|
||||
typedef unsigned int opus_uint32;
|
||||
|
||||
#elif defined (DJGPP)
|
||||
|
||||
/* DJGPP */
|
||||
typedef short opus_int16;
|
||||
typedef unsigned short opus_uint16;
|
||||
typedef int opus_int32;
|
||||
typedef unsigned int opus_uint32;
|
||||
|
||||
#elif defined(R5900)
|
||||
|
||||
/* PS2 EE */
|
||||
typedef int opus_int32;
|
||||
typedef unsigned opus_uint32;
|
||||
typedef short opus_int16;
|
||||
typedef unsigned short opus_uint16;
|
||||
|
||||
#elif defined(__SYMBIAN32__)
|
||||
|
||||
/* Symbian GCC */
|
||||
typedef signed short opus_int16;
|
||||
typedef unsigned short opus_uint16;
|
||||
typedef signed int opus_int32;
|
||||
typedef unsigned int opus_uint32;
|
||||
|
||||
#elif defined(CONFIG_TI_C54X) || defined (CONFIG_TI_C55X)
|
||||
|
||||
typedef short opus_int16;
|
||||
typedef unsigned short opus_uint16;
|
||||
typedef long opus_int32;
|
||||
typedef unsigned long opus_uint32;
|
||||
|
||||
#elif defined(CONFIG_TI_C6X)
|
||||
|
||||
typedef short opus_int16;
|
||||
typedef unsigned short opus_uint16;
|
||||
typedef int opus_int32;
|
||||
typedef unsigned int opus_uint32;
|
||||
|
||||
#else
|
||||
|
||||
/* Give up, take a reasonable guess */
|
||||
typedef short opus_int16;
|
||||
typedef unsigned short opus_uint16;
|
||||
typedef int opus_int32;
|
||||
typedef unsigned int opus_uint32;
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* OPUS_TYPES_H */
|
||||
+3
-3
@@ -26,7 +26,7 @@
|
||||
|
||||
int speed = 0;
|
||||
|
||||
#define MAXSPDARR 5
|
||||
#define MAXSPDARR 50
|
||||
int spdarr[MAXSPDARR];
|
||||
int spdarrbps[MAXSPDARR];
|
||||
|
||||
@@ -82,12 +82,12 @@ static int f=1;
|
||||
|
||||
int meanvalbps(int v)
|
||||
{
|
||||
static int f = 1;
|
||||
static int f = 5;
|
||||
|
||||
if (f)
|
||||
{
|
||||
f--;
|
||||
for (int i = 0; i < MAXSPDARR; i++) spdarrbps[i] = -1;
|
||||
f = 0;
|
||||
}
|
||||
|
||||
for (int i = (MAXSPDARR - 1); i > 0; i--)
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
|
||||
FILE........: version.h
|
||||
AUTHOR......: Tomas Härdin
|
||||
DATE CREATED: 03 November 2017
|
||||
|
||||
Codec 2 VERSION #defines
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
Copyright (C) 2017 Tomas Härdin
|
||||
|
||||
All rights reserved.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License version 2.1, as
|
||||
published by the Free Software Foundation. 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 Lesser General Public License
|
||||
along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//this functions both as an include guard and your typical HAVE macro
|
||||
#ifndef CODEC2_HAVE_VERSION
|
||||
#define CODEC2_HAVE_VERSION
|
||||
|
||||
#define CODEC2_VERSION_MAJOR 0
|
||||
#define CODEC2_VERSION_MINOR 9
|
||||
#define CODEC2_VERSION_PATCH 2
|
||||
#define CODEC2_VERSION "0.9.2"
|
||||
|
||||
#endif //CODEC2_HAVE_VERSION
|
||||
Executable
+252
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* 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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user