This commit is contained in:
Kurt 2021-01-18 15:36:29 +01:00
parent 46faf838bc
commit 43cea876bb
34 changed files with 335 additions and 1977 deletions

View File

@ -3,7 +3,7 @@ The purpose of this project is to transfer data (pictures...) via a 2,7kHz SSB c
Now also including RTTY mode. Now also including RTTY mode.
# this is work in progress # this is work in progress
Version 0.64 Version 0.72
Windows 10 (should work on Win7, not tested) Windows 10 (should work on Win7, not tested)
linux Desktop PC, linux Desktop PC,
Odroid SBC Odroid SBC
@ -18,7 +18,7 @@ Raspberry 4 (3B+)
* Odroid C2 ... working * Odroid C2 ... working
* Odroid C4 ... working * Odroid C4 ... working
* Raspberry: Raspian OS ist NOT working, instead Ubuntu 64bit is required * Raspberry: Raspian OS ist NOT working, instead Ubuntu 64bit is required. Or use the Beta Rapi-IO-64bit
* Application Software "oscardata.exe" running on Windows, Linux, (possibly MAC-OS, not tested) * Application Software "oscardata.exe" running on Windows, Linux, (possibly MAC-OS, not tested)
@ -29,11 +29,12 @@ this software uses these programs:
* libsoundio: https://github.com/andrewrk/libsoundio (MIT License) * libsoundio: https://github.com/andrewrk/libsoundio (MIT License)
* fftw3: http://www.fftw.org (GPL V.2 or later) * fftw3: http://www.fftw.org (GPL V.2 or later)
* libcodec2: https://github.com/drowe67/codec2 (LGPL 2.1, Linux: standard lib, Windows: from freeDV) * libcodec2: https://github.com/drowe67/codec2 (LGPL 2.1, Linux: standard lib, Windows: from freeDV)
* portaudio: https://github.com/PortAudio/portaudio
# Download alternatives # Download alternatives
* download from github and build from source * download from github and build from source
* download windows installer and download Odroid and Raspberry images: * download windows installer and download Odroid and Raspberry images:
https://dj0abr.de/german/technik/sat/modem/images.htm https://hsmodem.dj0abr.de (Download section)
# building the software for Linux # building the software for Linux

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -760,8 +760,8 @@ item: Install File
Flags=0000000010000010 Flags=0000000010000010
end end
item: Install File item: Install File
Source=c:\tmp\WinRelease\libsoundio.dll Source=c:\tmp\WinRelease\portaudio_x86.dll
Destination=%MAINDIR%\libsoundio.dll Destination=%MAINDIR%\portaudio_x86.dll
Flags=0000000010000010 Flags=0000000010000010
end end
item: Install File item: Install File

View File

