#include "krunch.h" #include "thrower.h" #include #include #include #include "refout.h" /* for class ref_to_krunch */ #include "gui.h" //??? #include "iir_bp.h" #include "bad_thing.h" //?? #include "biquad.h" // forward reference: void dumpit_cout(const double rp, const double ip); // typical usage: //?? biquad butterworth({1., 2., 1.}, //?? {1., -1.99911142347079540116, 0.99911181807963833634}); ////////////////////////////////////////////////////////////////////// // main entry point for krunch job // void* krunch(void* _arg){ using namespace std; krunch_arg* arg((krunch_arg*) _arg); int num_indic = arg->topwin->ndc8r.size(); thrower* toss = new thrower[num_indic]; thrower* flushy = new thrower; for (int ii = 0; ii < num_indic; ii++) { QMetaObject::Connection rslt = QObject::connect( &toss[ii], SIGNAL(_newReading(double,double, double,double)), arg->topwin->ndc8r[ii], SLOT(setIndication(double,double, double,double))); if (!rslt) { cout << "Krunch: Failed to connect, column " << ii << " rslt: " << rslt << endl; exit(3); } } { QMetaObject::Connection rslt = QObject::connect( flushy, SIGNAL(_flush()), arg->topwin, SLOT(flush()) ); if (!rslt) { cout << "Krunch: Failed to connect flush: " << rslt << endl; exit(3); } } alsa_pcm* pcm = arg->pcm; int nframe = arg->setup->nframe; int left_idx = 0; int right_idx = 1; /// Kludge test: left_idx = 10; right_idx = 11; // Be careful to index outside array boundaries: int topchan = pcm->nchan - 1; if (left_idx > topchan) left_idx = topchan; if (right_idx > topchan) right_idx = topchan; #if 0 if (snd_pcm_state(arg->otherpcm->phndl) != SND_PCM_STATE_RUNNING) { cout << "Krunch waits: " << arg->otherpcm->alsa_state_name() << endl; } #endif while (snd_pcm_state(arg->otherpcm->phndl) != SND_PCM_STATE_RUNNING) { usleep(1000); } double left_real(0); double left_ineg(0); // the *negative* of the imaginary part double right_real(0); double right_ineg(0); // the *negative* of the imaginary part //#define PHASE_CHANGE #ifdef PHASE_CHANGE double old_phase(0); #endif double old_cpkp(0), old_fpkp(0); double theta(0); double dtheta(0); int kperno(0); ////////////////// // main krunch loop // // this is a SINGLE loop over TWO variables; // the cap_buffer is filled in units of nframe // and emptied (processed) in units of fpkp. // Parallel code appears in refout.cxx int cap_end(nframe * pcm->nchan); valarray cap_buffer(cap_end); int cap_idx(cap_end); int ref_end(123456); int ref_idx(ref_end); double decalage(0); for (;;) { // loop over all samples // loop control for capture (read) process: if (cap_idx >= nframe) { int didread = arg->setup->read_stuff(pcm, &cap_buffer[0]); if (didread != nframe) { fprintf(stderr, "Krunch: ignoring peculiar buffer size %d not %d\n", didread, nframe); continue; } cap_idx = 0; } // Loop control for reference process. // Note that frequency and phase-offset only changes at // the boundary between krunch periods. if (ref_idx == ref_end) { if (kperno == 0) { decalage = arg->otherpcm->ss_time() - pcm->ss_time(); } #if 0 cout << "declalage: " << decalage << " samples: " << decalage * pcm->rate << " skipme: " << skipme << endl; #endif ref_to_krunch kper; int didread = read(arg->pipefd, &kper, sizeof(kper)); if (didread != sizeof(kper)) { fprintf(stderr, "Krunch: could not read from pipe: "); perror(0); exeunt(1); } if (kperno != kper.period) { fprintf(stderr, "Krunch: phase error: expecting %d got %d\n", kperno, kper.period); exeunt(1); } double freq = double(pcm->rate) * double(kper.cpkp) / double(kper.fpkp); // the phase offset: theta = (decalage + timeShift) * freq * 2. * M_PI; theta += phaseShift * M_PI / 180.; #ifdef PHASE_CHANGE if (theta != old_phase){ cout << "Krunch: new phase: " << theta << " decalage: " << decalage << " timeShift: " << timeShift << " phaseShift: " << timeShift << " freq: " << freq << endl; old_phase = theta; } #endif left_real = left_ineg = 0.; right_real = right_ineg = 0.; ref_idx = 0; ref_end = kper.fpkp; dtheta = 2. * M_PI * kper.cpkp / kper.fpkp; // cpkp == cycles per krunch period // fpkp == frames per krunch period if (old_cpkp != kper.cpkp || old_fpkp != kper.fpkp){ #if 0 cout.precision(6); cout << fixed; cout << "Krunch switching to: " << freq << setprecision(1) << " cpkp: " << kper.cpkp << " fpkp: " << kper.fpkp << " dtheta: " << setprecision(20) << dtheta << endl; #endif old_cpkp = kper.cpkp; old_fpkp = kper.fpkp; } kperno++; } double left = cap_buffer[cap_idx * pcm->nchan + left_idx ]; double right = cap_buffer[cap_idx * pcm->nchan + right_idx]; left_real += left * cos(theta); left_ineg += left * sin(theta); right_real += right * cos(theta); right_ineg += right * sin(theta); theta += dtheta; cap_idx++; ref_idx++; // Do a little post-processing. // This does not replace or even affect the // loop-control above. if (ref_idx >= ref_end) { // put in the minus sign here, to convert ineg to the // actual imaginary part with the proper sign. do { double unit = pow(10., ViCal_dB / 20.); unit /= full_swing; // account for the number of points of points in this krunch period: unit /= double(ref_end); // account for the fact that integral(sin^2) is only 0.5, not 1: unit *= 2.; if (num_indic <= 0) break; toss[0].newReading(0,0, left_real * unit, -left_ineg * unit); if (num_indic <= 1) break; toss[1].newReading(0,0, right_real * unit, -right_ineg * unit); if (num_indic <= 2) break; toss[2].newReading(right_real * unit, -right_ineg * unit, left_real * unit, -left_ineg * unit); } while (0); flushy->flush(); } } // should never reach here return 0; } //////////////////// void dumpit_cout(const double rp, const double ip){ using namespace std; cout.precision(4); int wid(12); double mag(sqrt(rp*rp + ip*ip)); double phase(0); if (mag) phase = atan2(ip, rp); cout << " : " << fixed << setw(wid) << rp << " " << fixed << setw(wid) << ip << " " << fixed << setw(wid) << mag << " " << fixed << setw(wid) << phase * 180 / M_PI << endl; } // Just like snd_pcm_readi(), // except the buff is always of the type of datum (int32_t), // no matter what type the raw hardware uses. // Return value: // nonnegative: # of frames read // negative: error code int readi(const alsa_pcm* pcm, datum* buff, const int nframe){ using namespace std; if (pcm->format == SND_PCM_FORMAT_S32_LE) { return snd_pcm_readi(pcm->phndl, buff, nframe); } else if (pcm->format == SND_PCM_FORMAT_S16_LE) { valarray tmp(nframe * pcm->nchan); int rslt = snd_pcm_readi(pcm->phndl, &tmp[0], nframe); if (rslt <= 0) return rslt; int16_t* from (&tmp[0]); datum* to (buff); int fudge(1<<16); for (unsigned int ii = 0; ii < rslt * pcm->nchan; ii++){ *to++ = *from++ * fudge; } return rslt; } else { cerr << "Don't know how to convert pcm data from format " << snd_pcm_format_name(pcm->format) << endl; exeunt(1); } return 0; // defeat stupid compiler warning } //////////////////////////////////////// // Read some data and maybe write checkfile int snark::read_stuff(alsa_pcm* pcm, datum* cap_buffer){ int sts; int frames_read; for (;;){ // keep trying to read frames_read = readi(pcm, &cap_buffer[0], nframe); if (frames_read == nframe) break; // good! if (frames_read > 0 || frames_read == -EPIPE) { // recover from overruns if (xrun_verbosity) fprintf(stderr, "Read_stuff overrun: requested %i got %i: %s" NL, nframe, frames_read, snd_strerror(frames_read)); snd_pcm_prepare(pcm->phndl); } else { // unrecoverable error fprintf(stderr, "Read_stuff requested %i got %i: %s" NL, nframe, frames_read, snd_strerror(frames_read)); exeunt(1); } } int gotbytes = snd_pcm_frames_to_bytes(pcm->phndl, frames_read); if (wcheck_fd >= 0) { sts = write(wcheck_fd, &cap_buffer[0], gotbytes); if (sts != gotbytes) { fprintf(stderr, "Write error on raw output file (%i): %m" NL, wcheck_fd); exeunt(1); } close(wcheck_fd); wcheck_fd = -1; } return frames_read; }