diff --git a/WinRelease/hsmodem.exe b/WinRelease/hsmodem.exe new file mode 100755 index 0000000..fc7486b Binary files /dev/null and b/WinRelease/hsmodem.exe differ diff --git a/WinRelease/oscardata.exe b/WinRelease/oscardata.exe deleted file mode 100755 index a1a49d6..0000000 Binary files a/WinRelease/oscardata.exe and /dev/null differ diff --git a/hsmodem/fft.cpp b/hsmodem/fft.cpp index 92040ad..401ac7f 100755 --- a/hsmodem/fft.cpp +++ b/hsmodem/fft.cpp @@ -52,10 +52,11 @@ uint16_t *make_waterfall(float fre, int *retlen) // caprate 48k: downsample by 6 // caprate 44,1k: downsample by 5,5 - if (caprate == 48000) + if (physcaprate == 48000) if (++downsamp < 6) return NULL; - if (caprate == 44100) + // TODO: the following simple resamp results in double peeks in fft + if (physcaprate == 44100) { if (downphase <= 1100) { @@ -95,43 +96,60 @@ uint16_t *make_waterfall(float fre, int *retlen) fftrdy = 1; } - // signal detection - // measure level at band edges - float edgelevel = 0; - for (int e = 0; e < 10; e++) - edgelevel += f_fftout[e]; - for (int e = 390; e < fftcnt; e++) - edgelevel += f_fftout[e]; - edgelevel /= 20; - - // measure level at mid band - float midlevel = 0; - for (int e = 100; e < 300; e++) - midlevel += f_fftout[e]; - midlevel /= 20; - - //calc difference in % - int idiff = (int)((edgelevel * 100) / midlevel); - //printf("diff:%d %% edge:%10.6f midband:%10.6f\n", ldiff, idiff,edgelevel, midlevel); - - // IC9700 ... noise makes about 5% idiff - // Signal makes 0-2%, so we take the decision at 3% - const int maxchecks = 3; - static int rxstat = 0; - if (idiff < 3) + if (rx_in_sync == 0) { - if (rxstat == maxchecks) + // signal detection + // measure level at band edges + float edgelevel = 0; + for (int e = 0; e < 10; e++) + edgelevel += f_fftout[e]; + edgelevel /= 10; + + for (int e = 300; e < 320; e++) + edgelevel += f_fftout[e]; + edgelevel /= 20; + + // measure level at mid band + float midlevel = 0; + for (int e = 100; e < 300; e++) + midlevel += f_fftout[e]; + midlevel /= 200; + + //calc difference in % + int idiff = (int)((edgelevel * 100) / midlevel); + //printf("diff:%d %% edge:%10.6f midband:%10.6f\n", idiff,edgelevel, midlevel); + + // idiff SDR Console: + // no signal ... > 100 + // signal < 20 + static int checks = 0; + static int lastsig = 0; + int sig = 0; + + // check if signal detected or not + if (idiff > 100) sig = 0; + if (idiff < 20) sig = 1; + + rxlevel_deteced = sig; + + // check if changed since last check + if (sig != lastsig) { - printf("===>>> level detected, reset modem\n"); - trigger_resetmodem = 1; - rxlevel_deteced = 1; + lastsig = sig; + checks = 0; + } + else + { + if (checks <= 3) checks++; + if (checks == 3) + { + if (sig == 1) + { + printf("===>>> level detected, reset modem\n"); + trigger_resetmodem = 1; + } + } } - if(rxstat <= maxchecks) rxstat++; - } - else - { - rxstat = 0; - rxlevel_deteced = 0; } } diff --git a/hsmodem/fifo_voice.cpp b/hsmodem/fifo_voice.cpp index 98fd3bf..9e9bb30 100755 --- a/hsmodem/fifo_voice.cpp +++ b/hsmodem/fifo_voice.cpp @@ -53,7 +53,7 @@ void IO_MIC_UNLOCK() { pthread_mutex_unlock(&io_mic_crit_sec); } void IO_LS_UNLOCK() { pthread_mutex_unlock(&io_ls_crit_sec); } #endif -#define VOICE_PLAYBACK_BUFLEN (48000 * 15) // space for 10 seconds of samples +#define VOICE_PLAYBACK_BUFLEN (500000) #define VOICE_CAPTURE_BUFLEN (10000) //48000)// * 10) // space for 10 seconds of samples int io_mic_wridx = 0; diff --git a/hsmodem/hsmodem.cpp b/hsmodem/hsmodem.cpp index 6f0f973..bd82c29 100755 --- a/hsmodem/hsmodem.cpp +++ b/hsmodem/hsmodem.cpp @@ -31,35 +31,6 @@ * !!! compile x86 (32bit) version !!! all supplied dlls are 32 bit !!! * ==================================================================== * -* 3rd party libraries: -* 1) BASS Audio from https://www.un4seen.com/ - copy bass.h and bass.lib into source directory - Windows: copy bass.dll into executable directory - Linux: copy libbass.so into shared-lib folder, usually /usr/local/lib - ! NOTE: for PC-Linux and ARM-Linux you need different libraries ! - -2) liquid-DSP - Linux Install Script: - this installs it from source - - sudo apt install git autoconf libsndfile-dev libasound-dev - git clone git://github.com/jgaeddert/liquid-dsp.git - cd liquid-dsp - ./bootstrap.sh - ./configure - make -j 8 - sudo make install - sudo ldconfig - - a working copy of the source code is in ../3rdParty/liquid-dsp - to use this source simply remove the "git clone" line from above script - it installs libliquid.so into /usr/local/lib (Ubuntu) and - liquid.h into /usr/local/include/liquid/ - - Windows: - ready libraries are in ../3rdParty/liquid-dsp-windows - copy liquid.h and liquid.lib into source directory - copy liquid.dll into executable directory */ @@ -97,6 +68,7 @@ int restart_modems = 0; int trigger_resetmodem = 0; int caprate = 44100; +int physcaprate = 44100; int txinterpolfactor = 20; int rxPreInterpolfactor = 5; int linespeed = 4410; @@ -203,7 +175,7 @@ int main(int argc, char* argv[]) { // loop voice mic to LS float f; - if (io_mic_read_fifo(&f)) + while (io_mic_read_fifo(&f)) { io_ls_write_fifo(f); } @@ -213,7 +185,7 @@ int main(int argc, char* argv[]) { // send mic to codec float f; - if (io_mic_read_fifo(&f)) + while (io_mic_read_fifo(&f)) { encode(f); } @@ -590,7 +562,7 @@ void GRdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock) fnd = 0; trigger_resetmodem = 0; rx_in_sync = 0; - printf("no signal detected %d, reset RX modem\n", wt); + //printf("no signal detected %d, reset RX modem\n", wt); resetModem(); } else if (fnd >= wt) diff --git a/hsmodem/hsmodem.h b/hsmodem/hsmodem.h index 161d8cf..705696e 100755 --- a/hsmodem/hsmodem.h +++ b/hsmodem/hsmodem.h @@ -76,6 +76,7 @@ // voice audio sampling rate #define VOICE_SAMPRATE 48000 // do NOT change, OPUS works with 48k only +#define AUDIO_SAMPRATE 48000 // do NOT change, WASAPI VACs work with 48k only enum _VOICEMODES_ { VOICEMODE_OFF, @@ -180,6 +181,8 @@ void io_close_voice(); int io_ls_read_fifo_num(float* data, int num); void io_mic_write_fifo(float sample); void write_sample_s16ne(char* ptr, double sample); +int io_ls_fifo_usedspace(); +void write_sample_float32ne(char* ptr, double sample); void km_symtrack_cccf_create(int _ftype, unsigned int _k, @@ -222,6 +225,8 @@ extern int rxlevel_deteced; extern int rx_in_sync; extern float softwareMICvolume; extern float softwareLSvolume; +extern int physcaprate; +extern int restart_modems; #ifdef _LINUX_ int isRunning(char* prgname); diff --git a/hsmodem/hsmodem.vcxproj b/hsmodem/hsmodem.vcxproj index 9a005a5..f7b0623 100755 --- a/hsmodem/hsmodem.vcxproj +++ b/hsmodem/hsmodem.vcxproj @@ -223,10 +223,6 @@ - - - - diff --git a/hsmodem/hsmodem.vcxproj.filters b/hsmodem/hsmodem.vcxproj.filters index 72c6c2c..66ee4f2 100755 --- a/hsmodem/hsmodem.vcxproj.filters +++ b/hsmodem/hsmodem.vcxproj.filters @@ -83,9 +83,6 @@ Header Files - - Header Files - Header Files @@ -95,21 +92,12 @@ Header Files - - Header Files - Header Files Header Files - - Header Files - - - Header Files - Header Files diff --git a/hsmodem/liquid_if.cpp b/hsmodem/liquid_if.cpp index 6550e48..4f872ee 100755 --- a/hsmodem/liquid_if.cpp +++ b/hsmodem/liquid_if.cpp @@ -212,11 +212,16 @@ void modulator(uint8_t sym_in) nco_crcf dnnco = NULL; symtrack_cccf symtrack = NULL; firdecim_crcf decim = NULL; +msresamp_crcf adecim = NULL; // decimator parameters unsigned int m_predec = 8; // filter delay float As_predec = 40.0f; // stop-band att +// adecimator parameters +float r_OutDivInRatio; // resampling rate (output/input) +float As_adecim = 60.0f; // resampling filter stop-band attenuation [dB] + // symtrack parameters int ftype_st = LIQUID_FIRFILT_RRC; unsigned int k_st = 4; // samples per symbol @@ -240,20 +245,24 @@ void init_demodulator() // create pre-decimator decim = firdecim_crcf_create_kaiser(rxPreInterpolfactor, m_predec, As_predec); firdecim_crcf_set_scale(decim, 1.0f/(float)rxPreInterpolfactor); + + // create arbitrary pre decimator + // if Audio SR is 48000 but caprate is 44100 + r_OutDivInRatio = (float)((float)caprate / 48000.0); + adecim = msresamp_crcf_create(r_OutDivInRatio, As_adecim); // create symbol tracking synchronizer - //k_st = txinterpolfactor; - //symtrack = km_symtrack_cccf_create(ftype_st,k_st,m_st,beta_st,getMod()); km_symtrack_cccf_create(ftype_st, k_st, m_st, beta_st, getMod()); - //symtrack_cccf_set_bandwidth(symtrack,bandwidth_st); km_symtrack_cccf_set_bandwidth(bandwidth_st); } void close_demodulator() { if(decim != NULL) firdecim_crcf_destroy(decim); + if(adecim) msresamp_crcf_destroy(adecim); symtrack = NULL; decim = NULL; + adecim = NULL; } void resetModem() @@ -340,7 +349,7 @@ static int ccol_idx = 0; float f; int ret = io_cap_read_fifo(&f); if(ret == 0) return 0; - + if (VoiceAudioMode == VOICEMODE_LISTENAUDIOIN) { io_ls_write_fifo(f); @@ -351,7 +360,21 @@ static int ccol_idx = 0; getMax(f); - make_FFTdata(f*60); + make_FFTdata(f * 60); + + if (caprate == 44100 && physcaprate == 48000) + { + // the sound card capture for a VAC always works with 48000 because + // a VAC cannot be set to a specific cap rate in shared mode + unsigned int num_written = 0; + liquid_float_complex in; + liquid_float_complex out; + in.real = f; + in.imag = 0; + msresamp_crcf_execute(adecim, &in, 1, &out, &num_written); + if (num_written == 0) return 1; + f = out.real; + } // downconvert into baseband // still at soundcard sample rate @@ -376,51 +399,55 @@ static int ccol_idx = 0; liquid_float_complex y; firdecim_crcf_execute(decim, ccol, &y); - unsigned int num_symbols_sync; - liquid_float_complex syms; - //symtrack_cccf_execute(symtrack, y, &syms, &num_symbols_sync); - unsigned int nsym_out; // output symbol - km_symtrack_execute(y, &syms, &num_symbols_sync,&nsym_out); - - if(num_symbols_sync > 1) printf("symtrack_cccf_execute %d output symbols ???\n",num_symbols_sync); - if(num_symbols_sync != 0) - { - unsigned int sym_out; // output symbol - sym_out = nsym_out; + unsigned int num_written = 1; - measure_speed_syms(1); - - // try to extract a complete frame - uint8_t symb = sym_out; - if(bitsPerSymbol == 2) symb ^= (symb>>1); - GRdata_rxdata(&symb, 1, NULL); - - // send the data "as is" to app for Constellation Diagram - // we have about 2000 S/s, but this many points would make the GUI slow - // so we send only every x - static int ev = 0; - if (++ev >= 10) + for (unsigned int sa = 0; sa < num_written; sa++) + { + unsigned int num_symbols_sync; + liquid_float_complex syms; + unsigned int nsym_out; // output symbol + km_symtrack_execute(y, &syms, &num_symbols_sync, &nsym_out); + + if (num_symbols_sync > 1) printf("symtrack_cccf_execute %d output symbols ???\n", num_symbols_sync); + if (num_symbols_sync != 0) { - ev = 0; - int32_t re = (int32_t)(syms.real * 16777216.0); - int32_t im = (int32_t)(syms.imag * 16777216.0); - uint8_t txpl[13]; - int idx = 0; - txpl[idx++] = 5; // type 5: IQ data follows - uint32_t sy = 0x3e8; - txpl[idx++] = sy >> 24; - txpl[idx++] = sy >> 16; - txpl[idx++] = sy >> 8; - txpl[idx++] = sy; - txpl[idx++] = re >> 24; - txpl[idx++] = re >> 16; - txpl[idx++] = re >> 8; - txpl[idx++] = re; - txpl[idx++] = im >> 24; - txpl[idx++] = im >> 16; - txpl[idx++] = im >> 8; - txpl[idx++] = im; - sendUDP(appIP, UdpDataPort_ModemToApp, txpl, 13); + unsigned int sym_out; // output symbol + sym_out = nsym_out; + + //measure_speed_syms(1); + + // try to extract a complete frame + uint8_t symb = sym_out; + if (bitsPerSymbol == 2) symb ^= (symb >> 1); + GRdata_rxdata(&symb, 1, NULL); + + // send the data "as is" to app for Constellation Diagram + // we have about 2000 S/s, but this many points would make the GUI slow + // so we send only every x + static int ev = 0; + if (++ev >= 10) + { + ev = 0; + int32_t re = (int32_t)(syms.real * 16777216.0); + int32_t im = (int32_t)(syms.imag * 16777216.0); + uint8_t txpl[13]; + int idx = 0; + txpl[idx++] = 5; // type 5: IQ data follows + uint32_t sy = 0x3e8; + txpl[idx++] = sy >> 24; + txpl[idx++] = sy >> 16; + txpl[idx++] = sy >> 8; + txpl[idx++] = sy; + txpl[idx++] = re >> 24; + txpl[idx++] = re >> 16; + txpl[idx++] = re >> 8; + txpl[idx++] = re; + txpl[idx++] = im >> 24; + txpl[idx++] = im >> 16; + txpl[idx++] = im >> 8; + txpl[idx++] = im; + sendUDP(appIP, UdpDataPort_ModemToApp, txpl, 13); + } } } diff --git a/hsmodem/soundio.cpp b/hsmodem/soundio.cpp index bf8822b..746496c 100755 --- a/hsmodem/soundio.cpp +++ b/hsmodem/soundio.cpp @@ -49,6 +49,9 @@ typedef struct _AUDIODEV_ { AUDIODEV audiodev[MAXAUDIODEVICES]; int audiodevidx = 0; +bool pbrawdev = true; +bool caprawdev = true; + void print_devs() { printf("\n ==== AUDIO devices ====\n"); @@ -80,11 +83,6 @@ 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; @@ -223,8 +221,8 @@ int min_int(int a, int b) void read_callback(struct SoundIoInStream* instream, int frame_count_min, int frame_count_max) { - int err; + if (instream == NULL) return; //printf("cap: %d %d\n", frame_count_min, frame_count_max); //int chans = instream->layout.channel_count; @@ -237,7 +235,8 @@ void read_callback(struct SoundIoInStream* instream, int frame_count_min, int fr if ((err = soundio_instream_begin_read(instream, &areas, &frame_count))) { fprintf(stderr, "begin read error: %s", soundio_strerror(err)); - exit(1); + restart_modems = 1; + return; } if (!frame_count) break; @@ -246,25 +245,47 @@ void read_callback(struct SoundIoInStream* instream, int frame_count_min, int fr { 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) + if (caprawdev == false) { - float f = rxdata; - f /= 32768; - f *= softwareCAPvolume; - io_cap_write_fifo(f); + // shared device + float frxdata; + memcpy(&frxdata, areas[ch].ptr, instream->bytes_per_sample); + areas[ch].ptr += areas[ch].step; + if (ch == 0) + { + float f = frxdata; + f *= softwareCAPvolume; + io_cap_write_fifo(f); + } + } + else + { + // raw device + // shared device + 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); + } } } } + //printf("%d into fifo\n", frame_count); + // needs to sleep or it will not work correctly, no idea why + sleep_ms(1); //measure_speed_bps(frame_count); if ((err = soundio_instream_end_read(instream))) { fprintf(stderr, "end read error: %s", soundio_strerror(err)); - exit(1); + restart_modems = 1; + return; } frames_left -= frame_count; @@ -272,106 +293,91 @@ void read_callback(struct SoundIoInStream* instream, int frame_count_min, int fr 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 +// #define SINEWAVETEST + +#ifdef SINEWAVETEST +static const double PI = 3.14159265358979323846264338328; +static double seconds_offset = 0.0; +#endif static void write_callback(struct SoundIoOutStream* outstream, int frame_count_min, int frame_count_max) { + //printf("pb: %d %d\n", frame_count_min, frame_count_max); +#ifdef SINEWAVETEST + double float_sample_rate = outstream->sample_rate; + double seconds_per_frame = 1.0 / float_sample_rate; + double pitch = 440.0; + double radians_per_second = pitch * 2.0 * PI; +#endif struct SoundIoChannelArea* areas; int err; - int frames_left = frame_count_max; - while (1) - { + int frames_left = 4800; + if (frame_count_max < frames_left) + frames_left = frame_count_max; + + for (;;) { 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 ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count))) { + fprintf(stderr, "write_callback unrecoverable soundio_outstream_begin_write error: %s\n", soundio_strerror(err)); + restart_modems = 1; + return; } if (!frame_count) break; - float f[MAXCAPCHUNKLEN]; + //printf("ck: %d\n", frame_count); + + float* f = (float*)malloc(frame_count * sizeof(float)); 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 frame = 0; frame < frame_count; frame += 1) { - for (int channel = 0; channel < layout->channel_count; channel++) +#ifdef SINEWAVETEST + double sample = sin((seconds_offset + frame * seconds_per_frame) * radians_per_second); +#endif + for (int channel = 0; channel < layout->channel_count; channel += 1) { - write_sample_s16ne(areas[channel].ptr, f[frame]); + float ftx = f[frame] * softwarePBvolume; + if (pbrawdev == false) + { +#ifdef SINEWAVETEST + write_sample_float32ne(areas[channel].ptr, sample); // sine wave test tone +#endif + write_sample_float32ne(areas[channel].ptr, ftx); + } + else + write_sample_s16ne(areas[channel].ptr, ftx); areas[channel].ptr += areas[channel].step; } } +#ifdef SINEWAVETEST + seconds_offset = fmod(seconds_offset + seconds_per_frame * frame_count, 1.0); +#endif + + free(f); if ((err = soundio_outstream_end_write(outstream))) { if (err == SoundIoErrorUnderflow) return; fprintf(stderr, "unrecoverable stream error: %s\n", soundio_strerror(err)); - exit(1); + restart_modems = 1; + return; } frames_left -= frame_count; @@ -379,66 +385,7 @@ static void write_callback(struct SoundIoOutStream* outstream, int frame_count_m 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; @@ -452,7 +399,7 @@ int io_init_sound(char *pbname, char *capname) init_audio_result = 0; printf("\n ==== IO INIT AUDIO devices ====\n"); - printf("requested: <%s> <%s>\ncapture rate:%d\n",pbname,capname,caprate); + printf("requested\nTX:<%s>\nRX:<%s>\ncapture rate:%d\n\n",pbname,capname,caprate); io_close_audio(); @@ -491,17 +438,26 @@ int io_init_sound(char *pbname, char *capname) if (capdevid == NULL) return 0; // define the capture device - printf("selected CAP device:\nname:%s\nid :%s\n", capname, capdevid); - soundio_flush_events(soundio); + // under Windows we usually use raw devices. This does not work with + // virtual sound cards due to problems in libsoundio + // for VACs we use shared devices, otherwise raw + pbrawdev = true; + if (strstr(pbname, "irtual") || strstr(pbname, "VAC")) + pbrawdev = false; + + caprawdev = true; + if (strstr(capname, "irtual") || strstr(capname, "VAC")) + caprawdev = false; + 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 + && device->is_raw == caprawdev #endif ) { @@ -529,15 +485,27 @@ int io_init_sound(char *pbname, char *capname) return 0; } - instream->format = SoundIoFormatS16NE; - instream->sample_rate = caprate; + // raw devices: 16bit LE, shared devices float + if (caprawdev) + { + instream->format = SoundIoFormatS16NE; + instream->sample_rate = caprate; + physcaprate = caprate; + } + else + { + // a VAC needs these settings or it will not work with 44100 + instream->format = SoundIoFormatFloat32NE; + instream->sample_rate = AUDIO_SAMPRATE; + physcaprate = AUDIO_SAMPRATE; + } 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)); + printf("unable to open input stream: %d: %s", err, soundio_strerror(err)); return 0; } @@ -546,18 +514,21 @@ int io_init_sound(char *pbname, char *capname) return 0; } init_audio_result |= 2; + + printf("selected CAPTURE device:\nname:%s\nid :%s\n", capname, capdevid); + printf("physical capture rate:%d, logical capture rate:%d\n", physcaprate, caprate); + printf("format: %s\n\n", soundio_format_string(instream->format)); + // 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 + && device->is_raw == pbrawdev #endif ) { @@ -585,9 +556,14 @@ int io_init_sound(char *pbname, char *capname) return 0; } - outstream->format = SoundIoFormatS16NE; + // raw devices: 16bit LE, shared devices float + if (pbrawdev) + outstream->format = SoundIoFormatS16NE; + else + outstream->format = SoundIoFormatFloat32NE; + outstream->sample_rate = caprate; - outstream->software_latency = 0.0; + outstream->software_latency = 1.0; outstream->write_callback = write_callback; outstream->underflow_callback = underflow_callback; outstream->userdata = NULL; @@ -603,7 +579,9 @@ int io_init_sound(char *pbname, char *capname) } init_audio_result |= 1; - printf("==== Audio init finished: %d ====\n", init_audio_result); + printf("selected PLAYBACK device:\nname:%s\nid :%s\n", pbname, pbdevid); + printf("physical capture rate:%d, logical capture rate:%d\n", caprate, caprate); + printf("format: %s\n\n", soundio_format_string(outstream->format)); return init_audio_result; } diff --git a/hsmodem/soundio_backup.cpp b/hsmodem/soundio_backup.cpp new file mode 100755 index 0000000..bf8822b --- /dev/null +++ b/hsmodem/soundio_backup.cpp @@ -0,0 +1,665 @@ +/* +* 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/speed.cpp b/hsmodem/speed.cpp index 21cda62..188f30f 100755 --- a/hsmodem/speed.cpp +++ b/hsmodem/speed.cpp @@ -26,7 +26,7 @@ int speed = 0; -#define MAXSPDARR 50 +#define MAXSPDARR 10 int spdarr[MAXSPDARR]; int spdarrbps[MAXSPDARR]; @@ -132,7 +132,7 @@ void measure_speed_syms(int len) speed = meanval((int)dspd) * bitsPerSymbol; // here we have number of elements after 1s - //printf("%d sym/s\n",speed); + printf("%d sym/s\n",speed); elems=0; lasttim = tim; diff --git a/hsmodem/voiceio.cpp b/hsmodem/voiceio.cpp index 6247062..2c239c5 100755 --- a/hsmodem/voiceio.cpp +++ b/hsmodem/voiceio.cpp @@ -32,9 +32,11 @@ struct SoundIoDevice* io_mic_device = NULL; struct SoundIoOutStream* outlsstream = NULL; struct SoundIoInStream* inmicstream = NULL; +bool lsrawdev = true; +bool micrawdev = true; + void read_voicecallback(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; @@ -57,19 +59,37 @@ void read_voicecallback(struct SoundIoInStream* instream, int frame_count_min, i { 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) + if (micrawdev == false) { - float f = rxdata; - f /= 32768; - f *= softwareCAPvolume; - io_mic_write_fifo(f); + float frxdata; + memcpy(&frxdata, areas[ch].ptr, instream->bytes_per_sample); + areas[ch].ptr += areas[ch].step; + if (ch == 0) + { + float f = frxdata; + f *= softwareMICvolume; + io_mic_write_fifo(f); + } + } + else + { + 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 *= softwareMICvolume; + io_mic_write_fifo(f); + } } } } + // needs to sleep or it will not work correctly, no idea why + sleep_ms(1); + //measure_speed_bps(frame_count); if ((err = soundio_instream_end_read(instream))) @@ -97,52 +117,79 @@ void write_sample_s16ne(char* ptr, double sample) { *buf = (int16_t)val; } +void write_sample_float32ne(char* ptr, double sample) { + float* buf = (float*)ptr; + *buf = (float)sample; +} + #define MAXCAPCHUNKLEN 50000 +//static const double PI = 3.14159265358979323846264338328; +//static double seconds_offset = 0.0; static void write_voicecallback(struct SoundIoOutStream* outstream, int frame_count_min, int frame_count_max) { + //printf("pb: %d %d\n", frame_count_min, frame_count_max); + + //double float_sample_rate = outstream->sample_rate; + //double seconds_per_frame = 1.0 / float_sample_rate; struct SoundIoChannelArea* areas; int err; - int frames_left = frame_count_max; - while(1) - { + int frames_left = 4800; + if (frame_count_max < frames_left) + frames_left = frame_count_max; + + for (;;) { int frame_count = frames_left; - if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count))) - { + if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count))) { fprintf(stderr, "unrecoverable stream error: %s\n", soundio_strerror(err)); - exit(1); + return; } if (!frame_count) break; - // soundio_outstream_begin_write requested to write frame_count elements - float f[MAXCAPCHUNKLEN]; + //printf("ck: %d\n", frame_count); + + //float f[MAXCAPCHUNKLEN]; + float *f = (float *)malloc(frame_count * sizeof(float)); int fiforet = io_ls_read_fifo_num(f, frame_count); if (fiforet == 0) { // elements not available, fill with zeroes - //printf("not enough data, send zeroes\n"); + //printf("only %d not enough data, send zeroes\n", io_ls_fifo_usedspace()); memset(f, 0, sizeof(float) * frame_count); } const struct SoundIoChannelLayout* layout = &outstream->layout; - for (int frame = 0; frame < frame_count; frame++) + //double pitch = 440.0; + //double radians_per_second = pitch * 2.0 * PI; + for (int frame = 0; frame < frame_count; frame += 1) { - for (int channel = 0; channel < layout->channel_count; channel++) + //double sample = sin((seconds_offset + frame * seconds_per_frame) * radians_per_second); + for (int channel = 0; channel < layout->channel_count; channel += 1) { - write_sample_s16ne(areas[channel].ptr, f[frame]); + float ftx = f[frame] * softwareLSvolume; + //write_sample_float32ne(areas[channel].ptr, sample); + if(lsrawdev == false) + write_sample_float32ne(areas[channel].ptr, ftx); + else + write_sample_s16ne(areas[channel].ptr, ftx); areas[channel].ptr += areas[channel].step; } } + //seconds_offset = fmod(seconds_offset + seconds_per_frame * frame_count, 1.0); + + free(f); + + //measure_speed_bps(frame_count); if ((err = soundio_outstream_end_write(outstream))) { if (err == SoundIoErrorUnderflow) return; fprintf(stderr, "unrecoverable stream error: %s\n", soundio_strerror(err)); - exit(1); + return; } frames_left -= frame_count; @@ -151,82 +198,6 @@ static void write_voicecallback(struct SoundIoOutStream* outstream, int frame_co } } - -/* -static void write_voicecallback(struct SoundIoOutStream* outstream, int frame_count_min, int frame_count_max) -{ - float f[MAXCAPCHUNKLEN]; - - int chans = outstream->layout.channel_count; - struct SoundIoChannelArea* areas; - int err; - int frames_left = min_int(frame_count_max, MAXCAPCHUNKLEN);// frame_count_max; - printf("min: %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 - - 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); - } - printf("chunk: %d\n", frame_count); - if (!frame_count) break; // will normally never happen - - // soundio_outstream_begin_write requested to write frame_count elements - int fiforet = io_ls_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; - } - } - - for (int frame = 0; frame < frame_count; frame += 1) - { - for (int channel = 0; channel < chans; channel += 1) - { - write_sample_float32ne(areas[channel].ptr, f[frame]); - areas[channel].ptr += areas[channel].step; - } - } - - // and finalize this chunk - if ((err = soundio_outstream_end_write(outstream))) { - if (err == SoundIoErrorUnderflow) - { - printf("underflow x\n"); - 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_voicecallback(struct SoundIoOutStream* outstream) { static int count = 0; @@ -240,7 +211,7 @@ int io_init_voice(char* lsname, char* micname) init_voice_result = 0; printf("\n ==== IO INIT VOICE devices ====\n"); - printf("requested: <%s> <%s>\ncapture rate:%d\n", lsname, micname, VOICE_SAMPRATE); + printf("requested:\nPB :<%s>\nCAP:<%s>\ncapture rate:%d\n", lsname, micname, VOICE_SAMPRATE); io_close_voice(); @@ -276,21 +247,28 @@ int io_init_voice(char* lsname, char* micname) if (micdevid == NULL) return 0; soundio_flush_events(voice_soundio); + // under Windows we usually use raw devices. This does not work with + // virtual sound cards due to problems in libsoundio + // for VACs we use shared devices, otherwise raw + lsrawdev = true; + if (strstr(lsname, "irtual") || strstr(lsname, "VAC")) + lsrawdev = false; + + micrawdev = true; + if (strstr(micname, "irtual") || strstr(micname, "VAC")) + micrawdev = false; - printf("================ %d\n", VoiceAudioMode); if (VoiceAudioMode != VOICEMODE_LISTENAUDIOIN) { // define the capture device - printf("selected CAP device:\nname:%s\nid :%s\n", micname, micdevid); - for (int i = 0; i < soundio_input_device_count(voice_soundio); i++) { io_mic_device = NULL; struct SoundIoDevice* device = soundio_get_input_device(voice_soundio, i); if (strcmp(device->id, micdevid) == 0 #ifdef _WIN32_ - && device->is_raw == true + && device->is_raw == micrawdev #endif ) { @@ -314,8 +292,6 @@ int io_init_voice(char* lsname, char* micname) return 0; } - printf("cap raw: %s\n", io_mic_device->is_raw ? "raw" : "---"); - // create capture callback inmicstream = soundio_instream_create(io_mic_device); if (!inmicstream) { @@ -323,7 +299,10 @@ int io_init_voice(char* lsname, char* micname) return 0; } - inmicstream->format = SoundIoFormatS16NE; + if (micrawdev) + inmicstream->format = SoundIoFormatS16NE; + else + inmicstream->format = SoundIoFormatFloat32NE; inmicstream->sample_rate = VOICE_SAMPRATE; inmicstream->software_latency = 0.0; inmicstream->read_callback = read_voicecallback; @@ -340,18 +319,21 @@ int io_init_voice(char* lsname, char* micname) return 0; } init_voice_result |= 2; + + printf("selected MICROPHONE device:\nname:%s\nid :%s\n", micname, micdevid); + printf("physical capture rate:%d\n", VOICE_SAMPRATE); + printf("format: %s\n\n", soundio_format_string(inmicstream->format)); // the CAP callback is running now } // define the playback device - printf("selected PB device:\nname:%s\nid :%s\n", lsname, lsdevid); for (int i = 0; i < soundio_output_device_count(voice_soundio); i++) { io_ls_device = NULL; struct SoundIoDevice* device = soundio_get_output_device(voice_soundio, i); if (strcmp(device->id, lsdevid) == 0 #ifdef _WIN32_ - && device->is_raw == true + && device->is_raw == lsrawdev #endif ) { @@ -381,7 +363,11 @@ int io_init_voice(char* lsname, char* micname) return 0; } - outlsstream->format = SoundIoFormatS16NE; + + if (lsrawdev) + outlsstream->format = SoundIoFormatS16NE; + else + outlsstream->format = SoundIoFormatFloat32NE; outlsstream->sample_rate = VOICE_SAMPRATE; outlsstream->software_latency = 0.0; outlsstream->write_callback = write_voicecallback; @@ -399,7 +385,9 @@ int io_init_voice(char* lsname, char* micname) } init_voice_result |= 1; - printf("==== Voice init finished: %d ====\n", init_audio_result); + printf("selected LOUDSPEAKER device:\nname:%s\nid :%s\n", lsname, lsdevid); + printf("physical capture rate:%d\n", VOICE_SAMPRATE); + printf("format: %s\n\n", soundio_format_string(outlsstream->format)); return init_voice_result; } diff --git a/oscardata/oscardata/Form1.Designer.cs b/oscardata/oscardata/Form1.Designer.cs index 213599f..a5b9fdb 100755 --- a/oscardata/oscardata/Form1.Designer.cs +++ b/oscardata/oscardata/Form1.Designer.cs @@ -1428,7 +1428,7 @@ this.ForeColor = System.Drawing.SystemColors.ControlText; this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Name = "Form1"; - this.Text = "QO-100 NB Transponder HS Multimedia Modem AMSAT-DL V0.44 by DJ0ABR"; + this.Text = "QO-100 NB Transponder HS Multimedia Modem AMSAT-DL V0.45 by DJ0ABR"; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing); this.statusStrip1.ResumeLayout(false); this.statusStrip1.PerformLayout(); diff --git a/oscardata/oscardata/Form1.resx b/oscardata/oscardata/Form1.resx index 4b604ce..c1275b2 100755 --- a/oscardata/oscardata/Form1.resx +++ b/oscardata/oscardata/Form1.resx @@ -137,7 +137,7 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAABw - FwAAAk1TRnQBSQFMAgEBDQEAATgBAQE4AQEBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo + FwAAAk1TRnQBSQFMAgEBDQEAAUABAQFAAQEBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo AwABQAMAAUADAAEBAQABCAYAARAYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5 AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA diff --git a/oscardata/oscardata/bin/Release/oscardata.exe b/oscardata/oscardata/bin/Release/oscardata.exe index 69e11f1..a716c2c 100755 Binary files a/oscardata/oscardata/bin/Release/oscardata.exe and b/oscardata/oscardata/bin/Release/oscardata.exe differ