@ -29,4 +29,5 @@ default: $(OBJ)
clean: clean:
rm -f *.o rm -f *.o
rm -f libkmaudio/*.o
rm -f libkmaudio/*.o

View File

@ -92,7 +92,6 @@ void playAudioPCM(char* fn, int destination)
{ {
int to = 4000; int to = 4000;
int res; int res;
//while ((res = io_ls_fifo_usedspace()) > 10000)
while ((res = io_fifo_usedspace(voice_pbidx)) > 10000) while ((res = io_fifo_usedspace(voice_pbidx)) > 10000)
{ {
if (--to == 0) if (--to == 0)

View File

@ -88,14 +88,18 @@ int marker = 1;
int init_voice_result = 0; int init_voice_result = 0;
// number of audio device in libkmaudio // number of audio device in libkmaudio
int io_capidx = 0; int io_capidx = -1;
int io_pbidx = 0; int io_pbidx = -1;
int voice_capidx = 0; int voice_capidx = -1;
int voice_pbidx = 0; int voice_pbidx = -1;
int safemode = 0; int safemode = 0;
int sendIntro = 0; int sendIntro = 0;
char mycallsign[21];
char myqthloc[11];
char myname[21];
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
int opt = 0; int opt = 0;
@ -239,7 +243,7 @@ int main(int argc, char* argv[])
{ {
// loop voice mic to LS, and record into PCM file // loop voice mic to LS, and record into PCM file
float f[1100]; float f[1100];
while (1) while (keeprunning)
{ {
int anz = kmaudio_readsamples(voice_capidx, f, 1000, micvol,0); int anz = kmaudio_readsamples(voice_capidx, f, 1000, micvol,0);
if (anz > 0) if (anz > 0)
@ -324,7 +328,8 @@ typedef struct {
} SPEEDRATE; } SPEEDRATE;
// AudioRate, TX-Resampler, RX-Resampler/4, bit/symbol, Codec-Rate // AudioRate, TX-Resampler, RX-Resampler/4, bit/symbol, Codec-Rate
SPEEDRATE sr[11] = { #define NUMSPEEDMODES 11
SPEEDRATE sr[NUMSPEEDMODES] = {
// BPSK modes // BPSK modes
{48000, 40,10, 1, 1200, 800}, {48000, 40,10, 1, 1200, 800},
{48000, 20, 5, 1, 2400, 2000}, {48000, 20, 5, 1, 2400, 2000},
@ -351,6 +356,8 @@ void startModem()
close_dsp(); close_dsp();
close_rtty(); close_rtty();
speedmode = set_speedmode; speedmode = set_speedmode;
if (speedmode < 0 || speedmode >= NUMSPEEDMODES)
speedmode = 4;
bitsPerSymbol = sr[speedmode].bpsym; bitsPerSymbol = sr[speedmode].bpsym;
constellationSize = (1 << bitsPerSymbol); // QPSK=4, 8PSK=8 constellationSize = (1 << bitsPerSymbol); // QPSK=4, 8PSK=8
@ -398,12 +405,32 @@ void initVoice()
float f = 0.0f; float f = 0.0f;
io_saveStream(&f, 1); // close recording io_saveStream(&f, 1); // close recording
close_voiceproc(); close_voiceproc();
close_stream(voice_capidx);
close_stream(voice_pbidx);
} }
else else
{ {
int srate = VOICE_SAMPRATE;
voice_capidx = kmaudio_startCapture(micDeviceName, VOICE_SAMPRATE);
voice_pbidx = kmaudio_startPlayback(lsDeviceName, VOICE_SAMPRATE); // voice always runs with 48000 with one exception:
// if it is used for monitoring only and the digital audio
// stream runs with 44100, then also the monitoring voice audio
// must runs with 44100
if (VoiceAudioMode == VOICEMODE_LISTENAUDIOIN && caprate == 44100)
srate = 44100;
voice_capidx = kmaudio_startCapture(micDeviceName, srate);
if (voice_capidx == -1)
{
printf("Voice CAP: cannot open device: %s\n", micDeviceName);
return;
}
voice_pbidx = kmaudio_startPlayback(lsDeviceName, srate);
if (voice_pbidx == -1)
{
printf("Voice PB: cannot open device: %s\n", lsDeviceName);
return;
}
init_voiceproc(); init_voiceproc();
} }
} }
@ -439,12 +466,27 @@ void io_setAudioDevices(uint8_t pbvol, uint8_t capvol, uint8_t announce, uint8_t
} }
} }
uint8_t *getDevList(int* plen)
{
uint8_t* txdata = io_getAudioDevicelist(plen);
txdata[0] = 3; // ID of this UDP message
txdata[1] = (io_capidx != -1 && devlist[io_capidx].working) ? '1' : '0';
txdata[2] = (io_pbidx != -1 && devlist[io_pbidx].working) ? '1' : '0';
txdata[3] = (voice_capidx != -1 && devlist[voice_capidx].working) ? '1' : '0';
txdata[4] = (voice_pbidx != -1 && devlist[voice_pbidx].working) ? '1' : '0';
return txdata;
}
// called from UDP RX thread for Broadcast-search from App // called from UDP RX thread for Broadcast-search from App
void bc_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock) void bc_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
{ {
static uint64_t lastms = 0; // time of last received BC message static uint64_t lastms = 0; // time of last received BC message
uint64_t actms = getms(); uint64_t actms = getms();
if (len > 0 && pdata[0] == 0x3c) if (len > 0 && pdata[0] == 0x3c)
{ {
/* searchmodem message received /* searchmodem message received
@ -462,6 +504,9 @@ void bc_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
* 9 ... unused * 9 ... unused
* 10 .. 109 ... PB device name * 10 .. 109 ... PB device name
* 110 .. 209 ... CAP device name * 110 .. 209 ... CAP device name
* 210 .. 229 ... Callsign
* 230 .. 239 ... qthloc
* 240 .. 259 ... Name
*/ */
char rxip[20]; char rxip[20];
@ -491,7 +536,7 @@ void bc_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
// App searches for the modem IP, mirror the received messages // App searches for the modem IP, mirror the received messages
// so the app gets an UDP message with this local IP // so the app gets an UDP message with this local IP
int alen; int alen;
uint8_t* txdata = io_getAudioDevicelist(&alen); uint8_t* txdata = getDevList(&alen);
sendUDP(appIP, UdpDataPort_ModemToApp, txdata, alen); sendUDP(appIP, UdpDataPort_ModemToApp, txdata, alen);
} }
else else
@ -503,13 +548,24 @@ void bc_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
// App searches for the modem IP, mirror the received messages // App searches for the modem IP, mirror the received messages
// so the app gets an UDP message with this local IP // so the app gets an UDP message with this local IP
int alen; int alen;
uint8_t* txdata = io_getAudioDevicelist(&alen); uint8_t* txdata = getDevList(&alen);
sendUDP(appIP, UdpDataPort_ModemToApp, txdata, alen); sendUDP(appIP, UdpDataPort_ModemToApp, txdata, alen);
} }
else else
return; return;
} }
memcpy(mycallsign, pdata + 210, sizeof(mycallsign));
mycallsign[sizeof(mycallsign) - 1] = 0;
memcpy(myqthloc, pdata + 230, sizeof(myqthloc));
myqthloc[sizeof(myqthloc) - 1] = 0;
memcpy(myname, pdata + 240, sizeof(myname));
myname[sizeof(myname) - 1] = 0;
//printf("<%s> <%s> <%s>\n", mycallsign, myqthloc, myname);
//printf("%d %d %d %d %d %d %d \n",pdata[1], pdata[2], pdata[3], pdata[4], pdata[5], pdata[6], pdata[7]); //printf("%d %d %d %d %d %d %d \n",pdata[1], pdata[2], pdata[3], pdata[4], pdata[5], pdata[6], pdata[7]);
io_setAudioDevices(pdata[1], pdata[2], pdata[3], pdata[4], pdata[5], (char*)(pdata + 10), (char*)(pdata + 110)); io_setAudioDevices(pdata[1], pdata[2], pdata[3], pdata[4], pdata[5], (char*)(pdata + 10), (char*)(pdata + 110));
safemode = pdata[6]; safemode = pdata[6];
@ -629,6 +685,18 @@ void appdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
//printf("LS:<%s> MIC:<%s> Mode:%d codec:%d\n", lsDeviceName, micDeviceName, VoiceAudioMode, codec); //printf("LS:<%s> MIC:<%s> Mode:%d codec:%d\n", lsDeviceName, micDeviceName, VoiceAudioMode, codec);
init_voice = 1; init_voice = 1;
if (!strcmp(micDeviceName, captureDeviceName))
{
printf("capture device already in use, ignoring: %s\n", micDeviceName);
init_voice = 0;
}
if (!strcmp(lsDeviceName, playbackDeviceName))
{
printf("playback device already in use, ignoring: %s\n", lsDeviceName);
init_voice = 0;
}
return; return;
} }
@ -734,10 +802,11 @@ void appdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
// one frame has 258 bytes, so we need for 6s: 6* ((caprate/txinterpolfactor) * bitsPerSymbol / 8) /258 + 1 frames // one frame has 258 bytes, so we need for 6s: 6* ((caprate/txinterpolfactor) * bitsPerSymbol / 8) /258 + 1 frames
toGR_sendData(pdata + 2, type, minfo,0); toGR_sendData(pdata + 2, type, minfo,0);
int numframespreamble = 6 * ((caprate / txinterpolfactor) * bitsPerSymbol / 8) / 258 + 1; int numframespreamble = 6 * ((caprate / txinterpolfactor) * bitsPerSymbol / 8) / 258 + 1;
if (type == 1)// BER Test //if (type == 1)// BER Test
numframespreamble = 1; // numframespreamble = 1;
for (int i = 0; i < numframespreamble; i++) for (int i = 0; i < numframespreamble; i++)
toGR_sendData(pdata + 2, type, minfo,1); toGR_sendData(pdata + 2, type, minfo,1);
sendStationInfo();
} }
else if ((len - 2) < PAYLOADLEN) else if ((len - 2) < PAYLOADLEN)
{ {
@ -781,11 +850,29 @@ void toGR_sendData(uint8_t* data, int type, int status, int repeat)
int len = 0; int len = 0;
uint8_t* txdata = Pack(data, type, status, &len, repeat); uint8_t* txdata = Pack(data, type, status, &len, repeat);
//showbytestring((char *)"BERtx: ", txdata, len); //showbytestring((char *)"TX: ", txdata, len);
if (txdata != NULL) sendToModulator(txdata, len); if (txdata != NULL) sendToModulator(txdata, len);
} }
void sendStationInfo()
{
uint8_t payload[PAYLOADLEN];
memcpy(payload, mycallsign, 20);
memcpy(payload+20, myqthloc, 10);
memcpy(payload+30, myname, 20);
int len = 0;
uint8_t* txdata = Pack(payload, 7, 1, &len, 1);
//showbytestring((char *)"TX Userinfo: ", txdata, len);
for (int i = 0; i < 2; i++)
{
if (txdata != NULL) sendToModulator(txdata, len);
}
}
// called by liquid demodulator for received data // called by liquid demodulator for received data
void GRdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock) void GRdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
{ {
@ -797,6 +884,7 @@ void GRdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
if (pl != NULL) if (pl != NULL)
{ {
// complete frame received // complete frame received
//printf("type:%d\n", pl[0]);
// send payload to app // send payload to app
uint8_t txpl[PAYLOADLEN + 10 + 1]; uint8_t txpl[PAYLOADLEN + 10 + 1];
memcpy(txpl + 1, pl, PAYLOADLEN + 10); memcpy(txpl + 1, pl, PAYLOADLEN + 10);
@ -862,7 +950,7 @@ void GRdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
rx_in_sync = 0; rx_in_sync = 0;
if (speedmode < 10) if (speedmode < 10)
{ {
printf("no signal detected, reset RX modem\n"); //printf("no signal detected, reset RX modem\n");
resetModem(); resetModem();
} }
lasttime = acttm; lasttime = acttm;

View File

@ -3,6 +3,7 @@
#ifdef _WIN32 #ifdef _WIN32
#define _WIN32_ #define _WIN32_
// ignore senseless warnings invented by M$ to confuse developers // ignore senseless warnings invented by M$ to confuse developers
// I love LINUX :-) which works 100000x better than Windows
#pragma warning( disable : 4091 ) #pragma warning( disable : 4091 )
#pragma warning( disable : 4003 ) #pragma warning( disable : 4003 )
#else #else
@ -230,6 +231,7 @@ void clear_rtty_txfifo();
void fmtest(); void fmtest();
void rtty_init_pipes(); void rtty_init_pipes();
void initVoice(); void initVoice();
void sendStationInfo();
extern int speedmode; extern int speedmode;

View File

@ -179,13 +179,33 @@ uint8_t* io_getAudioDevicelist(int* len);
*/ */
uint8_t kmaudio_maxlevel(int id); uint8_t kmaudio_maxlevel(int id);
/*
* closes a stream which was started by
* kmaudio_startCapture or kmaudio_startPlayback
* id ... stream ID which was returned by kmaudio_startCapture or kmaudio_startPlayback
*/
void close_stream(int id);
/*
* handle the FIFO which is used to buffer audio data
* pipenum ... stream ID which was returned by kmaudio_startCapture or kmaudio_startPlayback
* IMPORTANT: this information MUST be used to synchonize the data flow into
* the fifo. The speed is always defined by the audio sample rate
* by checking the fifo an application knows when it has to put more audio samples
* into the fifo
*/
// returns number of remaining elements (audio 16 bit short values)
int io_fifo_freespace(int pipenum); int io_fifo_freespace(int pipenum);
// returns number of used elements (audio 16 bit short values)
int io_fifo_usedspace(int pipenum); int io_fifo_usedspace(int pipenum);
void io_fifo_clear(int pipenum);
// like before, but returns a number between 0 and 100 %
int io_fifo_usedpercent(int pipenum); int io_fifo_usedpercent(int pipenum);
// clear the fifo
void io_fifo_clear(int pipenum);
// -------- functions for internal use only -------- // -------- functions for internal use only --------
@ -237,6 +257,8 @@ uint64_t getms();
void init_maxarray(); void init_maxarray();
void kmaudio_detectDropouts(int id); void kmaudio_detectDropouts(int id);
int io_read_fifo_num_short(int pipenum, int16_t* data, int num); int io_read_fifo_num_short(int pipenum, int16_t* data, int num);
void close_capture_stream(int idx);
void close_playback_stream(int idx);
extern DEVLIST devlist[MAXDEVICES]; extern DEVLIST devlist[MAXDEVICES];
extern int devanz; extern int devanz;

View File

@ -40,6 +40,15 @@ int recordCallback(const void* inputBuffer, void* outputBuffer,
PaStreamCallbackFlags statusFlags, PaStreamCallbackFlags statusFlags,
void* userData); void* userData);
void close_capture_stream(int idx)
{
if (devlist[idx].capStream != NULL)
{
Pa_CloseStream(devlist[idx].capStream);
devlist[idx].capStream = NULL;
}
}
int kmaudio_startCapture(char* devname, int samprate) int kmaudio_startCapture(char* devname, int samprate)
{ {
printf("Start request for CAP stream:%s\n", devname); printf("Start request for CAP stream:%s\n", devname);
@ -59,12 +68,8 @@ int kmaudio_startCapture(char* devname, int samprate)
devlist[idx].working = 0; devlist[idx].working = 0;
if (devlist[idx].capStream != NULL) close_capture_stream(idx);
{
printf("Closing old CAP stream:%s [%d]\n", devname, idx);
Pa_CloseStream(devlist[idx].capStream);
devlist[idx].capStream = NULL;
}
printf("Starting CAP stream:%s [%d]\n", devname, idx); printf("Starting CAP stream:%s [%d]\n", devname, idx);
io_fifo_clear(idx); io_fifo_clear(idx);

View File

@ -112,6 +112,15 @@ void overflow_callback(struct SoundIoInStream* instream)
printf("overflow %d\n", ++count); printf("overflow %d\n", ++count);
} }
void close_capture_stream(int idx)
{
if (devlist[idx].instream != NULL)
{
soundio_instream_destroy(devlist[idx].instream);
devlist[idx].instream = NULL;
}
}
int kmaudio_startCapture(char* devname, int samprate) int kmaudio_startCapture(char* devname, int samprate)
{ {
printf("Start request for CAP stream:%s\n", devname); printf("Start request for CAP stream:%s\n", devname);
@ -127,12 +136,8 @@ int kmaudio_startCapture(char* devname, int samprate)
if (capdevid == NULL) return -1; if (capdevid == NULL) return -1;
// if an old stream is open, close it // if an old stream is open, close it
if (devlist[idx].instream != NULL) close_capture_stream(idx);
{
printf("Closing old CAP stream:%s [%d]\n", devname, idx);
soundio_instream_destroy(devlist[idx].instream);
devlist[idx].instream = NULL;
}
printf("Starting CAP stream:%s [%d]\n", devname, idx); printf("Starting CAP stream:%s [%d]\n", devname, idx);
io_fifo_clear(idx); io_fifo_clear(idx);

View File

@ -82,6 +82,8 @@ void init_pipes()
// ignore data if the fifo is full // ignore data if the fifo is full
void io_write_fifo(int pipenum, float sample) void io_write_fifo(int pipenum, float sample)
{ {
if (pipenum < 0 || pipenum >= NUM_OF_PIPES) return;
LOCK(pipenum); LOCK(pipenum);
if (((io_wridx[pipenum] + 1) % AUDIO_FIFOFLEN) == io_rdidx[pipenum]) if (((io_wridx[pipenum] + 1) % AUDIO_FIFOFLEN) == io_rdidx[pipenum])
{ {
@ -98,6 +100,8 @@ void io_write_fifo(int pipenum, float sample)
void io_write_fifo_short(int pipenum, int16_t sample) void io_write_fifo_short(int pipenum, int16_t sample)
{ {
if (pipenum < 0 || pipenum >= NUM_OF_PIPES) return;
LOCK(pipenum); LOCK(pipenum);
if (((io_wridx[pipenum] + 1) % AUDIO_FIFOFLEN) == io_rdidx[pipenum]) if (((io_wridx[pipenum] + 1) % AUDIO_FIFOFLEN) == io_rdidx[pipenum])
{ {
@ -114,6 +118,8 @@ void io_write_fifo_short(int pipenum, int16_t sample)
int io_read_fifo(int pipenum, float* data) int io_read_fifo(int pipenum, float* data)
{ {
if (pipenum < 0 || pipenum >= NUM_OF_PIPES) return 0;
LOCK(pipenum); LOCK(pipenum);
if (io_rdidx[pipenum] == io_wridx[pipenum]) if (io_rdidx[pipenum] == io_wridx[pipenum])
@ -136,6 +142,8 @@ int io_read_fifo(int pipenum, float* data)
// if num elems not avail, return all what fifo has stored // if num elems not avail, return all what fifo has stored
int io_read_fifo_num(int pipenum, float* data, int num) int io_read_fifo_num(int pipenum, float* data, int num)
{ {
if (pipenum < 0 || pipenum >= NUM_OF_PIPES) return 0;
LOCK(pipenum); LOCK(pipenum);
int elemInFifo = (io_wridx[pipenum] + AUDIO_FIFOFLEN - io_rdidx[pipenum]) % AUDIO_FIFOFLEN; int elemInFifo = (io_wridx[pipenum] + AUDIO_FIFOFLEN - io_rdidx[pipenum]) % AUDIO_FIFOFLEN;
@ -166,6 +174,8 @@ int io_read_fifo_num(int pipenum, float* data, int num)
int io_read_fifo_num_short(int pipenum, int16_t* data, int num) int io_read_fifo_num_short(int pipenum, int16_t* data, int num)
{ {
if (pipenum < 0 || pipenum >= NUM_OF_PIPES) return 0;
LOCK(pipenum); LOCK(pipenum);
int elemInFifo = (io_wridx[pipenum] + AUDIO_FIFOFLEN - io_rdidx[pipenum]) % AUDIO_FIFOFLEN; int elemInFifo = (io_wridx[pipenum] + AUDIO_FIFOFLEN - io_rdidx[pipenum]) % AUDIO_FIFOFLEN;
@ -193,11 +203,15 @@ int io_read_fifo_num_short(int pipenum, int16_t* data, int num)
void io_fifo_clear(int pipenum) void io_fifo_clear(int pipenum)
{ {
if (pipenum < 0 || pipenum >= NUM_OF_PIPES) return;
io_wridx[pipenum] = io_rdidx[pipenum] = 0; io_wridx[pipenum] = io_rdidx[pipenum] = 0;
} }
int io_fifo_freespace(int pipenum) int io_fifo_freespace(int pipenum)
{ {
if (pipenum < 0 || pipenum >= NUM_OF_PIPES) return 0;
int freebuf = 0; int freebuf = 0;
LOCK(pipenum); LOCK(pipenum);
@ -211,6 +225,8 @@ int io_fifo_freespace(int pipenum)
int io_fifo_elems_avail(int pipenum) int io_fifo_elems_avail(int pipenum)
{ {
if (pipenum < 0 || pipenum >= NUM_OF_PIPES) return 0;
int elems = 0; int elems = 0;
LOCK(pipenum); LOCK(pipenum);

View File

@ -235,15 +235,17 @@ int getRealSamprate(int idx)
// build string of audio device name, to be sent to application as response to Broadcast search // build string of audio device name, to be sent to application as response to Broadcast search
// starting with PB devices, sperarator ^, capture devices // starting with PB devices, sperarator ^, capture devices
// separator between devices: ~ // separator between devices: ~
// the first character is 0 or 1 and does not belong to the device name
// it shows if this device was sucessfully started and is currently running (="1")
#define MAXDEVSTRLEN (MAXDEVICES * (MAXDEVNAMELENGTH + 2) + 10) #define MAXDEVSTRLEN (MAXDEVICES * (MAXDEVNAMELENGTH + 2) + 10)
uint8_t io_devstring[MAXDEVSTRLEN]; uint8_t io_devstring[MAXDEVSTRLEN];
void io_buildAudioDevString() void io_buildAudioDevString()
{ {
memset(io_devstring, 0, sizeof(io_devstring)); memset(io_devstring, 0, sizeof(io_devstring));
io_devstring[0] = ' '; // placeholder for ID for this UDP message io_devstring[0] = ' '; // placeholder for other data
io_devstring[1] = ' ';
io_devstring[2] = ' ';
io_devstring[3] = ' ';
io_devstring[4] = ' ';
// playback devices // playback devices
for (int i = 0; i < devanz; i++) for (int i = 0; i < devanz; i++)
@ -256,7 +258,6 @@ void io_buildAudioDevString()
} }
if (devlist[i].in_out == 1) if (devlist[i].in_out == 1)
{ {
strcat((char*)io_devstring, devlist[i].working?"1":"0");
strcat((char*)io_devstring, devlist[i].name); strcat((char*)io_devstring, devlist[i].name);
strcat((char*)io_devstring, "~"); // audio device separator strcat((char*)io_devstring, "~"); // audio device separator
} }
@ -275,15 +276,12 @@ void io_buildAudioDevString()
} }
if (devlist[i].in_out == 0) if (devlist[i].in_out == 0)
{ {
strcat((char*)io_devstring, devlist[i].working ? "1" : "0");
strcat((char*)io_devstring, devlist[i].name); strcat((char*)io_devstring, devlist[i].name);
strcat((char*)io_devstring, "~"); // audio device separator strcat((char*)io_devstring, "~"); // audio device separator
} }
} }
//printf("<%s>\n", (char *)io_devstring); //printf("<%s>\n", (char *)io_devstring);
io_devstring[0] = 3; // ID for this message
} }
uint8_t* io_getAudioDevicelist(int* len) uint8_t* io_getAudioDevicelist(int* len)

View File

@ -3,6 +3,21 @@
void getMax(int id, float fv); void getMax(int id, float fv);
void close_stream(int id)
{
#ifdef WIN32
if (devlist[id].capStream != NULL)
close_capture_stream(id);
if (devlist[id].pbStream != NULL)
close_playback_stream(id);
#else
if (devlist[id].instream != NULL)
close_capture_stream(id);
if (devlist[id].outstream != NULL)
close_playback_stream(id);
#endif
}
/* /*
* reads len samples from device id into psamp * reads len samples from device id into psamp
* returns: number of values written to psamp , -1=error * returns: number of values written to psamp , -1=error
@ -124,7 +139,6 @@ uint8_t kmaudio_maxlevel(int id)
void kmaudio_detectDropouts(int id) void kmaudio_detectDropouts(int id)
{ {
int dropout = 0;
int stat = 0; int stat = 0;
int drlen = 0; int drlen = 0;

View File

@ -42,6 +42,15 @@ int playCallback(const void* inputBuffer,
PaStreamCallbackFlags statusFlags, PaStreamCallbackFlags statusFlags,
void* userData); void* userData);
void close_playback_stream(int idx)
{
if (devlist[idx].pbStream != NULL)
{
Pa_CloseStream(devlist[idx].pbStream);
devlist[idx].pbStream = NULL;
}
}
int kmaudio_startPlayback(char* devname, int samprate) int kmaudio_startPlayback(char* devname, int samprate)
{ {
printf("Start request for PB stream:%s\n", devname); printf("Start request for PB stream:%s\n", devname);
@ -61,12 +70,7 @@ int kmaudio_startPlayback(char* devname, int samprate)
devlist[idx].working = 0; devlist[idx].working = 0;
if (devlist[idx].pbStream != NULL) close_playback_stream(idx);
{
printf("Closing old PB stream:%s [%d]\n", devname, idx);
Pa_CloseStream(devlist[idx].pbStream);
devlist[idx].pbStream = NULL;
}
printf("Starting PB stream:%s [%d]\n", devname, idx); printf("Starting PB stream:%s [%d]\n", devname, idx);

View File

@ -136,6 +136,15 @@ void underflow_callback(struct SoundIoOutStream* outstream)
printf("underflow %d\n", count++); printf("underflow %d\n", count++);
} }
void close_playback_stream(int idx)
{
if (devlist[idx].outstream != NULL)
{
soundio_outstream_destroy(devlist[idx].outstream);
devlist[idx].outstream = NULL;
}
}
int kmaudio_startPlayback(char* devname, int samprate) int kmaudio_startPlayback(char* devname, int samprate)
{ {
printf("Start request for PB stream:%s\n", devname); printf("Start request for PB stream:%s\n", devname);
@ -150,13 +159,7 @@ int kmaudio_startPlayback(char* devname, int samprate)
char* pbdevid = getDevID(devname, 1, &idx); char* pbdevid = getDevID(devname, 1, &idx);
if (pbdevid == NULL) return -1; if (pbdevid == NULL) return -1;
// if an old stream is open, close it close_playback_stream(idx);
if (devlist[idx].outstream != NULL)
{
printf("Closing old PB stream:%s [%d]\n", devname, idx);
soundio_outstream_destroy(devlist[idx].outstream);
devlist[idx].outstream = NULL;
}
printf("Starting PB stream:%s [%d]\n", devname, idx); printf("Starting PB stream:%s [%d]\n", devname, idx);

View File

@ -202,12 +202,14 @@ void modulator(uint8_t sym_in)
// adapt speed to soundcard samplerate // adapt speed to soundcard samplerate
int fs; int fs;
int to = 0;
while(keeprunning) while(keeprunning)
{ {
fs = io_fifo_freespace(io_pbidx); fs = io_fifo_freespace(io_pbidx);
// wait until there is space in fifo // wait until there is space in fifo
if(fs > 10) break; if(fs > 10) break;
sleep_ms(10); sleep_ms(10);
if (++to >= 400) break; // give up after 4s
} }
if (marker) if (marker)

File diff suppressed because it is too large Load Diff

View File

@ -1,665 +0,0 @@
/*
* 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.
*
* soundio.c ... interface to libsoundio
*
*/
#include "hsmodem.h"
void io_cap_write_fifo(float sample);
#define MAXAUDIODEVICES 50
struct SoundIo* soundio = NULL;
struct SoundIoDevice* io_pb_device = NULL;
struct SoundIoDevice* io_cap_device = NULL;
struct SoundIoInStream* instream = NULL;
struct SoundIoOutStream* outstream = NULL;
typedef struct _AUDIODEV_ {
int in_out = 0; // 0=in, 1=out
char name[1000] = { 0 };
char id[1000] = { 0 };
int minsamprate = 44100;
int maxsamprate = 48000;
int stereo_mono = 2; // 1=mono, 2=stereo
} AUDIODEV;
AUDIODEV audiodev[MAXAUDIODEVICES];
int audiodevidx = 0;
void print_devs()
{
printf("\n ==== AUDIO devices ====\n");
for (int i = 0; i < audiodevidx; i++)
{
if(i>0) printf(" -----------------\n");
printf("Name: %s\n", audiodev[i].name);
printf("ID : %s\n", audiodev[i].id);
printf("I/O : %s\n", (audiodev[i].in_out == 0) ? "record":"playback");
printf("Chan: %s\n", (audiodev[i].stereo_mono == 2) ? "stereo" : "mono");
printf("minR: %d\n", audiodev[i].minsamprate);
printf("maxR: %d\n", audiodev[i].maxsamprate);
}
printf("\n =======================\n");
}
static void get_channel_layout(const struct SoundIoChannelLayout* layout)
{
if (layout->name)
{
if (strstr(layout->name, "ereo"))
audiodev[audiodevidx].stereo_mono = 2;
if (strstr(layout->name, "ono"))
audiodev[audiodevidx].stereo_mono = 1;
}
}
int print_device(struct SoundIoDevice* device)
{
if (!device->probe_error)
{
/*#ifdef _WIN32_
// only use raw (exclusive) devices
if (device->is_raw == false) return 0;
#endif*/
// ignore if exists
for (int i = 0; i < audiodevidx; i++)
if (!strcmp(device->id, audiodev[i].id)) return 0;
if (strstr(device->name, "onitor")) return 0;
strncpy(audiodev[audiodevidx].id, device->id, 999);
audiodev[audiodevidx].id[999] = 0;
strncpy(audiodev[audiodevidx].name, device->name, 999);
audiodev[audiodevidx].name[999] = 0;
for (int i = 0; i < device->layout_count; i++)
get_channel_layout(&device->layouts[i]);
for (int i = 0; i < device->sample_rate_count; i++)
{
struct SoundIoSampleRateRange* range = &device->sample_rates[i];
if (range->min < audiodev[audiodevidx].minsamprate)
audiodev[audiodevidx].minsamprate = range->min;
if (range->max > audiodev[audiodevidx].maxsamprate)
audiodev[audiodevidx].maxsamprate = range->max;
}
if (audiodev[audiodevidx].minsamprate > 44100)
return 0;
if (audiodev[audiodevidx].maxsamprate < 48000)
return 0;
return 1;
}
return 0;
}
static int scan_devices(struct SoundIo* soundio)
{
audiodevidx = 0;
for (int i = 0; i < soundio_input_device_count(soundio); i++)
{
struct SoundIoDevice* device = soundio_get_input_device(soundio, i);
if (print_device(device) == 1)
{
audiodev[audiodevidx].in_out = 0;
audiodevidx++;
}
soundio_device_unref(device);
}
for (int i = 0; i < soundio_output_device_count(soundio); i++)
{
struct SoundIoDevice* device = soundio_get_output_device(soundio, i);
if (print_device(device) == 1)
{
audiodev[audiodevidx].in_out = 1;
audiodevidx++;
}
soundio_device_unref(device);
}
return 0;
}
// build string of audio device name, to be sent to application as response to Broadcast search
// starting with PB devices, sperarator ^, capture devices
// separator between devices: ~
uint8_t io_devstring[MAXDEVSTRLEN + 100];
void io_buildUdpAudioList()
{
memset(io_devstring, 0, sizeof(io_devstring));
io_devstring[0] = ' '; // placeholder for ID for this UDP message
io_devstring[1] = '0' + init_audio_result;
io_devstring[2] = '0' + init_voice_result;
// playback devices
for (int i = 0; i < audiodevidx; i++)
{
if (audiodev[i].in_out == 1)
{
strcat((char*)io_devstring, audiodev[i].name);
strcat((char*)io_devstring, "~"); // audio device separator
}
}
strcat((char*)(io_devstring + 1), "^"); // PB, CAP separator
// capture devices
for (int i = 0; i < audiodevidx; i++)
{
if (audiodev[i].in_out == 0)
{
strcat((char*)io_devstring, audiodev[i].name);
strcat((char*)io_devstring, "~"); // audio device separator
}
}
io_devstring[0] = 3; // ID for this UDP message
}
uint8_t* io_getAudioDevicelist(int* len)
{
// update Status
io_devstring[1] = '0' + init_audio_result;
io_devstring[2] = '0' + init_voice_result;
*len = strlen((char*)(io_devstring + 1)) + 1;
return io_devstring;
}
void io_readAudioDevices()
{
if (soundio == NULL) return;
// to get actual data
soundio_flush_events(soundio);
scan_devices(soundio); // read devices
io_buildUdpAudioList();
//print_devs();
}
char* getDevID(char* devname, int io)
{
for (int i = 0; i < audiodevidx; i++)
{
if (!strcmp(devname, audiodev[i].name) && io == audiodev[i].in_out)
{
return audiodev[i].id;
}
}
return NULL;
}
int min_int(int a, int b)
{
return (a < b) ? a : b;
}
void read_callback(struct SoundIoInStream* instream, int frame_count_min, int frame_count_max)
{
int err;
//printf("cap: %d %d\n", frame_count_min, frame_count_max);
//int chans = instream->layout.channel_count;
struct SoundIoChannelArea* areas;
// samples are in areas.ptr
int frames_left = frame_count_max; // take all
while (1)
{
int frame_count = frames_left;
if ((err = soundio_instream_begin_read(instream, &areas, &frame_count)))
{
fprintf(stderr, "begin read error: %s", soundio_strerror(err));
exit(1);
}
if (!frame_count)
break;
for (int frame = 0; frame < frame_count; frame += 1)
{
for (int ch = 0; ch < instream->layout.channel_count; ch += 1)
{
int16_t rxdata;
memcpy(&rxdata, areas[ch].ptr, instream->bytes_per_sample);
areas[ch].ptr += areas[ch].step;
if (ch == 0)
{
float f = rxdata;
f /= 32768;
f *= softwareCAPvolume;
io_cap_write_fifo(f);
}
}
}
//measure_speed_bps(frame_count);
if ((err = soundio_instream_end_read(instream)))
{
fprintf(stderr, "end read error: %s", soundio_strerror(err));
exit(1);
}
frames_left -= frame_count;
if (frames_left <= 0)
break;
}
}
/*
void read_callback(struct SoundIoInStream* instream, int frame_count_min, int frame_count_max)
{
int err;
//printf("cap: %d %d\n", frame_count_min, frame_count_max);
// bytes_per_frame == 4 because we use float
// instream->bytes_per_sample/bytes_per_frame = 1 (mono) or 2 (stereo)
int chans = instream->layout.channel_count;
struct SoundIoChannelArea* areas;
// samples are in areas.ptr
int frames_left = frame_count_max; // take all
while (1)
{
int frame_count = frames_left;
if ((err = soundio_instream_begin_read(instream, &areas, &frame_count)))
{
fprintf(stderr, "begin read error: %s", soundio_strerror(err));
exit(1);
}
if (!frame_count)
break;
// do something with the data in *area.ptr
// take a float every chans*4 Bytes
int16_t* samples = (int16_t*)(areas[0].ptr);
int pos = 0;
for (int i = 0; i < frame_count; i++)
{
float f = samples[pos];
f /= 32768;
f *= softwareCAPvolume;
io_cap_write_fifo(f);
pos += chans;
}
//measure_speed_bps(frame_count);
if ((err = soundio_instream_end_read(instream))) {
fprintf(stderr, "end read error: %s", soundio_strerror(err));
exit(1);
}
frames_left -= frame_count;
if (frames_left <= 0)
break;
}
}
*/
void overflow_callback(struct SoundIoInStream* instream)
{
static int count = 0;
printf("overflow %d\n", ++count);
}
#define MAXCAPCHUNKLEN 50000
static void write_callback(struct SoundIoOutStream* outstream, int frame_count_min, int frame_count_max)
{
struct SoundIoChannelArea* areas;
int err;
int frames_left = frame_count_max;
while (1)
{
int frame_count = frames_left;
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
{
fprintf(stderr, "unrecoverable stream error: %s\n", soundio_strerror(err));
exit(1);
}
if (!frame_count)
break;
float f[MAXCAPCHUNKLEN];
int fiforet = io_pb_read_fifo_num(f, frame_count);
if (fiforet == 0)
{
// elements not available, fill with zeroes
//printf("not enough data, send zeroes\n");
memset(f, 0, sizeof(float) * frame_count);
}
const struct SoundIoChannelLayout* layout = &outstream->layout;
for (int frame = 0; frame < frame_count; frame++)
{
for (int channel = 0; channel < layout->channel_count; channel++)
{
write_sample_s16ne(areas[channel].ptr, f[frame]);
areas[channel].ptr += areas[channel].step;
}
}
if ((err = soundio_outstream_end_write(outstream))) {
if (err == SoundIoErrorUnderflow)
return;
fprintf(stderr, "unrecoverable stream error: %s\n", soundio_strerror(err));
exit(1);
}
frames_left -= frame_count;
if (frames_left <= 0)
break;
}
}
/*
static void write_callback(struct SoundIoOutStream* outstream, int frame_count_min, int frame_count_max)
{
int chans = outstream->layout.channel_count;
struct SoundIoChannelArea* areas;
int err;
int frames_left = min_int(frame_count_max,MAXCAPCHUNKLEN);// frame_count_max;
//printf("\nmin: %d max:%d\n", frame_count_min, frame_count_max);
// we have to write frame_count_max, not less, or we get an underrun
// this has to be written in chunks requested by soundio_outstream_begin_write
float f[MAXCAPCHUNKLEN];
while (1)
{
int frame_count = frames_left;
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count))) {
printf("unrecoverable soundio_outstream_begin_write error: %s\n", soundio_strerror(err));
exit(1);
}
if (!frame_count) break; // will normally never happen
//printf("chunk: %d\n", frame_count);
// soundio_outstream_begin_write requested to write frame_count elements
int fiforet = io_pb_read_fifo_num(f, frame_count);
if (fiforet == 0)
{
// elements not available, fill with zeroes
//printf("not enough data, send zeroes\n");
memset(f, 0, sizeof(float) * frame_count);
}
// apply volume
for (int i = 0; i < frame_count; i++)
f[i] *= softwarePBvolume;
// put data into soundio buffer
for (int frame = 0; frame < frame_count; frame++)
{
for (int ch = 0; ch < chans; ch++)
{
int16_t* s = (int16_t*)areas[ch].ptr;
*s = (int16_t)(f[frame] * 32768.0);
areas[ch].ptr += areas[ch].step;
}
}
// and finalize this chunk
if ((err = soundio_outstream_end_write(outstream))) {
if (err == SoundIoErrorUnderflow)
return;
printf("unrecoverable soundio_outstream_end_write error: %s\n", soundio_strerror(err));
exit(1);
}
frames_left -= frame_count;
if (frames_left <= 0)
break;
}
}
*/
void underflow_callback(struct SoundIoOutStream* outstream)
{
static int count = 0;
printf("underflow %d\n", count++);
}
int io_init_sound(char *pbname, char *capname)
{
int err;
init_audio_result = 0;
printf("\n ==== IO INIT AUDIO devices ====\n");
printf("requested: <%s> <%s>\ncapture rate:%d\n",pbname,capname,caprate);
io_close_audio();
// prepare and connect to libsoundio
soundio = soundio_create();
if (!soundio) {
printf("soundio_create: out of memory\n");
return 0;
}
#ifdef _WIN32_
if ((err=soundio_connect_backend(soundio, SoundIoBackendWasapi))) {
printf("soundio_connect: %s\n", soundio_strerror(err));
return 0;
}
#endif
#ifdef _LINUX_
if ((err = soundio_connect(soundio))) {
printf("soundio_connect: %s\n", soundio_strerror(err));
return 0;
}
#endif
io_readAudioDevices();
io_init_pipes();
if (pbname == NULL || capname == NULL || strlen(pbname) < 3 || strlen(capname) < 3) // no devices defined yet
{
printf("no devices specified\n");
return 0;
}
char* pbdevid = getDevID(pbname,1);
if (pbdevid == NULL) return 0;
char* capdevid = getDevID(capname,0);
if (capdevid == NULL) return 0;
// define the capture device
printf("selected CAP device:\nname:%s\nid :%s\n", capname, capdevid);
soundio_flush_events(soundio);
for (int i = 0; i < soundio_input_device_count(soundio); i++)
{
io_cap_device = NULL;
struct SoundIoDevice* device = soundio_get_input_device(soundio, i);
if (strcmp(device->id, capdevid) == 0
#ifdef _WIN32_
&& device->is_raw == true
#endif
)
{
io_cap_device = device;
break;
}
soundio_device_unref(device);
}
if (!io_cap_device)
{
printf("Invalid device id: %s\n", capdevid);
return 0;
}
if (io_cap_device->probe_error)
{
printf("Unable to probe device: %s\n", soundio_strerror(io_cap_device->probe_error));
return 0;
}
// create capture callback
instream = soundio_instream_create(io_cap_device);
if (!instream) {
printf("out of memory\n");
return 0;
}
instream->format = SoundIoFormatS16NE;
instream->sample_rate = caprate;
instream->software_latency = 0.0;
instream->read_callback = read_callback;
instream->overflow_callback = overflow_callback;
instream->userdata = NULL;
if ((err = soundio_instream_open(instream))) {
printf("unable to open input stream: %s", soundio_strerror(err));
return 0;
}
if ((err = soundio_instream_start(instream))) {
fprintf(stderr, "unable to start input device: %s", soundio_strerror(err));
return 0;
}
init_audio_result |= 2;
// the CAP callback is running now
// define the playback device
printf("selected PB device:\nname:%s\nid :%s\n", pbname, pbdevid);
for (int i = 0; i < soundio_output_device_count(soundio); i++)
{
io_pb_device = NULL;
struct SoundIoDevice* device = soundio_get_output_device(soundio, i);
if (strcmp(device->id, pbdevid) == 0
#ifdef _WIN32_
&& device->is_raw == true
#endif
)
{
io_pb_device = device;
break;
}
soundio_device_unref(device);
}
if (!io_pb_device)
{
printf("Invalid device id: %s\n", pbdevid);
return 0;
}
if (io_pb_device->probe_error)
{
printf("Unable to probe device: %s\n", soundio_strerror(io_pb_device->probe_error));
return 0;
}
// create playback callback
outstream = soundio_outstream_create(io_pb_device);
if (!outstream) {
printf("soundio_outstream_create: out of memory\n");
return 0;
}
outstream->format = SoundIoFormatS16NE;
outstream->sample_rate = caprate;
outstream->software_latency = 0.0;
outstream->write_callback = write_callback;
outstream->underflow_callback = underflow_callback;
outstream->userdata = NULL;
if ((err = soundio_outstream_open(outstream))) {
printf("unable to open output stream: %s", soundio_strerror(err));
return 0;
}
if ((err = soundio_outstream_start(outstream))) {
fprintf(stderr, "unable to start output device: %s", soundio_strerror(err));
return 0;
}
init_audio_result |= 1;
printf("==== Audio init finished: %d ====\n", init_audio_result);
return init_audio_result;
}
void io_close_audio()
{
printf("close Audio\n");
if(instream) soundio_instream_destroy(instream);
instream = NULL;
if (outstream) soundio_outstream_destroy(outstream);
outstream = NULL;
if(io_pb_device) soundio_device_unref(io_pb_device);
io_pb_device = NULL;
if (io_cap_device) soundio_device_unref(io_cap_device);
io_cap_device = NULL;
if (soundio) soundio_destroy(soundio);
soundio = NULL;
}
void io_setPBvolume(int v)
{
// the volume comes in % 0..99
softwarePBvolume = ((float)v) / 50.0f;
}
void io_setCAPvolume(int v)
{
// the volume comes in % 0..99
softwareCAPvolume = ((float)v) / 50.0f;
}
// set volume
void io_setVolume(int pbcap, int v)
{
if (pbcap == 0) io_setPBvolume(v);
else io_setCAPvolume(v);
}
void io_setLSvolume(int v)
{
// the volume comes in % 0..99
softwareLSvolume = ((float)v) / 50.0f;
}
void io_setMICvolume(int v)
{
// the volume comes in % 0..99
softwareMICvolume = ((float)v) / 50.0f;
}
void setVolume_voice(int pbcap, int v)
{
}

View File

@ -74,7 +74,7 @@ void UdpRxInit(int *sock, int port, void (*rxfunc)(uint8_t *, int, struct sockad
memset(&sin, 0, sizeof(struct sockaddr_in)); memset(&sin, 0, sizeof(struct sockaddr_in));
sin.sin_family = AF_INET; sin.sin_family = AF_INET;
sin.sin_port = htons(port); sin.sin_port = htons(port);
sin.sin_addr.s_addr = INADDR_ANY; sin.sin_addr.s_addr = INADDR_ANY;
if (bind(*sock, (struct sockaddr *)&sin, sizeof(struct sockaddr_in)) != 0) if (bind(*sock, (struct sockaddr *)&sin, sizeof(struct sockaddr_in)) != 0)
{ {
@ -102,13 +102,11 @@ void UdpRxInit(int *sock, int port, void (*rxfunc)(uint8_t *, int, struct sockad
rxcfg_idx++; rxcfg_idx++;
} }
#ifdef _LINUX_ #ifdef _LINUX_
void* threadfunction(void* param) { void* threadfunction(void* param) {
socklen_t fromlen; socklen_t fromlen;
pthread_detach(pthread_self()); pthread_detach(pthread_self());
#endif #endif
#ifdef _WIN32_ #ifdef _WIN32_
void threadfunction(void* param) { void threadfunction(void* param) {
int fromlen; int fromlen;
@ -116,18 +114,24 @@ void threadfunction(void* param) {
RXCFG rxcfg; RXCFG rxcfg;
memcpy((uint8_t *)(&rxcfg), (uint8_t *)param, sizeof(RXCFG)); memcpy((uint8_t *)(&rxcfg), (uint8_t *)param, sizeof(RXCFG));
int recvlen; int recvlen;
char rxbuf[256]; const int maxUDPpacketsize = 1024;
char rxbuf[maxUDPpacketsize];
struct sockaddr_in fromSock; struct sockaddr_in fromSock;
fromlen = sizeof(struct sockaddr_in); fromlen = sizeof(struct sockaddr_in);
while(*rxcfg.keeprunning) while(*rxcfg.keeprunning)
{ {
recvlen = recvfrom(*rxcfg.sock, rxbuf, 256, 0, (struct sockaddr *)&fromSock, &fromlen); recvlen = recvfrom(*rxcfg.sock, rxbuf, maxUDPpacketsize, 0, (struct sockaddr *)&fromSock, &fromlen);
if (recvlen > 0) if (recvlen > 0)
{ {
// data received, send it to callback function // data received, send it to callback function
(*rxcfg.rxfunc)((uint8_t *)rxbuf,recvlen, &fromSock); (*rxcfg.rxfunc)((uint8_t *)rxbuf,recvlen, &fromSock);
} }
if (recvlen < 0)
{
#ifdef _WIN32_
printf("recvfrom error: %d", WSAGetLastError());
#endif
}
} }
#ifdef _LINUX_ #ifdef _LINUX_
pthread_exit(NULL); // self terminate this thread pthread_exit(NULL); // self terminate this thread

View File

@ -170,7 +170,6 @@ void sendCodecToModulator(uint8_t *pdata, int len)
while (keeprunning) while (keeprunning)
{ {
// we have to check if the TX fifo has enough data. In case of an underrun the Q(8A)PSK signal will be distorted // we have to check if the TX fifo has enough data. In case of an underrun the Q(8A)PSK signal will be distorted
//int us = io_pb_fifo_usedspace();
int us = io_fifo_usedspace(io_pbidx); int us = io_fifo_usedspace(io_pbidx);
if (us < 20000) if (us < 20000)
{ {

Binary file not shown.

Binary file not shown.

View File

@ -37,6 +37,8 @@
this.toolStripStatusLabel = new System.Windows.Forms.ToolStripStatusLabel(); this.toolStripStatusLabel = new System.Windows.Forms.ToolStripStatusLabel();
this.ts_ip = new System.Windows.Forms.ToolStripStatusLabel(); this.ts_ip = new System.Windows.Forms.ToolStripStatusLabel();
this.RXstatus = new System.Windows.Forms.ToolStripStatusLabel(); this.RXstatus = new System.Windows.Forms.ToolStripStatusLabel();
this.toolStrip_spacer = new System.Windows.Forms.ToolStripStatusLabel();
this.ts_userinfo = new System.Windows.Forms.ToolStripStatusLabel();
this.panel_constel = new System.Windows.Forms.Panel(); this.panel_constel = new System.Windows.Forms.Panel();
this.timer_qpsk = new System.Windows.Forms.Timer(this.components); this.timer_qpsk = new System.Windows.Forms.Timer(this.components);
this.panel_txspectrum = new System.Windows.Forms.Panel(); this.panel_txspectrum = new System.Windows.Forms.Panel();
@ -253,7 +255,9 @@
this.toolStrip_Type, this.toolStrip_Type,
this.toolStripStatusLabel, this.toolStripStatusLabel,
this.ts_ip, this.ts_ip,
this.RXstatus}); this.RXstatus,
this.toolStrip_spacer,
this.ts_userinfo});
this.statusStrip1.Location = new System.Drawing.Point(0, 671); this.statusStrip1.Location = new System.Drawing.Point(0, 671);
this.statusStrip1.Name = "statusStrip1"; this.statusStrip1.Name = "statusStrip1";
this.statusStrip1.Size = new System.Drawing.Size(1296, 22); this.statusStrip1.Size = new System.Drawing.Size(1296, 22);
@ -286,6 +290,20 @@
this.RXstatus.Size = new System.Drawing.Size(58, 17); this.RXstatus.Size = new System.Drawing.Size(58, 17);
this.RXstatus.Text = "RX-Status"; this.RXstatus.Text = "RX-Status";
// //
// toolStrip_spacer
//
this.toolStrip_spacer.Name = "toolStrip_spacer";
this.toolStrip_spacer.Size = new System.Drawing.Size(1156, 17);
this.toolStrip_spacer.Spring = true;
//
// ts_userinfo
//
this.ts_userinfo.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold);
this.ts_userinfo.ForeColor = System.Drawing.Color.Blue;
this.ts_userinfo.Name = "ts_userinfo";
this.ts_userinfo.Size = new System.Drawing.Size(16, 17);
this.ts_userinfo.Text = "...";
//
// panel_constel // panel_constel
// //
this.panel_constel.BackColor = System.Drawing.Color.AliceBlue; this.panel_constel.BackColor = System.Drawing.Color.AliceBlue;
@ -2268,7 +2286,7 @@
this.ForeColor = System.Drawing.SystemColors.ControlText; this.ForeColor = System.Drawing.SystemColors.ControlText;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Name = "Form1"; this.Name = "Form1";
this.Text = "AMSAT-DL Multimedia HS Modem V0.71 by DJ0ABR"; this.Text = "AMSAT-DL Multimedia HS Modem V0.72 by DJ0ABR";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing); this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing);
this.statusStrip1.ResumeLayout(false); this.statusStrip1.ResumeLayout(false);
this.statusStrip1.PerformLayout(); this.statusStrip1.PerformLayout();
@ -2498,6 +2516,8 @@
private System.Windows.Forms.CheckBox cb_rx_autosync; private System.Windows.Forms.CheckBox cb_rx_autosync;
private System.Windows.Forms.TextBox textBox6; private System.Windows.Forms.TextBox textBox6;
private System.Windows.Forms.Button button6; private System.Windows.Forms.Button button6;
private System.Windows.Forms.ToolStripStatusLabel toolStrip_spacer;
private System.Windows.Forms.ToolStripStatusLabel ts_userinfo;
} }
} }

View File

@ -44,8 +44,8 @@ namespace oscardata
String old_tsip = ""; String old_tsip = "";
bool modemrunning = false; bool modemrunning = false;
receivefile recfile = new receivefile(); receivefile recfile = new receivefile();
int last_initAudioStatus; int last_initAudioStatus = -1;
int last_initVoiceStatus; int last_initVoiceStatus = -1;
int recordStatus = 0; int recordStatus = 0;
int recPhase = 0; int recPhase = 0;
const int Rtty_deftext_anz = 20; const int Rtty_deftext_anz = 20;
@ -220,8 +220,8 @@ namespace oscardata
{ {
if (s.Length > 1) if (s.Length > 1)
{ {
cb_audioPB.Items.Add(s.Substring(1)); cb_audioPB.Items.Add(s);
cb_loudspeaker.Items.Add(s.Substring(1)); cb_loudspeaker.Items.Add(s);
} }
} }
cb_loudspeaker.EndUpdate(); cb_loudspeaker.EndUpdate();
@ -238,8 +238,8 @@ namespace oscardata
{ {
if (s.Length > 1) if (s.Length > 1)
{ {
cb_audioCAP.Items.Add(s.Substring(1)); cb_audioCAP.Items.Add(s);
cb_mic.Items.Add(s.Substring(1)); cb_mic.Items.Add(s);
} }
} }
cb_mic.EndUpdate(); cb_mic.EndUpdate();
@ -315,21 +315,17 @@ namespace oscardata
} }
} }
// correct entries in the Audio Device Comboboxes if Devices have changed // correct entries in the Audio Device Comboboxes if devices have changed
void findDevice(ComboBox cb) void findDevice(ComboBox cb)
{ {
int pos = -1; int pos = -1;
if (cb.Text.Length >= 4) if (cb.Text.Length >= 4)
{ {
// Device Name starts at Index 3 in the string
String dn = cb.Text.Substring(3);
int anz = cb.Items.Count; int anz = cb.Items.Count;
for (int i = 0; i < anz; i++) for (int i = 0; i < anz; i++)
{ {
String name = cb.Items[i].ToString(); if (cb.Text == cb.Items[i].ToString())
name = name.Substring(3);
if (dn == name)
{ {
pos = i; pos = i;
break; break;
@ -349,7 +345,6 @@ namespace oscardata
cb.Text = cb.Items[pos].ToString(); cb.Text = cb.Items[pos].ToString();
} }
private void Form1_FormClosing(object sender, FormClosingEventArgs e) private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{ {
save_Setup(); save_Setup();
@ -443,6 +438,14 @@ namespace oscardata
//Console.WriteLine("minfo:" + minfo + " data:" + rxdata[0].ToString("X2") + " " + rxdata[1].ToString("X2")); //Console.WriteLine("minfo:" + minfo + " data:" + rxdata[0].ToString("X2") + " " + rxdata[1].ToString("X2"));
if (rxtype == statics.Userinfo)
{
String call = statics.ByteArrayToString(rxdata, 0, 20);
String qthloc = statics.ByteArrayToString(rxdata, 20, 10);
String name = statics.ByteArrayToString(rxdata, 30, 20);
ts_userinfo.Text = call + " " + name + " " + qthloc;
}
// ========= receive file ========== // ========= receive file ==========
// handle file receive // handle file receive
if (rxtype == statics.Image) if (rxtype == statics.Image)
@ -1181,13 +1184,17 @@ namespace oscardata
public String GetMyBroadcastIP() public String GetMyBroadcastIP()
{ {
String ip = "255.255.255.255"; String ip = "255.255.255.255";
/*
// selective BCs fail if the computer has multiple IPs
// therefore use 255.255.255.255
String[] myips = statics.getOwnIPs(); String[] myips = statics.getOwnIPs();
//Console.WriteLine("BClen: " + myips.Length.ToString()); Console.WriteLine("BClen: " + myips.Length.ToString());
// if PC has multiple IPs then use 255.255.255.255 // if PC has multiple IPs then use 255.255.255.255
/*for (int i = 0; i < myips.Length; i++) for (int i = 0; i < myips.Length; i++)
{ {
Console.WriteLine("ip:" + myips[i]); Console.WriteLine("ip:" + myips[i]);
}*/ }
if (myips.Length >= 1) if (myips.Length >= 1)
{ {
statics.MyIP = myips[0]; statics.MyIP = myips[0];
@ -1199,7 +1206,7 @@ namespace oscardata
ip += ".255"; ip += ".255";
//Console.WriteLine("BCip: " + ip); //Console.WriteLine("BCip: " + ip);
} }
} }*/
return ip; return ip;
} }
@ -1231,7 +1238,7 @@ namespace oscardata
if (cb_safemode.Text.Contains("medium")) safemode = 2; if (cb_safemode.Text.Contains("medium")) safemode = 2;
else if (cb_safemode.Text.Contains("high")) safemode = 4; else if (cb_safemode.Text.Contains("high")) safemode = 4;
Byte[] txb = new byte[210]; Byte[] txb = new byte[260];
txb[0] = 0x3c; // ID of this message txb[0] = 0x3c; // ID of this message
txb[1] = (Byte)tb_PBvol.Value; txb[1] = (Byte)tb_PBvol.Value;
txb[2] = (Byte)tb_CAPvol.Value; txb[2] = (Byte)tb_CAPvol.Value;
@ -1248,6 +1255,7 @@ namespace oscardata
//Byte[] bpb = statics.StringToByteArray(cb_audioPB.Text); //Byte[] bpb = statics.StringToByteArray(cb_audioPB.Text);
//Byte[] bcap = statics.StringToByteArray(cb_audioCAP.Text); //Byte[] bcap = statics.StringToByteArray(cb_audioCAP.Text);
// 200 Bytes (from 10..209) name of selected sound device
for (int i=0; i<100; i++) for (int i=0; i<100; i++)
{ {
if (i >= bpb.Length) if (i >= bpb.Length)
@ -1261,6 +1269,28 @@ namespace oscardata
txb[i + 110] = bcap[i]; txb[i + 110] = bcap[i];
} }
// 210 .. 229 = Callsign
Byte[] callarr = statics.StringToByteArray(tb_callsign.Text);
for (int i = 0; i < 20; i++)
{
if (i >= callarr.Length) txb[i+210] = 0;
else txb[i + 210] = callarr[i];
}
// 230 .. 239 = qthloc
Byte[] qtharr = statics.StringToByteArray(tb_myqthloc.Text);
for (int i = 0; i < 10; i++)
{
if (i >= qtharr.Length) txb[i + 230] = 0;
else txb[i+230] = qtharr[i];
}
// 240 .. 259 = Name
Byte[] namearr = statics.StringToByteArray(tb_myname.Text);
for (int i = 0; i < 20; i++)
{
if (i >= namearr.Length) txb[i+240] = 0;
else txb[i + 240] = namearr[i];
}
if (statics.ModemIP == "1.2.3.4") if (statics.ModemIP == "1.2.3.4")
{ {
// still searching a modem // still searching a modem

View File

@ -137,7 +137,7 @@
AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w
LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAAA+ ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAAA+
JQAAAk1TRnQBSQFMAgEBFwEAAaABDAGgAQwBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo JQAAAk1TRnQBSQFMAgEBFwEAAdgBDAHYAQwBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo
AwABQAMAAWADAAEBAQABCAYAARgYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA AwABQAMAAWADAAEBAQABCAYAARgYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA
AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5 AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5
AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA

View File

@ -1,12 +1,7 @@
using System; using System;
using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
namespace oscardata namespace oscardata
{ {
class KmProgressBar : ProgressBar class KmProgressBar : ProgressBar

View File

@ -23,6 +23,7 @@ namespace oscardata
public static Byte HTMLFile = 4; public static Byte HTMLFile = 4;
public static Byte BinaryFile = 5; public static Byte BinaryFile = 5;
public static Byte Audio = 6; public static Byte Audio = 6;
public static Byte Userinfo = 7;
// the upper values are for internal use // the upper values are for internal use
public static Byte ResamplingRate = 16; public static Byte ResamplingRate = 16;
@ -185,6 +186,23 @@ namespace oscardata
return enc.GetString(ban); return enc.GetString(ban);
} }
public static string ByteArrayToString(byte[] arr, int offset, int length)
{
Byte[] ba = new byte[arr.Length];
int dst = 0;
for (int i = 0; i < length; i++)
{
if (i >= arr.Length) break;
if (arr[i+ offset] != 0) ba[dst++] = arr[i+ offset];
}
Byte[] ban = new byte[dst];
Array.Copy(ba, ban, dst);
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
return enc.GetString(ban);
}
public static string ByteArrayToStringUtf8(byte[] arr, int offset = 0) public static string ByteArrayToStringUtf8(byte[] arr, int offset = 0)
{ {
Byte[] ba = new byte[arr.Length]; Byte[] ba = new byte[arr.Length];

View File

@ -123,8 +123,13 @@ namespace oscardata
searchtimeout = 0; searchtimeout = 0;
// message b contains audio devices and init status // message b contains audio devices and init status
//String s = statics.ByteArrayToString(b,0); statics.initAudioStatus = (b[0] == '1') ? 2 : 0;
String s = statics.ByteArrayToStringUtf8(b, 0); statics.initAudioStatus |= (b[1] == '1') ? 1 : 0;
statics.initVoiceStatus = (b[2] == '1') ? 2 : 0;
statics.initVoiceStatus |= (b[3] == '1') ? 1 : 0;
//String s = statics.ByteArrayToString(b,4);
String s = statics.ByteArrayToStringUtf8(b, 4);
String[] sa1 = s.Split(new char[] { '^' }); String[] sa1 = s.Split(new char[] { '^' });
statics.AudioPBdevs = sa1[0].Split(new char[] { '~' }); statics.AudioPBdevs = sa1[0].Split(new char[] { '~' });
statics.AudioCAPdevs = sa1[1].Split(new char[] { '~' }); statics.AudioCAPdevs = sa1[1].Split(new char[] { '~' });
@ -143,18 +148,19 @@ namespace oscardata
// FFT data // FFT data
if (rxtype == statics.udp_fft) if (rxtype == statics.udp_fft)
{ {
statics.PBfifousage = b[0]; int idx = 0;
statics.CAPfifousage = b[1]; statics.PBfifousage = b[idx++];
statics.RXlevelDetected = b[2]; statics.CAPfifousage = b[idx++];
statics.RXinSync = b[3]; statics.RXlevelDetected = b[idx++];
statics.maxRXlevel = b[4]; statics.RXinSync = b[idx++];
statics.maxTXlevel = b[5]; statics.maxRXlevel = b[idx++];
statics.tune_frequency = b[6]; statics.maxTXlevel = b[idx++];
statics.tune_frequency = b[idx++];
statics.tune_frequency <<= 8; statics.tune_frequency <<= 8;
statics.tune_frequency += b[7]; statics.tune_frequency += b[idx++];
//Console.WriteLine("f:" + statics.tune_frequency); //Console.WriteLine("f:" + statics.tune_frequency);
Byte[] b1 = new byte[b.Length - 6]; Byte[] b1 = new byte[b.Length - idx];
Array.Copy(b, 6, b1, 0, b1.Length); Array.Copy(b, idx, b1, 0, b1.Length);
drawFftBitmap(b1); drawFftBitmap(b1);
} }