1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
|
#include "krunch.h"
#include "thrower.h"
#include <valarray>
#include <iostream>
#include <iomanip>
#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<datum> 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<int16_t> 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;
}
|