diff --git a/README.md b/README.md index ec0edd9..8c821dd 100644 --- a/README.md +++ b/README.md @@ -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. # this is work in progress -Version 0.64 +Version 0.72 Windows 10 (should work on Win7, not tested) linux Desktop PC, Odroid SBC @@ -18,7 +18,7 @@ Raspberry 4 (3B+) * Odroid C2 ... 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) @@ -29,11 +29,12 @@ this software uses these programs: * libsoundio: https://github.com/andrewrk/libsoundio (MIT License) * 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) +* portaudio: https://github.com/PortAudio/portaudio # Download alternatives * download from github and build from source * 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 diff --git a/WinRelease/hsmodem.exe b/WinRelease/hsmodem.exe index f953f75..430c8af 100755 Binary files a/WinRelease/hsmodem.exe and b/WinRelease/hsmodem.exe differ diff --git a/WinRelease/hsmodem.iobj b/WinRelease/hsmodem.iobj deleted file mode 100755 index e83c1a2..0000000 Binary files a/WinRelease/hsmodem.iobj and /dev/null differ diff --git a/WinRelease/hsmodem.ipdb b/WinRelease/hsmodem.ipdb deleted file mode 100755 index fccc4a2..0000000 Binary files a/WinRelease/hsmodem.ipdb and /dev/null differ diff --git a/WinRelease/hsmodem.pdb b/WinRelease/hsmodem.pdb deleted file mode 100755 index 33c9ce3..0000000 Binary files a/WinRelease/hsmodem.pdb and /dev/null differ diff --git a/WinRelease/oscardata.exe b/WinRelease/oscardata.exe index 817a6a8..ac7c65a 100755 Binary files a/WinRelease/oscardata.exe and b/WinRelease/oscardata.exe differ diff --git a/WinRelease/portaudio_x86.dll b/WinRelease/portaudio_x86.dll index e4f98d4..f49123d 100755 Binary files a/WinRelease/portaudio_x86.dll and b/WinRelease/portaudio_x86.dll differ diff --git a/hsmodem.wse b/hsmodem.wse index 8a391e2..2b77ea3 100644 --- a/hsmodem.wse +++ b/hsmodem.wse @@ -760,8 +760,8 @@ item: Install File Flags=0000000010000010 end item: Install File - Source=c:\tmp\WinRelease\libsoundio.dll - Destination=%MAINDIR%\libsoundio.dll + Source=c:\tmp\WinRelease\portaudio_x86.dll + Destination=%MAINDIR%\portaudio_x86.dll Flags=0000000010000010 end item: Install File diff --git a/hsmodem/Makefile b/hsmodem/Makefile index dfdd0f5..fdac833 100755 --- a/hsmodem/Makefile +++ b/hsmodem/Makefile @@ -29,4 +29,5 @@ default: $(OBJ) clean: rm -f *.o - + rm -f libkmaudio/*.o + rm -f libkmaudio/*.o diff --git a/hsmodem/announcement.cpp b/hsmodem/announcement.cpp index 39694df..8afdf9b 100755 --- a/hsmodem/announcement.cpp +++ b/hsmodem/announcement.cpp @@ -92,7 +92,6 @@ void playAudioPCM(char* fn, int destination) { int to = 4000; int res; - //while ((res = io_ls_fifo_usedspace()) > 10000) while ((res = io_fifo_usedspace(voice_pbidx)) > 10000) { if (--to == 0) diff --git a/hsmodem/hsmodem.cpp b/hsmodem/hsmodem.cpp index ad2ad17..b865296 100755 --- a/hsmodem/hsmodem.cpp +++ b/hsmodem/hsmodem.cpp @@ -88,14 +88,18 @@ int marker = 1; int init_voice_result = 0; // number of audio device in libkmaudio -int io_capidx = 0; -int io_pbidx = 0; -int voice_capidx = 0; -int voice_pbidx = 0; +int io_capidx = -1; +int io_pbidx = -1; +int voice_capidx = -1; +int voice_pbidx = -1; int safemode = 0; int sendIntro = 0; +char mycallsign[21]; +char myqthloc[11]; +char myname[21]; + int main(int argc, char* argv[]) { int opt = 0; @@ -239,7 +243,7 @@ int main(int argc, char* argv[]) { // loop voice mic to LS, and record into PCM file float f[1100]; - while (1) + while (keeprunning) { int anz = kmaudio_readsamples(voice_capidx, f, 1000, micvol,0); if (anz > 0) @@ -324,7 +328,8 @@ typedef struct { } SPEEDRATE; // AudioRate, TX-Resampler, RX-Resampler/4, bit/symbol, Codec-Rate -SPEEDRATE sr[11] = { +#define NUMSPEEDMODES 11 +SPEEDRATE sr[NUMSPEEDMODES] = { // BPSK modes {48000, 40,10, 1, 1200, 800}, {48000, 20, 5, 1, 2400, 2000}, @@ -351,6 +356,8 @@ void startModem() close_dsp(); close_rtty(); speedmode = set_speedmode; + if (speedmode < 0 || speedmode >= NUMSPEEDMODES) + speedmode = 4; bitsPerSymbol = sr[speedmode].bpsym; constellationSize = (1 << bitsPerSymbol); // QPSK=4, 8PSK=8 @@ -398,12 +405,32 @@ void initVoice() float f = 0.0f; io_saveStream(&f, 1); // close recording close_voiceproc(); + close_stream(voice_capidx); + close_stream(voice_pbidx); } else { - - voice_capidx = kmaudio_startCapture(micDeviceName, VOICE_SAMPRATE); - voice_pbidx = kmaudio_startPlayback(lsDeviceName, VOICE_SAMPRATE); + int srate = 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(); } } @@ -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 void bc_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock) { + static uint64_t lastms = 0; // time of last received BC message uint64_t actms = getms(); + if (len > 0 && pdata[0] == 0x3c) { /* searchmodem message received @@ -462,6 +504,9 @@ void bc_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock) * 9 ... unused * 10 .. 109 ... PB device name * 110 .. 209 ... CAP device name + * 210 .. 229 ... Callsign + * 230 .. 239 ... qthloc + * 240 .. 259 ... Name */ 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 // so the app gets an UDP message with this local IP int alen; - uint8_t* txdata = io_getAudioDevicelist(&alen); + uint8_t* txdata = getDevList(&alen); sendUDP(appIP, UdpDataPort_ModemToApp, txdata, alen); } 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 // so the app gets an UDP message with this local IP int alen; - uint8_t* txdata = io_getAudioDevicelist(&alen); + uint8_t* txdata = getDevList(&alen); sendUDP(appIP, UdpDataPort_ModemToApp, txdata, alen); } else 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]); io_setAudioDevices(pdata[1], pdata[2], pdata[3], pdata[4], pdata[5], (char*)(pdata + 10), (char*)(pdata + 110)); 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); 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; } @@ -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 toGR_sendData(pdata + 2, type, minfo,0); int numframespreamble = 6 * ((caprate / txinterpolfactor) * bitsPerSymbol / 8) / 258 + 1; - if (type == 1)// BER Test - numframespreamble = 1; + //if (type == 1)// BER Test + // numframespreamble = 1; for (int i = 0; i < numframespreamble; i++) toGR_sendData(pdata + 2, type, minfo,1); + sendStationInfo(); } else if ((len - 2) < PAYLOADLEN) { @@ -781,11 +850,29 @@ void toGR_sendData(uint8_t* data, int type, int status, int repeat) int len = 0; 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); } +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 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) { // complete frame received + //printf("type:%d\n", pl[0]); // send payload to app uint8_t txpl[PAYLOADLEN + 10 + 1]; 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; if (speedmode < 10) { - printf("no signal detected, reset RX modem\n"); + //printf("no signal detected, reset RX modem\n"); resetModem(); } lasttime = acttm; diff --git a/hsmodem/hsmodem.h b/hsmodem/hsmodem.h index 2c3d0a6..2cbd553 100755 --- a/hsmodem/hsmodem.h +++ b/hsmodem/hsmodem.h @@ -3,6 +3,7 @@ #ifdef _WIN32 #define _WIN32_ // 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 : 4003 ) #else @@ -230,6 +231,7 @@ void clear_rtty_txfifo(); void fmtest(); void rtty_init_pipes(); void initVoice(); +void sendStationInfo(); extern int speedmode; diff --git a/hsmodem/libkmaudio/libkmaudio.h b/hsmodem/libkmaudio/libkmaudio.h index b3eadb0..333f6c8 100755 --- a/hsmodem/libkmaudio/libkmaudio.h +++ b/hsmodem/libkmaudio/libkmaudio.h @@ -179,13 +179,33 @@ uint8_t* io_getAudioDevicelist(int* len); */ 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); + +// returns number of used elements (audio 16 bit short values) 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); +// clear the fifo +void io_fifo_clear(int pipenum); + // -------- functions for internal use only -------- @@ -237,6 +257,8 @@ uint64_t getms(); void init_maxarray(); void kmaudio_detectDropouts(int id); 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 int devanz; diff --git a/hsmodem/libkmaudio/libkmaudio_capture.cpp b/hsmodem/libkmaudio/libkmaudio_capture.cpp index 1fb6c9a..be26da7 100755 --- a/hsmodem/libkmaudio/libkmaudio_capture.cpp +++ b/hsmodem/libkmaudio/libkmaudio_capture.cpp @@ -40,6 +40,15 @@ int recordCallback(const void* inputBuffer, void* outputBuffer, PaStreamCallbackFlags statusFlags, 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) { printf("Start request for CAP stream:%s\n", devname); @@ -59,12 +68,8 @@ int kmaudio_startCapture(char* devname, int samprate) devlist[idx].working = 0; - if (devlist[idx].capStream != NULL) - { - printf("Closing old CAP stream:%s [%d]\n", devname, idx); - Pa_CloseStream(devlist[idx].capStream); - devlist[idx].capStream = NULL; - } + close_capture_stream(idx); + printf("Starting CAP stream:%s [%d]\n", devname, idx); io_fifo_clear(idx); diff --git a/hsmodem/libkmaudio/libkmaudio_capture_linux.cpp b/hsmodem/libkmaudio/libkmaudio_capture_linux.cpp index 7005e8b..b586ddd 100755 --- a/hsmodem/libkmaudio/libkmaudio_capture_linux.cpp +++ b/hsmodem/libkmaudio/libkmaudio_capture_linux.cpp @@ -112,6 +112,15 @@ void overflow_callback(struct SoundIoInStream* instream) 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) { 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 an old stream is open, close it - if (devlist[idx].instream != NULL) - { - printf("Closing old CAP stream:%s [%d]\n", devname, idx); - soundio_instream_destroy(devlist[idx].instream); - devlist[idx].instream = NULL; - } + close_capture_stream(idx); + printf("Starting CAP stream:%s [%d]\n", devname, idx); io_fifo_clear(idx); diff --git a/hsmodem/libkmaudio/libkmaudio_fifo.cpp b/hsmodem/libkmaudio/libkmaudio_fifo.cpp index a46772d..aeaba6f 100755 --- a/hsmodem/libkmaudio/libkmaudio_fifo.cpp +++ b/hsmodem/libkmaudio/libkmaudio_fifo.cpp @@ -82,6 +82,8 @@ void init_pipes() // ignore data if the fifo is full void io_write_fifo(int pipenum, float sample) { + if (pipenum < 0 || pipenum >= NUM_OF_PIPES) return; + LOCK(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) { + if (pipenum < 0 || pipenum >= NUM_OF_PIPES) return; + LOCK(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) { + if (pipenum < 0 || pipenum >= NUM_OF_PIPES) return 0; + LOCK(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 int io_read_fifo_num(int pipenum, float* data, int num) { + if (pipenum < 0 || pipenum >= NUM_OF_PIPES) return 0; + LOCK(pipenum); 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) { + if (pipenum < 0 || pipenum >= NUM_OF_PIPES) return 0; + LOCK(pipenum); 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) { + if (pipenum < 0 || pipenum >= NUM_OF_PIPES) return; + io_wridx[pipenum] = io_rdidx[pipenum] = 0; } int io_fifo_freespace(int pipenum) { + if (pipenum < 0 || pipenum >= NUM_OF_PIPES) return 0; + int freebuf = 0; LOCK(pipenum); @@ -211,6 +225,8 @@ int io_fifo_freespace(int pipenum) int io_fifo_elems_avail(int pipenum) { + if (pipenum < 0 || pipenum >= NUM_OF_PIPES) return 0; + int elems = 0; LOCK(pipenum); diff --git a/hsmodem/libkmaudio/libkmaudio_getDevices.cpp b/hsmodem/libkmaudio/libkmaudio_getDevices.cpp index 2cef52a..1e80b03 100755 --- a/hsmodem/libkmaudio/libkmaudio_getDevices.cpp +++ b/hsmodem/libkmaudio/libkmaudio_getDevices.cpp @@ -235,15 +235,17 @@ int getRealSamprate(int idx) // 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: ~ -// 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) uint8_t io_devstring[MAXDEVSTRLEN]; void io_buildAudioDevString() { 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 for (int i = 0; i < devanz; i++) @@ -256,7 +258,6 @@ void io_buildAudioDevString() } 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, "~"); // audio device separator } @@ -275,15 +276,12 @@ void io_buildAudioDevString() } 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, "~"); // audio device separator } } //printf("<%s>\n", (char *)io_devstring); - - io_devstring[0] = 3; // ID for this message } uint8_t* io_getAudioDevicelist(int* len) diff --git a/hsmodem/libkmaudio/libkmaudio_interface.cpp b/hsmodem/libkmaudio/libkmaudio_interface.cpp index d53c5a8..44116a3 100755 --- a/hsmodem/libkmaudio/libkmaudio_interface.cpp +++ b/hsmodem/libkmaudio/libkmaudio_interface.cpp @@ -3,6 +3,21 @@ 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 * returns: number of values written to psamp , -1=error @@ -124,7 +139,6 @@ uint8_t kmaudio_maxlevel(int id) void kmaudio_detectDropouts(int id) { - int dropout = 0; int stat = 0; int drlen = 0; diff --git a/hsmodem/libkmaudio/libkmaudio_playback.cpp b/hsmodem/libkmaudio/libkmaudio_playback.cpp index 67f6319..3d24d5f 100755 --- a/hsmodem/libkmaudio/libkmaudio_playback.cpp +++ b/hsmodem/libkmaudio/libkmaudio_playback.cpp @@ -42,6 +42,15 @@ int playCallback(const void* inputBuffer, PaStreamCallbackFlags statusFlags, 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) { printf("Start request for PB stream:%s\n", devname); @@ -61,12 +70,7 @@ int kmaudio_startPlayback(char* devname, int samprate) devlist[idx].working = 0; - if (devlist[idx].pbStream != NULL) - { - printf("Closing old PB stream:%s [%d]\n", devname, idx); - Pa_CloseStream(devlist[idx].pbStream); - devlist[idx].pbStream = NULL; - } + close_playback_stream(idx); printf("Starting PB stream:%s [%d]\n", devname, idx); diff --git a/hsmodem/libkmaudio/libkmaudio_playback_linux.cpp b/hsmodem/libkmaudio/libkmaudio_playback_linux.cpp index 18f064e..e5af6ae 100755 --- a/hsmodem/libkmaudio/libkmaudio_playback_linux.cpp +++ b/hsmodem/libkmaudio/libkmaudio_playback_linux.cpp @@ -136,6 +136,15 @@ void underflow_callback(struct SoundIoOutStream* outstream) 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) { 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); if (pbdevid == NULL) return -1; - // if an old stream is open, close it - 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; - } + close_playback_stream(idx); printf("Starting PB stream:%s [%d]\n", devname, idx); diff --git a/hsmodem/liquid_if.cpp b/hsmodem/liquid_if.cpp index 567b58b..064254c 100755 --- a/hsmodem/liquid_if.cpp +++ b/hsmodem/liquid_if.cpp @@ -202,12 +202,14 @@ void modulator(uint8_t sym_in) // adapt speed to soundcard samplerate int fs; + int to = 0; while(keeprunning) { fs = io_fifo_freespace(io_pbidx); // wait until there is space in fifo if(fs > 10) break; sleep_ms(10); + if (++to >= 400) break; // give up after 4s } if (marker) diff --git a/hsmodem/soundio.h b/hsmodem/soundio.h deleted file mode 100755 index 71b85ad..0000000 --- a/hsmodem/soundio.h +++ /dev/null @@ -1,1209 +0,0 @@ -/* - * Copyright (c) 2015 Andrew Kelley - * - * This file is part of libsoundio, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#ifndef SOUNDIO_SOUNDIO_H -#define SOUNDIO_SOUNDIO_H - -#include "endian.h" -#include - -/// \cond -#ifdef __cplusplus -#define SOUNDIO_EXTERN_C extern "C" -#else -#define SOUNDIO_EXTERN_C -#endif - -#if defined(SOUNDIO_STATIC_LIBRARY) -# define SOUNDIO_EXPORT SOUNDIO_EXTERN_C -#else -# if defined(_WIN32) -# if defined(SOUNDIO_BUILDING_LIBRARY) -# define SOUNDIO_EXPORT SOUNDIO_EXTERN_C __declspec(dllexport) -# else -# define SOUNDIO_EXPORT SOUNDIO_EXTERN_C __declspec(dllimport) -# endif -# else -# define SOUNDIO_EXPORT SOUNDIO_EXTERN_C __attribute__((visibility ("default"))) -# endif -#endif -/// \endcond - -/** \mainpage - * - * \section intro_sec Overview - * - * libsoundio is a C library for cross-platform audio input and output. It is - * suitable for real-time and consumer software. - * - * Documentation: soundio.h - */ - - -/** \example sio_list_devices.c - * List the available input and output devices on the system and their - * properties. Supports watching for changes and specifying backend to use. - */ - -/** \example sio_sine.c - * Play a sine wave over the default output device. - * Supports specifying device and backend to use. - */ - -/** \example sio_record.c - * Record audio to an output file. - * Supports specifying device and backend to use. - */ - -/** \example sio_microphone.c - * Stream the default input device over the default output device. - * Supports specifying device and backend to use. - */ - -/** \example backend_disconnect_recover.c - * Demonstrates recovering from a backend disconnecting. - */ - -/// See also ::soundio_strerror -enum SoundIoError { - SoundIoErrorNone, - /// Out of memory. - SoundIoErrorNoMem, - /// The backend does not appear to be active or running. - SoundIoErrorInitAudioBackend, - /// A system resource other than memory was not available. - SoundIoErrorSystemResources, - /// Attempted to open a device and failed. - SoundIoErrorOpeningDevice, - SoundIoErrorNoSuchDevice, - /// The programmer did not comply with the API. - SoundIoErrorInvalid, - /// libsoundio was compiled without support for that backend. - SoundIoErrorBackendUnavailable, - /// An open stream had an error that can only be recovered from by - /// destroying the stream and creating it again. - SoundIoErrorStreaming, - /// Attempted to use a device with parameters it cannot support. - SoundIoErrorIncompatibleDevice, - /// When JACK returns `JackNoSuchClient` - SoundIoErrorNoSuchClient, - /// Attempted to use parameters that the backend cannot support. - SoundIoErrorIncompatibleBackend, - /// Backend server shutdown or became inactive. - SoundIoErrorBackendDisconnected, - SoundIoErrorInterrupted, - /// Buffer underrun occurred. - SoundIoErrorUnderflow, - /// Unable to convert to or from UTF-8 to the native string format. - SoundIoErrorEncodingString, -}; - -/// Specifies where a channel is physically located. -enum SoundIoChannelId { - SoundIoChannelIdInvalid, - - SoundIoChannelIdFrontLeft, ///< First of the more commonly supported ids. - SoundIoChannelIdFrontRight, - SoundIoChannelIdFrontCenter, - SoundIoChannelIdLfe, - SoundIoChannelIdBackLeft, - SoundIoChannelIdBackRight, - SoundIoChannelIdFrontLeftCenter, - SoundIoChannelIdFrontRightCenter, - SoundIoChannelIdBackCenter, - SoundIoChannelIdSideLeft, - SoundIoChannelIdSideRight, - SoundIoChannelIdTopCenter, - SoundIoChannelIdTopFrontLeft, - SoundIoChannelIdTopFrontCenter, - SoundIoChannelIdTopFrontRight, - SoundIoChannelIdTopBackLeft, - SoundIoChannelIdTopBackCenter, - SoundIoChannelIdTopBackRight, ///< Last of the more commonly supported ids. - - SoundIoChannelIdBackLeftCenter, ///< First of the less commonly supported ids. - SoundIoChannelIdBackRightCenter, - SoundIoChannelIdFrontLeftWide, - SoundIoChannelIdFrontRightWide, - SoundIoChannelIdFrontLeftHigh, - SoundIoChannelIdFrontCenterHigh, - SoundIoChannelIdFrontRightHigh, - SoundIoChannelIdTopFrontLeftCenter, - SoundIoChannelIdTopFrontRightCenter, - SoundIoChannelIdTopSideLeft, - SoundIoChannelIdTopSideRight, - SoundIoChannelIdLeftLfe, - SoundIoChannelIdRightLfe, - SoundIoChannelIdLfe2, - SoundIoChannelIdBottomCenter, - SoundIoChannelIdBottomLeftCenter, - SoundIoChannelIdBottomRightCenter, - - /// Mid/side recording - SoundIoChannelIdMsMid, - SoundIoChannelIdMsSide, - - /// first order ambisonic channels - SoundIoChannelIdAmbisonicW, - SoundIoChannelIdAmbisonicX, - SoundIoChannelIdAmbisonicY, - SoundIoChannelIdAmbisonicZ, - - /// X-Y Recording - SoundIoChannelIdXyX, - SoundIoChannelIdXyY, - - SoundIoChannelIdHeadphonesLeft, ///< First of the "other" channel ids - SoundIoChannelIdHeadphonesRight, - SoundIoChannelIdClickTrack, - SoundIoChannelIdForeignLanguage, - SoundIoChannelIdHearingImpaired, - SoundIoChannelIdNarration, - SoundIoChannelIdHaptic, - SoundIoChannelIdDialogCentricMix, ///< Last of the "other" channel ids - - SoundIoChannelIdAux, - SoundIoChannelIdAux0, - SoundIoChannelIdAux1, - SoundIoChannelIdAux2, - SoundIoChannelIdAux3, - SoundIoChannelIdAux4, - SoundIoChannelIdAux5, - SoundIoChannelIdAux6, - SoundIoChannelIdAux7, - SoundIoChannelIdAux8, - SoundIoChannelIdAux9, - SoundIoChannelIdAux10, - SoundIoChannelIdAux11, - SoundIoChannelIdAux12, - SoundIoChannelIdAux13, - SoundIoChannelIdAux14, - SoundIoChannelIdAux15, -}; - -/// Built-in channel layouts for convenience. -enum SoundIoChannelLayoutId { - SoundIoChannelLayoutIdMono, - SoundIoChannelLayoutIdStereo, - SoundIoChannelLayoutId2Point1, - SoundIoChannelLayoutId3Point0, - SoundIoChannelLayoutId3Point0Back, - SoundIoChannelLayoutId3Point1, - SoundIoChannelLayoutId4Point0, - SoundIoChannelLayoutIdQuad, - SoundIoChannelLayoutIdQuadSide, - SoundIoChannelLayoutId4Point1, - SoundIoChannelLayoutId5Point0Back, - SoundIoChannelLayoutId5Point0Side, - SoundIoChannelLayoutId5Point1, - SoundIoChannelLayoutId5Point1Back, - SoundIoChannelLayoutId6Point0Side, - SoundIoChannelLayoutId6Point0Front, - SoundIoChannelLayoutIdHexagonal, - SoundIoChannelLayoutId6Point1, - SoundIoChannelLayoutId6Point1Back, - SoundIoChannelLayoutId6Point1Front, - SoundIoChannelLayoutId7Point0, - SoundIoChannelLayoutId7Point0Front, - SoundIoChannelLayoutId7Point1, - SoundIoChannelLayoutId7Point1Wide, - SoundIoChannelLayoutId7Point1WideBack, - SoundIoChannelLayoutIdOctagonal, -}; - -enum SoundIoBackend { - SoundIoBackendNone, - SoundIoBackendJack, - SoundIoBackendPulseAudio, - SoundIoBackendAlsa, - SoundIoBackendCoreAudio, - SoundIoBackendWasapi, - SoundIoBackendDummy, -}; - -enum SoundIoDeviceAim { - SoundIoDeviceAimInput, ///< capture / recording - SoundIoDeviceAimOutput, ///< playback -}; - -/// For your convenience, Native Endian and Foreign Endian constants are defined -/// which point to the respective SoundIoFormat values. -enum SoundIoFormat { - SoundIoFormatInvalid, - SoundIoFormatS8, ///< Signed 8 bit - SoundIoFormatU8, ///< Unsigned 8 bit - SoundIoFormatS16LE, ///< Signed 16 bit Little Endian - SoundIoFormatS16BE, ///< Signed 16 bit Big Endian - SoundIoFormatU16LE, ///< Unsigned 16 bit Little Endian - SoundIoFormatU16BE, ///< Unsigned 16 bit Big Endian - SoundIoFormatS24LE, ///< Signed 24 bit Little Endian using low three bytes in 32-bit word - SoundIoFormatS24BE, ///< Signed 24 bit Big Endian using low three bytes in 32-bit word - SoundIoFormatU24LE, ///< Unsigned 24 bit Little Endian using low three bytes in 32-bit word - SoundIoFormatU24BE, ///< Unsigned 24 bit Big Endian using low three bytes in 32-bit word - SoundIoFormatS32LE, ///< Signed 32 bit Little Endian - SoundIoFormatS32BE, ///< Signed 32 bit Big Endian - SoundIoFormatU32LE, ///< Unsigned 32 bit Little Endian - SoundIoFormatU32BE, ///< Unsigned 32 bit Big Endian - SoundIoFormatFloat32LE, ///< Float 32 bit Little Endian, Range -1.0 to 1.0 - SoundIoFormatFloat32BE, ///< Float 32 bit Big Endian, Range -1.0 to 1.0 - SoundIoFormatFloat64LE, ///< Float 64 bit Little Endian, Range -1.0 to 1.0 - SoundIoFormatFloat64BE, ///< Float 64 bit Big Endian, Range -1.0 to 1.0 -}; - -#if defined(SOUNDIO_OS_BIG_ENDIAN) -#define SoundIoFormatS16NE SoundIoFormatS16BE -#define SoundIoFormatU16NE SoundIoFormatU16BE -#define SoundIoFormatS24NE SoundIoFormatS24BE -#define SoundIoFormatU24NE SoundIoFormatU24BE -#define SoundIoFormatS32NE SoundIoFormatS32BE -#define SoundIoFormatU32NE SoundIoFormatU32BE -#define SoundIoFormatFloat32NE SoundIoFormatFloat32BE -#define SoundIoFormatFloat64NE SoundIoFormatFloat64BE - -#define SoundIoFormatS16FE SoundIoFormatS16LE -#define SoundIoFormatU16FE SoundIoFormatU16LE -#define SoundIoFormatS24FE SoundIoFormatS24LE -#define SoundIoFormatU24FE SoundIoFormatU24LE -#define SoundIoFormatS32FE SoundIoFormatS32LE -#define SoundIoFormatU32FE SoundIoFormatU32LE -#define SoundIoFormatFloat32FE SoundIoFormatFloat32LE -#define SoundIoFormatFloat64FE SoundIoFormatFloat64LE - -#elif defined(SOUNDIO_OS_LITTLE_ENDIAN) - -/// Note that we build the documentation in Little Endian mode, -/// so all the "NE" macros in the docs point to "LE" and -/// "FE" macros point to "BE". On a Big Endian system it is the -/// other way around. -#define SoundIoFormatS16NE SoundIoFormatS16LE -#define SoundIoFormatU16NE SoundIoFormatU16LE -#define SoundIoFormatS24NE SoundIoFormatS24LE -#define SoundIoFormatU24NE SoundIoFormatU24LE -#define SoundIoFormatS32NE SoundIoFormatS32LE -#define SoundIoFormatU32NE SoundIoFormatU32LE -#define SoundIoFormatFloat32NE SoundIoFormatFloat32LE -#define SoundIoFormatFloat64NE SoundIoFormatFloat64LE - -#define SoundIoFormatS16FE SoundIoFormatS16BE -#define SoundIoFormatU16FE SoundIoFormatU16BE -#define SoundIoFormatS24FE SoundIoFormatS24BE -#define SoundIoFormatU24FE SoundIoFormatU24BE -#define SoundIoFormatS32FE SoundIoFormatS32BE -#define SoundIoFormatU32FE SoundIoFormatU32BE -#define SoundIoFormatFloat32FE SoundIoFormatFloat32BE -#define SoundIoFormatFloat64FE SoundIoFormatFloat64BE - -#else -#error unknown byte order -#endif - -#define SOUNDIO_MAX_CHANNELS 24 -/// The size of this struct is OK to use. -struct SoundIoChannelLayout { - const char *name; - int channel_count; - enum SoundIoChannelId channels[SOUNDIO_MAX_CHANNELS]; -}; - -/// The size of this struct is OK to use. -struct SoundIoSampleRateRange { - int min; - int max; -}; - -/// The size of this struct is OK to use. -struct SoundIoChannelArea { - /// Base address of buffer. - char *ptr; - /// How many bytes it takes to get from the beginning of one sample to - /// the beginning of the next sample. - int step; -}; - -/// The size of this struct is not part of the API or ABI. -struct SoundIo { - /// Optional. Put whatever you want here. Defaults to NULL. - void *userdata; - /// Optional callback. Called when the list of devices change. Only called - /// during a call to ::soundio_flush_events or ::soundio_wait_events. - void (*on_devices_change)(struct SoundIo *); - /// Optional callback. Called when the backend disconnects. For example, - /// when the JACK server shuts down. When this happens, listing devices - /// and opening streams will always fail with - /// SoundIoErrorBackendDisconnected. This callback is only called during a - /// call to ::soundio_flush_events or ::soundio_wait_events. - /// If you do not supply a callback, the default will crash your program - /// with an error message. This callback is also called when the thread - /// that retrieves device information runs into an unrecoverable condition - /// such as running out of memory. - /// - /// Possible errors: - /// * #SoundIoErrorBackendDisconnected - /// * #SoundIoErrorNoMem - /// * #SoundIoErrorSystemResources - /// * #SoundIoErrorOpeningDevice - unexpected problem accessing device - /// information - void (*on_backend_disconnect)(struct SoundIo *, int err); - /// Optional callback. Called from an unknown thread that you should not use - /// to call any soundio functions. You may use this to signal a condition - /// variable to wake up. Called when ::soundio_wait_events would be woken up. - void (*on_events_signal)(struct SoundIo *); - - /// Read-only. After calling ::soundio_connect or ::soundio_connect_backend, - /// this field tells which backend is currently connected. - enum SoundIoBackend current_backend; - - /// Optional: Application name. - /// PulseAudio uses this for "application name". - /// JACK uses this for `client_name`. - /// Must not contain a colon (":"). - const char *app_name; - - /// Optional: Real time priority warning. - /// This callback is fired when making thread real-time priority failed. By - /// default, it will print to stderr only the first time it is called - /// a message instructing the user how to configure their system to allow - /// real-time priority threads. This must be set to a function not NULL. - /// To silence the warning, assign this to a function that does nothing. - void (*emit_rtprio_warning)(void); - - /// Optional: JACK info callback. - /// By default, libsoundio sets this to an empty function in order to - /// silence stdio messages from JACK. You may override the behavior by - /// setting this to `NULL` or providing your own function. This is - /// registered with JACK regardless of whether ::soundio_connect_backend - /// succeeds. - void (*jack_info_callback)(const char *msg); - /// Optional: JACK error callback. - /// See SoundIo::jack_info_callback - void (*jack_error_callback)(const char *msg); -}; - -/// The size of this struct is not part of the API or ABI. -struct SoundIoDevice { - /// Read-only. Set automatically. - struct SoundIo *soundio; - - /// A string of bytes that uniquely identifies this device. - /// If the same physical device supports both input and output, that makes - /// one SoundIoDevice for the input and one SoundIoDevice for the output. - /// In this case, the id of each SoundIoDevice will be the same, and - /// SoundIoDevice::aim will be different. Additionally, if the device - /// supports raw mode, there may be up to four devices with the same id: - /// one for each value of SoundIoDevice::is_raw and one for each value of - /// SoundIoDevice::aim. - char *id; - /// User-friendly UTF-8 encoded text to describe the device. - char *name; - - /// Tells whether this device is an input device or an output device. - enum SoundIoDeviceAim aim; - - /// Channel layouts are handled similarly to SoundIoDevice::formats. - /// If this information is missing due to a SoundIoDevice::probe_error, - /// layouts will be NULL. It's OK to modify this data, for example calling - /// ::soundio_sort_channel_layouts on it. - /// Devices are guaranteed to have at least 1 channel layout. - struct SoundIoChannelLayout *layouts; - int layout_count; - /// See SoundIoDevice::current_format - struct SoundIoChannelLayout current_layout; - - /// List of formats this device supports. See also - /// SoundIoDevice::current_format. - enum SoundIoFormat *formats; - /// How many formats are available in SoundIoDevice::formats. - int format_count; - /// A device is either a raw device or it is a virtual device that is - /// provided by a software mixing service such as dmix or PulseAudio (see - /// SoundIoDevice::is_raw). If it is a raw device, - /// current_format is meaningless; - /// the device has no current format until you open it. On the other hand, - /// if it is a virtual device, current_format describes the - /// destination sample format that your audio will be converted to. Or, - /// if you're the lucky first application to open the device, you might - /// cause the current_format to change to your format. - /// Generally, you want to ignore current_format and use - /// whatever format is most convenient - /// for you which is supported by the device, because when you are the only - /// application left, the mixer might decide to switch - /// current_format to yours. You can learn the supported formats via - /// formats and SoundIoDevice::format_count. If this information is missing - /// due to a probe error, formats will be `NULL`. If current_format is - /// unavailable, it will be set to #SoundIoFormatInvalid. - /// Devices are guaranteed to have at least 1 format available. - enum SoundIoFormat current_format; - - /// Sample rate is the number of frames per second. - /// Sample rate is handled very similar to SoundIoDevice::formats. - /// If sample rate information is missing due to a probe error, the field - /// will be set to NULL. - /// Devices which have SoundIoDevice::probe_error set to #SoundIoErrorNone are - /// guaranteed to have at least 1 sample rate available. - struct SoundIoSampleRateRange *sample_rates; - /// How many sample rate ranges are available in - /// SoundIoDevice::sample_rates. 0 if sample rate information is missing - /// due to a probe error. - int sample_rate_count; - /// See SoundIoDevice::current_format - /// 0 if sample rate information is missing due to a probe error. - int sample_rate_current; - - /// Software latency minimum in seconds. If this value is unknown or - /// irrelevant, it is set to 0.0. - /// For PulseAudio and WASAPI this value is unknown until you open a - /// stream. - double software_latency_min; - /// Software latency maximum in seconds. If this value is unknown or - /// irrelevant, it is set to 0.0. - /// For PulseAudio and WASAPI this value is unknown until you open a - /// stream. - double software_latency_max; - /// Software latency in seconds. If this value is unknown or - /// irrelevant, it is set to 0.0. - /// For PulseAudio and WASAPI this value is unknown until you open a - /// stream. - /// See SoundIoDevice::current_format - double software_latency_current; - - /// Raw means that you are directly opening the hardware device and not - /// going through a proxy such as dmix, PulseAudio, or JACK. When you open a - /// raw device, other applications on the computer are not able to - /// simultaneously access the device. Raw devices do not perform automatic - /// resampling and thus tend to have fewer formats available. - bool is_raw; - - /// Devices are reference counted. See ::soundio_device_ref and - /// ::soundio_device_unref. - int ref_count; - - /// This is set to a SoundIoError representing the result of the device - /// probe. Ideally this will be SoundIoErrorNone in which case all the - /// fields of the device will be populated. If there is an error code here - /// then information about formats, sample rates, and channel layouts might - /// be missing. - /// - /// Possible errors: - /// * #SoundIoErrorOpeningDevice - /// * #SoundIoErrorNoMem - int probe_error; -}; - -/// The size of this struct is not part of the API or ABI. -struct SoundIoOutStream { - /// Populated automatically when you call ::soundio_outstream_create. - struct SoundIoDevice *device; - - /// Defaults to #SoundIoFormatFloat32NE, followed by the first one - /// supported. - enum SoundIoFormat format; - - /// Sample rate is the number of frames per second. - /// Defaults to 48000 (and then clamped into range). - int sample_rate; - - /// Defaults to Stereo, if available, followed by the first layout - /// supported. - struct SoundIoChannelLayout layout; - - /// Ignoring hardware latency, this is the number of seconds it takes for - /// the last sample in a full buffer to be played. - /// After you call ::soundio_outstream_open, this value is replaced with the - /// actual software latency, as near to this value as possible. - /// On systems that support clearing the buffer, this defaults to a large - /// latency, potentially upwards of 2 seconds, with the understanding that - /// you will call ::soundio_outstream_clear_buffer when you want to reduce - /// the latency to 0. On systems that do not support clearing the buffer, - /// this defaults to a reasonable lower latency value. - /// - /// On backends with high latencies (such as 2 seconds), `frame_count_min` - /// will be 0, meaning you don't have to fill the entire buffer. In this - /// case, the large buffer is there if you want it; you only have to fill - /// as much as you want. On backends like JACK, `frame_count_min` will be - /// equal to `frame_count_max` and if you don't fill that many frames, you - /// will get glitches. - /// - /// If the device has unknown software latency min and max values, you may - /// still set this, but you might not get the value you requested. - /// For PulseAudio, if you set this value to non-default, it sets - /// `PA_STREAM_ADJUST_LATENCY` and is the value used for `maxlength` and - /// `tlength`. - /// - /// For JACK, this value is always equal to - /// SoundIoDevice::software_latency_current of the device. - double software_latency; - /// Core Audio and WASAPI only: current output Audio Unit volume. Float, 0.0-1.0. - float volume; - /// Defaults to NULL. Put whatever you want here. - void *userdata; - /// In this callback, you call ::soundio_outstream_begin_write and - /// ::soundio_outstream_end_write as many times as necessary to write - /// at minimum `frame_count_min` frames and at maximum `frame_count_max` - /// frames. `frame_count_max` will always be greater than 0. Note that you - /// should write as many frames as you can; `frame_count_min` might be 0 and - /// you can still get a buffer underflow if you always write - /// `frame_count_min` frames. - /// - /// For Dummy, ALSA, and PulseAudio, `frame_count_min` will be 0. For JACK - /// and CoreAudio `frame_count_min` will be equal to `frame_count_max`. - /// - /// The code in the supplied function must be suitable for real-time - /// execution. That means that it cannot call functions that might block - /// for a long time. This includes all I/O functions (disk, TTY, network), - /// malloc, free, printf, pthread_mutex_lock, sleep, wait, poll, select, - /// pthread_join, pthread_cond_wait, etc. - void (*write_callback)(struct SoundIoOutStream *, - int frame_count_min, int frame_count_max); - /// This optional callback happens when the sound device runs out of - /// buffered audio data to play. After this occurs, the outstream waits - /// until the buffer is full to resume playback. - /// This is called from the SoundIoOutStream::write_callback thread context. - void (*underflow_callback)(struct SoundIoOutStream *); - /// Optional callback. `err` is always SoundIoErrorStreaming. - /// SoundIoErrorStreaming is an unrecoverable error. The stream is in an - /// invalid state and must be destroyed. - /// If you do not supply error_callback, the default callback will print - /// a message to stderr and then call `abort`. - /// This is called from the SoundIoOutStream::write_callback thread context. - void (*error_callback)(struct SoundIoOutStream *, int err); - - /// Optional: Name of the stream. Defaults to "SoundIoOutStream" - /// PulseAudio uses this for the stream name. - /// JACK uses this for the client name of the client that connects when you - /// open the stream. - /// WASAPI uses this for the session display name. - /// Must not contain a colon (":"). - const char *name; - - /// Optional: Hint that this output stream is nonterminal. This is used by - /// JACK and it means that the output stream data originates from an input - /// stream. Defaults to `false`. - bool non_terminal_hint; - - - /// computed automatically when you call ::soundio_outstream_open - int bytes_per_frame; - /// computed automatically when you call ::soundio_outstream_open - int bytes_per_sample; - - /// If setting the channel layout fails for some reason, this field is set - /// to an error code. Possible error codes are: - /// * #SoundIoErrorIncompatibleDevice - int layout_error; -}; - -/// The size of this struct is not part of the API or ABI. -struct SoundIoInStream { - /// Populated automatically when you call ::soundio_outstream_create. - struct SoundIoDevice *device; - - /// Defaults to #SoundIoFormatFloat32NE, followed by the first one - /// supported. - enum SoundIoFormat format; - - /// Sample rate is the number of frames per second. - /// Defaults to max(sample_rate_min, min(sample_rate_max, 48000)) - int sample_rate; - - /// Defaults to Stereo, if available, followed by the first layout - /// supported. - struct SoundIoChannelLayout layout; - - /// Ignoring hardware latency, this is the number of seconds it takes for a - /// captured sample to become available for reading. - /// After you call ::soundio_instream_open, this value is replaced with the - /// actual software latency, as near to this value as possible. - /// A higher value means less CPU usage. Defaults to a large value, - /// potentially upwards of 2 seconds. - /// If the device has unknown software latency min and max values, you may - /// still set this, but you might not get the value you requested. - /// For PulseAudio, if you set this value to non-default, it sets - /// `PA_STREAM_ADJUST_LATENCY` and is the value used for `fragsize`. - /// For JACK, this value is always equal to - /// SoundIoDevice::software_latency_current - double software_latency; - - /// Defaults to NULL. Put whatever you want here. - void *userdata; - - - /// In this function call ::soundio_instream_begin_read and - /// ::soundio_instream_end_read as many times as necessary to read at - /// minimum `frame_count_min` frames and at maximum `frame_count_max` - /// frames. If you return from read_callback without having read - /// `frame_count_min`, the frames will be dropped. `frame_count_max` is how - /// many frames are available to read. - /// - /// The code in the supplied function must be suitable for real-time - /// execution. That means that it cannot call functions that might block - /// for a long time. This includes all I/O functions (disk, TTY, network), - /// malloc, free, printf, pthread_mutex_lock, sleep, wait, poll, select, - /// pthread_join, pthread_cond_wait, etc. - void (*read_callback)(struct SoundIoInStream *, int frame_count_min, int frame_count_max); - /// This optional callback happens when the sound device buffer is full, - /// yet there is more captured audio to put in it. - /// This is never fired for PulseAudio. - /// This is called from the SoundIoInStream::read_callback thread context. - void (*overflow_callback)(struct SoundIoInStream *); - /// Optional callback. `err` is always SoundIoErrorStreaming. - /// SoundIoErrorStreaming is an unrecoverable error. The stream is in an - /// invalid state and must be destroyed. - /// If you do not supply `error_callback`, the default callback will print - /// a message to stderr and then abort(). - /// This is called from the SoundIoInStream::read_callback thread context. - void (*error_callback)(struct SoundIoInStream *, int err); - - /// Optional: Name of the stream. Defaults to "SoundIoInStream"; - /// PulseAudio uses this for the stream name. - /// JACK uses this for the client name of the client that connects when you - /// open the stream. - /// WASAPI uses this for the session display name. - /// Must not contain a colon (":"). - const char *name; - - /// Optional: Hint that this input stream is nonterminal. This is used by - /// JACK and it means that the data received by the stream will be - /// passed on or made available to another stream. Defaults to `false`. - bool non_terminal_hint; - - /// computed automatically when you call ::soundio_instream_open - int bytes_per_frame; - /// computed automatically when you call ::soundio_instream_open - int bytes_per_sample; - - /// If setting the channel layout fails for some reason, this field is set - /// to an error code. Possible error codes are: #SoundIoErrorIncompatibleDevice - int layout_error; -}; - -/// See also ::soundio_version_major, ::soundio_version_minor, ::soundio_version_patch -SOUNDIO_EXPORT const char *soundio_version_string(void); -/// See also ::soundio_version_string, ::soundio_version_minor, ::soundio_version_patch -SOUNDIO_EXPORT int soundio_version_major(void); -/// See also ::soundio_version_major, ::soundio_version_string, ::soundio_version_patch -SOUNDIO_EXPORT int soundio_version_minor(void); -/// See also ::soundio_version_major, ::soundio_version_minor, ::soundio_version_string -SOUNDIO_EXPORT int soundio_version_patch(void); - -/// Create a SoundIo context. You may create multiple instances of this to -/// connect to multiple backends. Sets all fields to defaults. -/// Returns `NULL` if and only if memory could not be allocated. -/// See also ::soundio_destroy -SOUNDIO_EXPORT struct SoundIo *soundio_create(void); -SOUNDIO_EXPORT void soundio_destroy(struct SoundIo *soundio); - - -/// Tries ::soundio_connect_backend on all available backends in order. -/// Possible errors: -/// * #SoundIoErrorInvalid - already connected -/// * #SoundIoErrorNoMem -/// * #SoundIoErrorSystemResources -/// * #SoundIoErrorNoSuchClient - when JACK returns `JackNoSuchClient` -/// See also ::soundio_disconnect -SOUNDIO_EXPORT int soundio_connect(struct SoundIo *soundio); -/// Instead of calling ::soundio_connect you may call this function to try a -/// specific backend. -/// Possible errors: -/// * #SoundIoErrorInvalid - already connected or invalid backend parameter -/// * #SoundIoErrorNoMem -/// * #SoundIoErrorBackendUnavailable - backend was not compiled in -/// * #SoundIoErrorSystemResources -/// * #SoundIoErrorNoSuchClient - when JACK returns `JackNoSuchClient` -/// * #SoundIoErrorInitAudioBackend - requested `backend` is not active -/// * #SoundIoErrorBackendDisconnected - backend disconnected while connecting -/// See also ::soundio_disconnect -SOUNDIO_EXPORT int soundio_connect_backend(struct SoundIo *soundio, enum SoundIoBackend backend); -SOUNDIO_EXPORT void soundio_disconnect(struct SoundIo *soundio); - -/// Get a string representation of a #SoundIoError -SOUNDIO_EXPORT const char *soundio_strerror(int error); -/// Get a string representation of a #SoundIoBackend -SOUNDIO_EXPORT const char *soundio_backend_name(enum SoundIoBackend backend); - -/// Returns the number of available backends. -SOUNDIO_EXPORT int soundio_backend_count(struct SoundIo *soundio); -/// get the available backend at the specified index -/// (0 <= index < ::soundio_backend_count) -SOUNDIO_EXPORT enum SoundIoBackend soundio_get_backend(struct SoundIo *soundio, int index); - -/// Returns whether libsoundio was compiled with backend. -SOUNDIO_EXPORT bool soundio_have_backend(enum SoundIoBackend backend); - -/// Atomically update information for all connected devices. Note that calling -/// this function merely flips a pointer; the actual work of collecting device -/// information is done elsewhere. It is performant to call this function many -/// times per second. -/// -/// When you call this, the following callbacks might be called: -/// * SoundIo::on_devices_change -/// * SoundIo::on_backend_disconnect -/// This is the only time those callbacks can be called. -/// -/// This must be called from the same thread as the thread in which you call -/// these functions: -/// * ::soundio_input_device_count -/// * ::soundio_output_device_count -/// * ::soundio_get_input_device -/// * ::soundio_get_output_device -/// * ::soundio_default_input_device_index -/// * ::soundio_default_output_device_index -/// -/// Note that if you do not care about learning about updated devices, you -/// might call this function only once ever and never call -/// ::soundio_wait_events. -SOUNDIO_EXPORT void soundio_flush_events(struct SoundIo *soundio); - -/// This function calls ::soundio_flush_events then blocks until another event -/// is ready or you call ::soundio_wakeup. Be ready for spurious wakeups. -SOUNDIO_EXPORT void soundio_wait_events(struct SoundIo *soundio); - -/// Makes ::soundio_wait_events stop blocking. -SOUNDIO_EXPORT void soundio_wakeup(struct SoundIo *soundio); - - -/// If necessary you can manually trigger a device rescan. Normally you will -/// not ever have to call this function, as libsoundio listens to system events -/// for device changes and responds to them by rescanning devices and preparing -/// the new device information for you to be atomically replaced when you call -/// ::soundio_flush_events. However you might run into cases where you want to -/// force trigger a device rescan, for example if an ALSA device has a -/// SoundIoDevice::probe_error. -/// -/// After you call this you still have to use ::soundio_flush_events or -/// ::soundio_wait_events and then wait for the -/// SoundIo::on_devices_change callback. -/// -/// This can be called from any thread context except for -/// SoundIoOutStream::write_callback and SoundIoInStream::read_callback -SOUNDIO_EXPORT void soundio_force_device_scan(struct SoundIo *soundio); - - -// Channel Layouts - -/// Returns whether the channel count field and each channel id matches in -/// the supplied channel layouts. -SOUNDIO_EXPORT bool soundio_channel_layout_equal( - const struct SoundIoChannelLayout *a, - const struct SoundIoChannelLayout *b); - -SOUNDIO_EXPORT const char *soundio_get_channel_name(enum SoundIoChannelId id); -/// Given UTF-8 encoded text which is the name of a channel such as -/// "Front Left", "FL", or "front-left", return the corresponding -/// SoundIoChannelId. Returns SoundIoChannelIdInvalid for no match. -SOUNDIO_EXPORT enum SoundIoChannelId soundio_parse_channel_id(const char *str, int str_len); - -/// Returns the number of builtin channel layouts. -SOUNDIO_EXPORT int soundio_channel_layout_builtin_count(void); -/// Returns a builtin channel layout. 0 <= `index` < ::soundio_channel_layout_builtin_count -/// -/// Although `index` is of type `int`, it should be a valid -/// #SoundIoChannelLayoutId enum value. -SOUNDIO_EXPORT const struct SoundIoChannelLayout *soundio_channel_layout_get_builtin(int index); - -/// Get the default builtin channel layout for the given number of channels. -SOUNDIO_EXPORT const struct SoundIoChannelLayout *soundio_channel_layout_get_default(int channel_count); - -/// Return the index of `channel` in `layout`, or `-1` if not found. -SOUNDIO_EXPORT int soundio_channel_layout_find_channel( - const struct SoundIoChannelLayout *layout, enum SoundIoChannelId channel); - -/// Populates the name field of layout if it matches a builtin one. -/// returns whether it found a match -SOUNDIO_EXPORT bool soundio_channel_layout_detect_builtin(struct SoundIoChannelLayout *layout); - -/// Iterates over preferred_layouts. Returns the first channel layout in -/// preferred_layouts which matches one of the channel layouts in -/// available_layouts. Returns NULL if none matches. -SOUNDIO_EXPORT const struct SoundIoChannelLayout *soundio_best_matching_channel_layout( - const struct SoundIoChannelLayout *preferred_layouts, int preferred_layout_count, - const struct SoundIoChannelLayout *available_layouts, int available_layout_count); - -/// Sorts by channel count, descending. -SOUNDIO_EXPORT void soundio_sort_channel_layouts(struct SoundIoChannelLayout *layouts, int layout_count); - - -// Sample Formats - -/// Returns -1 on invalid format. -SOUNDIO_EXPORT int soundio_get_bytes_per_sample(enum SoundIoFormat format); - -/// A frame is one sample per channel. -static inline int soundio_get_bytes_per_frame(enum SoundIoFormat format, int channel_count) { - return soundio_get_bytes_per_sample(format) * channel_count; -} - -/// Sample rate is the number of frames per second. -static inline int soundio_get_bytes_per_second(enum SoundIoFormat format, - int channel_count, int sample_rate) -{ - return soundio_get_bytes_per_frame(format, channel_count) * sample_rate; -} - -/// Returns string representation of `format`. -SOUNDIO_EXPORT const char * soundio_format_string(enum SoundIoFormat format); - - - - -// Devices - -/// When you call ::soundio_flush_events, a snapshot of all device state is -/// saved and these functions merely access the snapshot data. When you want -/// to check for new devices, call ::soundio_flush_events. Or you can call -/// ::soundio_wait_events to block until devices change. If an error occurs -/// scanning devices in a background thread, SoundIo::on_backend_disconnect is called -/// with the error code. - -/// Get the number of input devices. -/// Returns -1 if you never called ::soundio_flush_events. -SOUNDIO_EXPORT int soundio_input_device_count(struct SoundIo *soundio); -/// Get the number of output devices. -/// Returns -1 if you never called ::soundio_flush_events. -SOUNDIO_EXPORT int soundio_output_device_count(struct SoundIo *soundio); - -/// Always returns a device. Call ::soundio_device_unref when done. -/// `index` must be 0 <= index < ::soundio_input_device_count -/// Returns NULL if you never called ::soundio_flush_events or if you provide -/// invalid parameter values. -SOUNDIO_EXPORT struct SoundIoDevice *soundio_get_input_device(struct SoundIo *soundio, int index); -/// Always returns a device. Call ::soundio_device_unref when done. -/// `index` must be 0 <= index < ::soundio_output_device_count -/// Returns NULL if you never called ::soundio_flush_events or if you provide -/// invalid parameter values. -SOUNDIO_EXPORT struct SoundIoDevice *soundio_get_output_device(struct SoundIo *soundio, int index); - -/// returns the index of the default input device -/// returns -1 if there are no devices or if you never called -/// ::soundio_flush_events. -SOUNDIO_EXPORT int soundio_default_input_device_index(struct SoundIo *soundio); - -/// returns the index of the default output device -/// returns -1 if there are no devices or if you never called -/// ::soundio_flush_events. -SOUNDIO_EXPORT int soundio_default_output_device_index(struct SoundIo *soundio); - -/// Add 1 to the reference count of `device`. -SOUNDIO_EXPORT void soundio_device_ref(struct SoundIoDevice *device); -/// Remove 1 to the reference count of `device`. Clean up if it was the last -/// reference. -SOUNDIO_EXPORT void soundio_device_unref(struct SoundIoDevice *device); - -/// Return `true` if and only if the devices have the same SoundIoDevice::id, -/// SoundIoDevice::is_raw, and SoundIoDevice::aim are the same. -SOUNDIO_EXPORT bool soundio_device_equal( - const struct SoundIoDevice *a, - const struct SoundIoDevice *b); - -/// Sorts channel layouts by channel count, descending. -SOUNDIO_EXPORT void soundio_device_sort_channel_layouts(struct SoundIoDevice *device); - -/// Convenience function. Returns whether `format` is included in the device's -/// supported formats. -SOUNDIO_EXPORT bool soundio_device_supports_format(struct SoundIoDevice *device, - enum SoundIoFormat format); - -/// Convenience function. Returns whether `layout` is included in the device's -/// supported channel layouts. -SOUNDIO_EXPORT bool soundio_device_supports_layout(struct SoundIoDevice *device, - const struct SoundIoChannelLayout *layout); - -/// Convenience function. Returns whether `sample_rate` is included in the -/// device's supported sample rates. -SOUNDIO_EXPORT bool soundio_device_supports_sample_rate(struct SoundIoDevice *device, - int sample_rate); - -/// Convenience function. Returns the available sample rate nearest to -/// `sample_rate`, rounding up. -SOUNDIO_EXPORT int soundio_device_nearest_sample_rate(struct SoundIoDevice *device, - int sample_rate); - - - -// Output Streams -/// Allocates memory and sets defaults. Next you should fill out the struct fields -/// and then call ::soundio_outstream_open. Sets all fields to defaults. -/// Returns `NULL` if and only if memory could not be allocated. -/// See also ::soundio_outstream_destroy -SOUNDIO_EXPORT struct SoundIoOutStream *soundio_outstream_create(struct SoundIoDevice *device); -/// You may not call this function from the SoundIoOutStream::write_callback thread context. -SOUNDIO_EXPORT void soundio_outstream_destroy(struct SoundIoOutStream *outstream); - -/// After you call this function, SoundIoOutStream::software_latency is set to -/// the correct value. -/// -/// The next thing to do is call ::soundio_outstream_start. -/// If this function returns an error, the outstream is in an invalid state and -/// you must call ::soundio_outstream_destroy on it. -/// -/// Possible errors: -/// * #SoundIoErrorInvalid -/// * SoundIoDevice::aim is not #SoundIoDeviceAimOutput -/// * SoundIoOutStream::format is not valid -/// * SoundIoOutStream::channel_count is greater than #SOUNDIO_MAX_CHANNELS -/// * #SoundIoErrorNoMem -/// * #SoundIoErrorOpeningDevice -/// * #SoundIoErrorBackendDisconnected -/// * #SoundIoErrorSystemResources -/// * #SoundIoErrorNoSuchClient - when JACK returns `JackNoSuchClient` -/// * #SoundIoErrorIncompatibleBackend - SoundIoOutStream::channel_count is -/// greater than the number of channels the backend can handle. -/// * #SoundIoErrorIncompatibleDevice - stream parameters requested are not -/// compatible with the chosen device. -SOUNDIO_EXPORT int soundio_outstream_open(struct SoundIoOutStream *outstream); - -/// After you call this function, SoundIoOutStream::write_callback will be called. -/// -/// This function might directly call SoundIoOutStream::write_callback. -/// -/// Possible errors: -/// * #SoundIoErrorStreaming -/// * #SoundIoErrorNoMem -/// * #SoundIoErrorSystemResources -/// * #SoundIoErrorBackendDisconnected -SOUNDIO_EXPORT int soundio_outstream_start(struct SoundIoOutStream *outstream); - -/// Call this function when you are ready to begin writing to the device buffer. -/// * `outstream` - (in) The output stream you want to write to. -/// * `areas` - (out) The memory addresses you can write data to, one per -/// channel. It is OK to modify the pointers if that helps you iterate. -/// * `frame_count` - (in/out) Provide the number of frames you want to write. -/// Returned will be the number of frames you can actually write, which is -/// also the number of frames that will be written when you call -/// ::soundio_outstream_end_write. The value returned will always be less -/// than or equal to the value provided. -/// It is your responsibility to call this function exactly as many times as -/// necessary to meet the `frame_count_min` and `frame_count_max` criteria from -/// SoundIoOutStream::write_callback. -/// You must call this function only from the SoundIoOutStream::write_callback thread context. -/// After calling this function, write data to `areas` and then call -/// ::soundio_outstream_end_write. -/// If this function returns an error, do not call ::soundio_outstream_end_write. -/// -/// Possible errors: -/// * #SoundIoErrorInvalid -/// * `*frame_count` <= 0 -/// * `*frame_count` < `frame_count_min` or `*frame_count` > `frame_count_max` -/// * function called too many times without respecting `frame_count_max` -/// * #SoundIoErrorStreaming -/// * #SoundIoErrorUnderflow - an underflow caused this call to fail. You might -/// also get a SoundIoOutStream::underflow_callback, and you might not get -/// this error code when an underflow occurs. Unlike #SoundIoErrorStreaming, -/// the outstream is still in a valid state and streaming can continue. -/// * #SoundIoErrorIncompatibleDevice - in rare cases it might just now -/// be discovered that the device uses non-byte-aligned access, in which -/// case this error code is returned. -SOUNDIO_EXPORT int soundio_outstream_begin_write(struct SoundIoOutStream *outstream, - struct SoundIoChannelArea **areas, int *frame_count); - -/// Commits the write that you began with ::soundio_outstream_begin_write. -/// You must call this function only from the SoundIoOutStream::write_callback thread context. -/// -/// Possible errors: -/// * #SoundIoErrorStreaming -/// * #SoundIoErrorUnderflow - an underflow caused this call to fail. You might -/// also get a SoundIoOutStream::underflow_callback, and you might not get -/// this error code when an underflow occurs. Unlike #SoundIoErrorStreaming, -/// the outstream is still in a valid state and streaming can continue. -SOUNDIO_EXPORT int soundio_outstream_end_write(struct SoundIoOutStream *outstream); - -/// Clears the output stream buffer. -/// This function can be called from any thread. -/// This function can be called regardless of whether the outstream is paused -/// or not. -/// Some backends do not support clearing the buffer. On these backends this -/// function will return SoundIoErrorIncompatibleBackend. -/// Some devices do not support clearing the buffer. On these devices this -/// function might return SoundIoErrorIncompatibleDevice. -/// Possible errors: -/// -/// * #SoundIoErrorStreaming -/// * #SoundIoErrorIncompatibleBackend -/// * #SoundIoErrorIncompatibleDevice -SOUNDIO_EXPORT int soundio_outstream_clear_buffer(struct SoundIoOutStream *outstream); - -/// If the underlying backend and device support pausing, this pauses the -/// stream. SoundIoOutStream::write_callback may be called a few more times if -/// the buffer is not full. -/// Pausing might put the hardware into a low power state which is ideal if your -/// software is silent for some time. -/// This function may be called from any thread context, including -/// SoundIoOutStream::write_callback. -/// Pausing when already paused or unpausing when already unpaused has no -/// effect and returns #SoundIoErrorNone. -/// -/// Possible errors: -/// * #SoundIoErrorBackendDisconnected -/// * #SoundIoErrorStreaming -/// * #SoundIoErrorIncompatibleDevice - device does not support -/// pausing/unpausing. This error code might not be returned even if the -/// device does not support pausing/unpausing. -/// * #SoundIoErrorIncompatibleBackend - backend does not support -/// pausing/unpausing. -/// * #SoundIoErrorInvalid - outstream not opened and started -SOUNDIO_EXPORT int soundio_outstream_pause(struct SoundIoOutStream *outstream, bool pause); - -/// Obtain the total number of seconds that the next frame written after the -/// last frame written with ::soundio_outstream_end_write will take to become -/// audible. This includes both software and hardware latency. In other words, -/// if you call this function directly after calling ::soundio_outstream_end_write, -/// this gives you the number of seconds that the next frame written will take -/// to become audible. -/// -/// This function must be called only from within SoundIoOutStream::write_callback. -/// -/// Possible errors: -/// * #SoundIoErrorStreaming -SOUNDIO_EXPORT int soundio_outstream_get_latency(struct SoundIoOutStream *outstream, - double *out_latency); - -SOUNDIO_EXPORT int soundio_outstream_set_volume(struct SoundIoOutStream *outstream, - double volume); - - - -// Input Streams -/// Allocates memory and sets defaults. Next you should fill out the struct fields -/// and then call ::soundio_instream_open. Sets all fields to defaults. -/// Returns `NULL` if and only if memory could not be allocated. -/// See also ::soundio_instream_destroy -SOUNDIO_EXPORT struct SoundIoInStream *soundio_instream_create(struct SoundIoDevice *device); -/// You may not call this function from SoundIoInStream::read_callback. -SOUNDIO_EXPORT void soundio_instream_destroy(struct SoundIoInStream *instream); - -/// After you call this function, SoundIoInStream::software_latency is set to the correct -/// value. -/// The next thing to do is call ::soundio_instream_start. -/// If this function returns an error, the instream is in an invalid state and -/// you must call ::soundio_instream_destroy on it. -/// -/// Possible errors: -/// * #SoundIoErrorInvalid -/// * device aim is not #SoundIoDeviceAimInput -/// * format is not valid -/// * requested layout channel count > #SOUNDIO_MAX_CHANNELS -/// * #SoundIoErrorOpeningDevice -/// * #SoundIoErrorNoMem -/// * #SoundIoErrorBackendDisconnected -/// * #SoundIoErrorSystemResources -/// * #SoundIoErrorNoSuchClient -/// * #SoundIoErrorIncompatibleBackend -/// * #SoundIoErrorIncompatibleDevice -SOUNDIO_EXPORT int soundio_instream_open(struct SoundIoInStream *instream); - -/// After you call this function, SoundIoInStream::read_callback will be called. -/// -/// Possible errors: -/// * #SoundIoErrorBackendDisconnected -/// * #SoundIoErrorStreaming -/// * #SoundIoErrorOpeningDevice -/// * #SoundIoErrorSystemResources -SOUNDIO_EXPORT int soundio_instream_start(struct SoundIoInStream *instream); - -/// Call this function when you are ready to begin reading from the device -/// buffer. -/// * `instream` - (in) The input stream you want to read from. -/// * `areas` - (out) The memory addresses you can read data from. It is OK -/// to modify the pointers if that helps you iterate. There might be a "hole" -/// in the buffer. To indicate this, `areas` will be `NULL` and `frame_count` -/// tells how big the hole is in frames. -/// * `frame_count` - (in/out) - Provide the number of frames you want to read; -/// returns the number of frames you can actually read. The returned value -/// will always be less than or equal to the provided value. If the provided -/// value is less than `frame_count_min` from SoundIoInStream::read_callback this function -/// returns with #SoundIoErrorInvalid. -/// It is your responsibility to call this function no more and no fewer than the -/// correct number of times according to the `frame_count_min` and -/// `frame_count_max` criteria from SoundIoInStream::read_callback. -/// You must call this function only from the SoundIoInStream::read_callback thread context. -/// After calling this function, read data from `areas` and then use -/// ::soundio_instream_end_read` to actually remove the data from the buffer -/// and move the read index forward. ::soundio_instream_end_read should not be -/// called if the buffer is empty (`frame_count` == 0), but it should be called -/// if there is a hole. -/// -/// Possible errors: -/// * #SoundIoErrorInvalid -/// * `*frame_count` < `frame_count_min` or `*frame_count` > `frame_count_max` -/// * #SoundIoErrorStreaming -/// * #SoundIoErrorIncompatibleDevice - in rare cases it might just now -/// be discovered that the device uses non-byte-aligned access, in which -/// case this error code is returned. -SOUNDIO_EXPORT int soundio_instream_begin_read(struct SoundIoInStream *instream, - struct SoundIoChannelArea **areas, int *frame_count); -/// This will drop all of the frames from when you called -/// ::soundio_instream_begin_read. -/// You must call this function only from the SoundIoInStream::read_callback thread context. -/// You must call this function only after a successful call to -/// ::soundio_instream_begin_read. -/// -/// Possible errors: -/// * #SoundIoErrorStreaming -SOUNDIO_EXPORT int soundio_instream_end_read(struct SoundIoInStream *instream); - -/// If the underyling device supports pausing, this pauses the stream and -/// prevents SoundIoInStream::read_callback from being called. Otherwise this returns -/// #SoundIoErrorIncompatibleDevice. -/// This function may be called from any thread. -/// Pausing when already paused or unpausing when already unpaused has no -/// effect and always returns #SoundIoErrorNone. -/// -/// Possible errors: -/// * #SoundIoErrorBackendDisconnected -/// * #SoundIoErrorStreaming -/// * #SoundIoErrorIncompatibleDevice - device does not support pausing/unpausing -SOUNDIO_EXPORT int soundio_instream_pause(struct SoundIoInStream *instream, bool pause); - -/// Obtain the number of seconds that the next frame of sound being -/// captured will take to arrive in the buffer, plus the amount of time that is -/// represented in the buffer. This includes both software and hardware latency. -/// -/// This function must be called only from within SoundIoInStream::read_callback. -/// -/// Possible errors: -/// * #SoundIoErrorStreaming -SOUNDIO_EXPORT int soundio_instream_get_latency(struct SoundIoInStream *instream, - double *out_latency); - - -struct SoundIoRingBuffer; - -/// A ring buffer is a single-reader single-writer lock-free fixed-size queue. -/// libsoundio ring buffers use memory mapping techniques to enable a -/// contiguous buffer when reading or writing across the boundary of the ring -/// buffer's capacity. -/// `requested_capacity` in bytes. -/// Returns `NULL` if and only if memory could not be allocated. -/// Use ::soundio_ring_buffer_capacity to get the actual capacity, which might -/// be greater for alignment purposes. -/// See also ::soundio_ring_buffer_destroy -SOUNDIO_EXPORT struct SoundIoRingBuffer *soundio_ring_buffer_create(struct SoundIo *soundio, int requested_capacity); -SOUNDIO_EXPORT void soundio_ring_buffer_destroy(struct SoundIoRingBuffer *ring_buffer); - -/// When you create a ring buffer, capacity might be more than the requested -/// capacity for alignment purposes. This function returns the actual capacity. -SOUNDIO_EXPORT int soundio_ring_buffer_capacity(struct SoundIoRingBuffer *ring_buffer); - -/// Do not write more than capacity. -SOUNDIO_EXPORT char *soundio_ring_buffer_write_ptr(struct SoundIoRingBuffer *ring_buffer); -/// `count` in bytes. -SOUNDIO_EXPORT void soundio_ring_buffer_advance_write_ptr(struct SoundIoRingBuffer *ring_buffer, int count); - -/// Do not read more than capacity. -SOUNDIO_EXPORT char *soundio_ring_buffer_read_ptr(struct SoundIoRingBuffer *ring_buffer); -/// `count` in bytes. -SOUNDIO_EXPORT void soundio_ring_buffer_advance_read_ptr(struct SoundIoRingBuffer *ring_buffer, int count); - -/// Returns how many bytes of the buffer is used, ready for reading. -SOUNDIO_EXPORT int soundio_ring_buffer_fill_count(struct SoundIoRingBuffer *ring_buffer); - -/// Returns how many bytes of the buffer is free, ready for writing. -SOUNDIO_EXPORT int soundio_ring_buffer_free_count(struct SoundIoRingBuffer *ring_buffer); - -/// Must be called by the writer. -SOUNDIO_EXPORT void soundio_ring_buffer_clear(struct SoundIoRingBuffer *ring_buffer); - -#endif diff --git a/hsmodem/soundio_backup.cpp b/hsmodem/soundio_backup.cpp deleted file mode 100755 index bf8822b..0000000 --- a/hsmodem/soundio_backup.cpp +++ /dev/null @@ -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) -{ - -} - diff --git a/hsmodem/udp.cpp b/hsmodem/udp.cpp index 50f1ec5..a3a987b 100755 --- a/hsmodem/udp.cpp +++ b/hsmodem/udp.cpp @@ -74,7 +74,7 @@ void UdpRxInit(int *sock, int port, void (*rxfunc)(uint8_t *, int, struct sockad memset(&sin, 0, sizeof(struct sockaddr_in)); sin.sin_family = AF_INET; 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) { @@ -102,13 +102,11 @@ void UdpRxInit(int *sock, int port, void (*rxfunc)(uint8_t *, int, struct sockad rxcfg_idx++; } - #ifdef _LINUX_ void* threadfunction(void* param) { socklen_t fromlen; pthread_detach(pthread_self()); #endif - #ifdef _WIN32_ void threadfunction(void* param) { int fromlen; @@ -116,18 +114,24 @@ void threadfunction(void* param) { RXCFG rxcfg; memcpy((uint8_t *)(&rxcfg), (uint8_t *)param, sizeof(RXCFG)); int recvlen; - char rxbuf[256]; + const int maxUDPpacketsize = 1024; + char rxbuf[maxUDPpacketsize]; struct sockaddr_in fromSock; fromlen = sizeof(struct sockaddr_in); 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) { // data received, send it to callback function (*rxcfg.rxfunc)((uint8_t *)rxbuf,recvlen, &fromSock); } - + if (recvlen < 0) + { +#ifdef _WIN32_ + printf("recvfrom error: %d", WSAGetLastError()); +#endif + } } #ifdef _LINUX_ pthread_exit(NULL); // self terminate this thread diff --git a/hsmodem/voiceprocessor.cpp b/hsmodem/voiceprocessor.cpp index 30428dd..5f6d844 100755 --- a/hsmodem/voiceprocessor.cpp +++ b/hsmodem/voiceprocessor.cpp @@ -170,7 +170,6 @@ void sendCodecToModulator(uint8_t *pdata, int len) while (keeprunning) { // we have to check if the TX fifo has enough data. In case of an underrun the Q(8A)PSK signal will be distorted - //int us = io_pb_fifo_usedspace(); int us = io_fifo_usedspace(io_pbidx); if (us < 20000) { diff --git a/hsmodemLinux/hsmodem b/hsmodemLinux/hsmodem index 8ef8553..fcf6d6a 100755 Binary files a/hsmodemLinux/hsmodem and b/hsmodemLinux/hsmodem differ diff --git a/hsmodemLinux/oscardata.exe b/hsmodemLinux/oscardata.exe index 817a6a8..ac7c65a 100755 Binary files a/hsmodemLinux/oscardata.exe and b/hsmodemLinux/oscardata.exe differ diff --git a/oscardata/oscardata/Form1.Designer.cs b/oscardata/oscardata/Form1.Designer.cs index fe6340f..0886a46 100755 --- a/oscardata/oscardata/Form1.Designer.cs +++ b/oscardata/oscardata/Form1.Designer.cs @@ -37,6 +37,8 @@ this.toolStripStatusLabel = new System.Windows.Forms.ToolStripStatusLabel(); this.ts_ip = 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.timer_qpsk = new System.Windows.Forms.Timer(this.components); this.panel_txspectrum = new System.Windows.Forms.Panel(); @@ -253,7 +255,9 @@ this.toolStrip_Type, this.toolStripStatusLabel, this.ts_ip, - this.RXstatus}); + this.RXstatus, + this.toolStrip_spacer, + this.ts_userinfo}); this.statusStrip1.Location = new System.Drawing.Point(0, 671); this.statusStrip1.Name = "statusStrip1"; this.statusStrip1.Size = new System.Drawing.Size(1296, 22); @@ -286,6 +290,20 @@ this.RXstatus.Size = new System.Drawing.Size(58, 17); 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 // this.panel_constel.BackColor = System.Drawing.Color.AliceBlue; @@ -2268,7 +2286,7 @@ this.ForeColor = System.Drawing.SystemColors.ControlText; this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); 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.statusStrip1.ResumeLayout(false); this.statusStrip1.PerformLayout(); @@ -2498,6 +2516,8 @@ private System.Windows.Forms.CheckBox cb_rx_autosync; private System.Windows.Forms.TextBox textBox6; private System.Windows.Forms.Button button6; + private System.Windows.Forms.ToolStripStatusLabel toolStrip_spacer; + private System.Windows.Forms.ToolStripStatusLabel ts_userinfo; } } diff --git a/oscardata/oscardata/Form1.cs b/oscardata/oscardata/Form1.cs index 918d53d..83c4bf8 100755 --- a/oscardata/oscardata/Form1.cs +++ b/oscardata/oscardata/Form1.cs @@ -44,8 +44,8 @@ namespace oscardata String old_tsip = ""; bool modemrunning = false; receivefile recfile = new receivefile(); - int last_initAudioStatus; - int last_initVoiceStatus; + int last_initAudioStatus = -1; + int last_initVoiceStatus = -1; int recordStatus = 0; int recPhase = 0; const int Rtty_deftext_anz = 20; @@ -220,8 +220,8 @@ namespace oscardata { if (s.Length > 1) { - cb_audioPB.Items.Add(s.Substring(1)); - cb_loudspeaker.Items.Add(s.Substring(1)); + cb_audioPB.Items.Add(s); + cb_loudspeaker.Items.Add(s); } } cb_loudspeaker.EndUpdate(); @@ -238,8 +238,8 @@ namespace oscardata { if (s.Length > 1) { - cb_audioCAP.Items.Add(s.Substring(1)); - cb_mic.Items.Add(s.Substring(1)); + cb_audioCAP.Items.Add(s); + cb_mic.Items.Add(s); } } 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) { int pos = -1; 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; for (int i = 0; i < anz; i++) { - String name = cb.Items[i].ToString(); - name = name.Substring(3); - if (dn == name) + if (cb.Text == cb.Items[i].ToString()) { pos = i; break; @@ -349,7 +345,6 @@ namespace oscardata cb.Text = cb.Items[pos].ToString(); } - private void Form1_FormClosing(object sender, FormClosingEventArgs e) { save_Setup(); @@ -443,6 +438,14 @@ namespace oscardata //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 ========== // handle file receive if (rxtype == statics.Image) @@ -1181,13 +1184,17 @@ namespace oscardata public String GetMyBroadcastIP() { 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(); - //Console.WriteLine("BClen: " + myips.Length.ToString()); + Console.WriteLine("BClen: " + myips.Length.ToString()); // 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]); - }*/ + } if (myips.Length >= 1) { statics.MyIP = myips[0]; @@ -1199,7 +1206,7 @@ namespace oscardata ip += ".255"; //Console.WriteLine("BCip: " + ip); } - } + }*/ return ip; } @@ -1231,7 +1238,7 @@ namespace oscardata if (cb_safemode.Text.Contains("medium")) safemode = 2; 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[1] = (Byte)tb_PBvol.Value; txb[2] = (Byte)tb_CAPvol.Value; @@ -1248,6 +1255,7 @@ namespace oscardata //Byte[] bpb = statics.StringToByteArray(cb_audioPB.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++) { if (i >= bpb.Length) @@ -1261,6 +1269,28 @@ namespace oscardata 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") { // still searching a modem diff --git a/oscardata/oscardata/Form1.resx b/oscardata/oscardata/Form1.resx index 5bd45d7..2bf716c 100755 --- a/oscardata/oscardata/Form1.resx +++ b/oscardata/oscardata/Form1.resx @@ -137,7 +137,7 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAAA+ - JQAAAk1TRnQBSQFMAgEBFwEAAaABDAGgAQwBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo + JQAAAk1TRnQBSQFMAgEBFwEAAdgBDAHYAQwBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo AwABQAMAAWADAAEBAQABCAYAARgYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5 AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA diff --git a/oscardata/oscardata/KmProgressBar.cs b/oscardata/oscardata/KmProgressBar.cs index 4441b9a..95310a5 100755 --- a/oscardata/oscardata/KmProgressBar.cs +++ b/oscardata/oscardata/KmProgressBar.cs @@ -1,12 +1,7 @@ using System; -using System.Collections.Generic; using System.Drawing; -using System.Drawing.Drawing2D; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows.Forms; - + namespace oscardata { class KmProgressBar : ProgressBar diff --git a/oscardata/oscardata/bin/Release/oscardata.exe b/oscardata/oscardata/bin/Release/oscardata.exe index 817a6a8..ac7c65a 100755 Binary files a/oscardata/oscardata/bin/Release/oscardata.exe and b/oscardata/oscardata/bin/Release/oscardata.exe differ diff --git a/oscardata/oscardata/config.cs b/oscardata/oscardata/config.cs index d353501..614051f 100755 --- a/oscardata/oscardata/config.cs +++ b/oscardata/oscardata/config.cs @@ -23,6 +23,7 @@ namespace oscardata public static Byte HTMLFile = 4; public static Byte BinaryFile = 5; public static Byte Audio = 6; + public static Byte Userinfo = 7; // the upper values are for internal use public static Byte ResamplingRate = 16; @@ -185,6 +186,23 @@ namespace oscardata 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) { Byte[] ba = new byte[arr.Length]; diff --git a/oscardata/oscardata/udp.cs b/oscardata/oscardata/udp.cs index 87ac1db..5e7b90f 100755 --- a/oscardata/oscardata/udp.cs +++ b/oscardata/oscardata/udp.cs @@ -123,8 +123,13 @@ namespace oscardata searchtimeout = 0; // message b contains audio devices and init status - //String s = statics.ByteArrayToString(b,0); - String s = statics.ByteArrayToStringUtf8(b, 0); + statics.initAudioStatus = (b[0] == '1') ? 2 : 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[] { '^' }); statics.AudioPBdevs = sa1[0].Split(new char[] { '~' }); statics.AudioCAPdevs = sa1[1].Split(new char[] { '~' }); @@ -143,18 +148,19 @@ namespace oscardata // FFT data if (rxtype == statics.udp_fft) { - statics.PBfifousage = b[0]; - statics.CAPfifousage = b[1]; - statics.RXlevelDetected = b[2]; - statics.RXinSync = b[3]; - statics.maxRXlevel = b[4]; - statics.maxTXlevel = b[5]; - statics.tune_frequency = b[6]; + int idx = 0; + statics.PBfifousage = b[idx++]; + statics.CAPfifousage = b[idx++]; + statics.RXlevelDetected = b[idx++]; + statics.RXinSync = b[idx++]; + statics.maxRXlevel = b[idx++]; + statics.maxTXlevel = b[idx++]; + statics.tune_frequency = b[idx++]; statics.tune_frequency <<= 8; - statics.tune_frequency += b[7]; + statics.tune_frequency += b[idx++]; //Console.WriteLine("f:" + statics.tune_frequency); - Byte[] b1 = new byte[b.Length - 6]; - Array.Copy(b, 6, b1, 0, b1.Length); + Byte[] b1 = new byte[b.Length - idx]; + Array.Copy(b, idx, b1, 0, b1.Length); drawFftBitmap(b1); }