1 /*
2 * audio-af.cc --
3 *
4 * FIXME: This file needs a description here.
5 *
6 * Copyright (c) 1993-2002 The Regents of the University of California.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions are met:
11 *
12 * A. Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 * B. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 * C. Neither the names of the copyright holders nor the names of its
18 * contributors may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
22 * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
23 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 static const char rcsid[] =
35 "@(#) $Header: /usr/mash/src/repository/mash/mash-1/audio/audio-af.cc,v 1.7 2002/02/03 03:10:46 lim Exp $";
36
37 #include <sys/file.h>
38
39 #include "audio.h"
40 #include <AF/AFlib.h>
41
42 struct afstate {
43 AC ac;
44 int mingain;
45 int maxgain;
46 int gain;
47 int soft;
48 };
49
50 class AFAudio : public Audio {
51 public:
52 AFAudio();
53 virtual int FrameReady();
54 virtual u_char* Read();
55 virtual void Write(u_char *);
56 virtual void SetRGain(int);
57 virtual void SetPGain(int);
58 virtual void OutputPort(int);
59 virtual void Obtain();
60 virtual void Release();
61 protected:
62 void SendReadRequest();
63 int FindDefaultDevice(AFAudioConn*);
64 void noserver();
65 int slidergain(const afstate& af) const;
66 void setgain(int level, afstate& af);
67 void chksoftgain(afstate&, int softgain, int mask);
68
69 AFAudioConn* raud;
70 AFAudioConn* paud;
71
72 u_char* readptr;
73 u_char* readbufend;
74 u_char* readbuf;
75 u_char* replybuf;
76 u_int afblksize;
77
78 u_char* nextframe;
79 u_char* firstframe;
80 u_char* writebuf;
81 int usingbuf;
82
83 int lastmean_[4];
84
85 u_int aftime; /* time of last frame read */
86 u_int wrttime; /* time of last frame written */
87 u_int minusoff; /* max - diff. between server & writer */
88 u_int plusoff; /* avg + diff. between server & writer */
89 int plusvar; /* avg + variation between server & writer */
90 u_int poff; /* play offset relative to aftime */
91 int pmiss; /* number of consecutive missed frames */
92
93 afstate raf; /* record context */
94 afstate saf; /* speaker context */
95 afstate haf; /* headphone context */
96 afstate *paf; /* play context (points at saf/haf) */
97
98 enum {
99 PHONE_CODEC = 0,
100 LOCAL_CODEC = 1,
101 HIFI_BOTH = 2,
102 HIFI_LEFT = 3,
103 HIFI_RIGHT = 4
104 };
105 };
106
107 static class AFAudioMatcher : public Matcher {
108 public:
109 AFAudioMatcher() : Matcher("audio") {}
110 TclObject* match(const char* id) {
111 if (strcasecmp(id, "af") == 0)
112 return (new AFAudio);
113 return (0);
114 }
115 } afaudio_matcher;
116
117 /*FIXME*/
118 #include "/usr/src/local/AudioFile/AF/lib/AF/Alibint.h"
119 extern "C" void _AFlush(AFAudioConn* aud);
120 extern "C" void _ARead(AFAudioConn* aud, char* data, long size);
121 extern "C" void _AReadPad(AFAudioConn* aud, char* data, long size);
122 extern "C" AStatus _AReply(AFAudioConn* aud, aReply* rep,
123 int extra, ABool discard);
124 extern "C" AStatus _AReplyAsync(AFAudioConn* aud, aReply* rep,
125 int extra, ABool discard);
126
127 #ifdef __osf__
128 extern "C" int flock(int, int);
129 #endif
130
131 void AFAudio::chksoftgain(afstate& af, int softgain, int mask)
132 {
133 if (af.mingain == af.maxgain) {
134 af.mingain = -30;
135 af.maxgain = 30;
136 af.soft = 1;
137 } else {
138 af.soft = 0;
139 if (softgain != 0) {
140 AFSetACAttributes attr;
141 attr.rec_gain = softgain;
142 AFChangeACAttributes(af.ac, mask, &attr);
143 }
144 }
145 }
146
147 AFAudio::AFAudio()
148 {
149 Tcl& tcl = Tcl::instance();
150 int device = atoi(tcl.attr("afDevice"));
151 int blocks = atoi(tcl.attr("afBlocks"));
152 int rgain = atoi(tcl.attr("afSoftInputGain"));
153 int pgain = atoi(tcl.attr("afSoftOuputGain"));
154
155 /*
156 * if the AUDIOFILE environment variable is set, use it as
157 * the server name. Otherwise AFOpenAudioConn will try
158 * to use DISPLAY which is probably wrong so force ":0".
159 */
160 const char* sname = getenv("AUDIOFILE");
161 if (sname == 0)
162 sname = ":0";
163 raud = AFOpenAudioConn((char*)sname);
164 if (raud == 0)
165 noserver();
166 paud = AFOpenAudioConn((char*)sname);
167 if (paud == 0)
168 noserver();
169
170 if (device >= ANumberOfAudioDevices(raud)) {
171 fprintf(stderr, "vat: AF: bad device %d", device);
172 exit(1);
173 }
174 if (device < 0) {
175 device = FindDefaultDevice(raud);
176 if (device < 0) {
177 fprintf(stderr, "vat: AF: cannot find ulaw device");
178 exit(1);
179 }
180 }
181
182 /* set up audio context, find sample size and sample rate */
183
184 AFSetACAttributes attr;
185 attr.type = MU255;
186 raf.ac = AFCreateAC(raud, device, ACEncodingType, &attr);
187 saf.ac = AFCreateAC(paud, device, ACEncodingType, &attr);
188 #ifdef notyet
189 haf.ac = AFCreateAC(paud, HIFI_LEFT, 0, 0);
190 #endif
191 paf = &saf;
192 raf.gain = AFQueryInputGain(raf.ac, &raf.mingain, &raf.maxgain);
193 chksoftgain(raf, rgain, ACRecordGain);
194 saf.gain = AFQueryOutputGain(saf.ac, &saf.mingain, &saf.maxgain);
195 chksoftgain(saf, pgain, ACPlayGain);
196 #ifdef notyet
197 haf.gain = AFQueryOutputGain(haf.ac, &haf.mingain, &haf.maxgain);
198 chksoftgain(haf);
199 #endif
200 /*
201 * Set midscale initial values. Since the server won't tell us
202 * what the real initial value is, and we want the slider position
203 * to reflect the startup value, we have no other choice.
204 */
205 SetRGain(128);
206 SetPGain(128);
207
208 afblksize = blksize * blocks;
209 replybuf = new u_char[afblksize + sizeof(aReply)];
210 readbuf = replybuf + sizeof(aReply);
211 readptr = readbufend = readbuf + afblksize;
212
213 nextframe = 0;
214 firstframe = 0;
215 usingbuf = 0;
216 if (afblksize != blksize)
217 writebuf = new u_char[afblksize];
218 else
219 writebuf = 0;
220
221 poff = 3 * afblksize;
222 plusoff = poff << 5;
223 plusvar = 0;
224
225 lastmean_[0] = 0;
226 lastmean_[1] = 0;
227 lastmean_[2] = 0;
228 lastmean_[3] = 0;
229
230 /* open (or create) the lock file */
231 openlock();
232 }
233
234 void AFAudio::noserver()
235 {
236 if (getenv("AUDIOFILE") == 0) {
237 fprintf(stderr,
238 "vat: can't connect to AF server (AUDIOFILE not set)");
239 } else
240 fprintf(stderr, "can't connect to AF server");
241 exit(1);
242 }
243
244 /* Find a suitable default device (the first device not connected to the phone)
245 * Returns device number or -1 if no suitable device can be found.
246 */
247 int
248 AFAudio::FindDefaultDevice(AFAudioConn* aud)
249 {
250 char *s = (char *)getenv("AF_DEVICE");
251 if (s != NULL)
252 return (atoi(s));
253
254 /* Find the first non-phone, 8kHz, mono device */
255 int n = ANumberOfAudioDevices(aud);
256 for (int i = 0; i < n; ++i) {
257 AFDeviceDescriptor* a = AAudioDeviceDescriptor(aud, i);
258 if (a->inputsFromPhone == 0 && a->outputsToPhone == 0 &&
259 a->playSampleFreq == 8000 && a->playNchannels == 1)
260 return (i);
261 }
262 return (-1);
263 }
264
265 void AFAudio::Release()
266 {
267 if (HaveAudio()) {
268 /* gobble the result of the in-progress read */
269 aRecordSamplesReply reply;
270 _AReply(raud, (aReply*)&reply, 0, aFalse);
271 if (reply.length * 4 == afblksize) {
272 char dummy[512];
273 _AReadPad(raud, dummy, afblksize);
274 }
275 unlock();
276 unlink();
277 fd_ = -1;
278 notify();
279 }
280 }
281
282 void AFAudio::Obtain()
283 {
284 if (HaveAudio())
285 abort();
286
287 if (lock() == 0) {
288 /* audio is ours - kick off first read */
289 fd_ = raud->fd;
290 aftime = AFGetTime(raf.ac);
291 wrttime = 0;
292 minusoff = 0;
293 readptr = readbufend;
294 SendReadRequest();
295 Audio::Obtain();
296 }
297 }
298
299 void AFAudio::Write(u_char *cp)
300 {
301 if (HaveAudio()) {
302 if (afblksize != blksize) {
303 if (nextframe == 0) {
304 firstframe = cp;
305 nextframe = cp + blksize;
306 usingbuf = 0;
307 return;
308 }
309 if (nextframe != cp) {
310 if (! usingbuf) {
311 /*
312 * frames wrapped in ss buffer --
313 * copy to writebuf to keep things
314 * contiguous.
315 */
316 int curlen = nextframe - firstframe;
317 memcpy(writebuf, firstframe, curlen);
318 firstframe = writebuf;
319 nextframe = writebuf + curlen;
320 usingbuf = 1;
321 }
322 memcpy(nextframe, cp, blksize);
323 }
324 nextframe += blksize;
325 u_int len = nextframe - firstframe;
326 if (len < afblksize)
327 return;
328
329 cp = firstframe;
330 nextframe = 0;
331 }
332 u_int at = aftime + poff;
333 if (at - wrttime > 3 * afblksize && wrttime) {
334 /*
335 * start of talk after silence -- see if we
336 * should adjust offset. If AF missed any
337 * frames in the last talkspurt, adjust the
338 * offset to one that wouldn't have missed any
339 * frames. Otherwise if we're more than a
340 * frame time ahead of the recent average offset,
341 * drop the current offset by half the difference
342 * (or the max that wouldn't reorder AF playout,
343 * whichever is smaller).
344 */
345 u_int noff = poff;
346 if (minusoff) {
347 noff = minusoff >> 2;
348 minusoff = 0;
349 } else {
350 /*
351 * we went through the last talkspurt
352 * with no drops & an average backlog
353 * variation between us & AF of 'plusvar'.
354 * To avoid drops we need 2*afblksize +
355 * 2*plusvar of buffer between us & AF.
356 * If we have more than that, reduce it.
357 */
358 u_int doff = (plusvar >> 2) + (2 * afblksize);
359 if (doff < noff) {
360 int adj = (noff - doff) >> 2;
361 noff -= adj;
362 if (int(noff) < int(wrttime - aftime))
363 noff = wrttime - aftime;
364 }
365 }
366 if (noff != poff) {
367 poff = (noff + 3) & ~3;
368 at = aftime + noff;
369 }
370 }
371 wrttime = at;
372 u_int now = AFPlaySamples(paf->ac, at, afblksize, cp);
373 int dif = now - at;
374 if (dif > 0) {
375 u_int noff = now - aftime + 2 * afblksize;
376 if (minusoff)
377 minusoff += noff - (minusoff >> 1);
378 else
379 minusoff = noff << 1;
380 if (++pmiss >= 3) {
381 /*
382 * losing bad - adapt now rather than
383 * waiting for next talkspurt.
384 */
385 poff = ((minusoff >> 1) + 3) & ~3;
386 pmiss = 0;
387 minusoff = 0;
388 }
389 } else {
390 int delta = dif + (plusoff >> 5);
391 plusoff -= delta;
392 if (delta < 0)
393 delta = -delta;
394 plusvar += delta - (plusvar >> 3);
395 pmiss = 0;
396 }
397 }
398 }
399
400 void AFAudio::SendReadRequest()
401 {
402 register aRecordSamplesReq *req;
403
404 if (HaveAudio()) {
405 #define aud raud
406 GetReq(RecordSamples, req);
407 #undef aud
408 req->ac = raf.ac->acontext;
409 req->startTime = aftime;
410 req->nbytes = afblksize;
411 req->sampleType = raf.ac->attributes.type;
412 req->nchannels = raf.ac->attributes.channels;
413 req->mask = ABlockMask;
414 if (raf.ac->attributes.endian == ABigEndian)
415 req->mask |= ABigEndianMask;
416 _AFlush(raud);
417 }
418 }
419
420 int AFAudio::FrameReady()
421 {
422 u_char* cp = readptr;
423 if (cp >= readbufend) {
424 aRecordSamplesReply* reply = (aRecordSamplesReply*)replybuf;
425 if (_AReplyAsync(raud, (aReply*)reply, afblksize >> 2, aFalse) <= 0)
426 /* no data available */
427 return (0);
428
429 /* queue the next read */
430 SendReadRequest();
431 /*
432 * If we get too far behind or get confused &
433 * think we're ahead, jump forward. (This can
434 * easily happen if the process is suspended.)
435 */
436 u_int srvtime = reply->currentTime;
437 u_int dif = srvtime - aftime;
438 if (dif > 16000)
439 if (int(dif) < -1600 || int(dif) > 0) {
440 aftime = srvtime;
441 }
442 aftime += afblksize;
443 readptr = readbuf;
444 }
445 return (1);
446 }
447
448 extern const unsigned char lintomulaw[];
449 extern const short mulawtolin[];
450
451 u_char* AFAudio::Read()
452 {
453 u_char* cp = readptr;
454 readptr = cp + blksize;
455
456 /*
457 * remove any dc bias from the input signal.
458 */
459 register u_char* ip = cp;
460 register u_char* ep = readptr;
461 register int smean = lastmean_[iport];
462 register const short* u2l = mulawtolin;
463 register const u_char* l2u = lintomulaw;
464 while (ip < ep) {
465 register int mean, dif;
466 register int s0 = u2l[ip[0]];
467 register int s1 = u2l[ip[1]];
468 register int s2 = u2l[ip[2]];
469 register int s3 = u2l[ip[3]];
470
471 mean = smean >> 13;
472 dif = s0 - mean;
473 smean += dif;
474 ip[0] = l2u[dif & 0xffff];
475
476 mean = smean >> 13;
477 dif = s1 - mean;
478 smean += dif;
479 ip[1] = l2u[dif & 0xffff];
480
481 mean = smean >> 13;
482 dif = s2 - mean;
483 smean += dif;
484 ip[2] = l2u[dif & 0xffff];
485
486 mean = smean >> 13;
487 dif = s3 - mean;
488 smean += dif;
489 ip[3] = l2u[dif & 0xffff];
490
491 ip += 4;
492 }
493 return (cp);
494 }
495
496 int AFAudio::slidergain(const afstate& af) const
497 {
498 if (af.mingain == 0 && af.maxgain == 0)
499 /* Not adjustable. Just maintain mid-scale */
500 return (128);
501
502 float range = af.maxgain - af.mingain;
503 return (int(255. * float(af.gain - af.mingain) / range));
504 }
505
506 void AFAudio::setgain(int level, afstate& af)
507 {
508 float range = af.maxgain - af.mingain;
509 af.gain = int(af.mingain + range * float(level) / 255.);
510 }
511
512 void AFAudio::SetRGain(int level)
513 {
514 setgain(level, raf);
515 if (raf.soft) {
516 AFSetACAttributes attr;
517 attr.rec_gain = raf.gain;
518 AFChangeACAttributes(raf.ac, ACRecordGain, &attr);
519 } else
520 AFSetInputGain(raf.ac, raf.gain);
521 rgain = slidergain(raf);
522 }
523
524 void AFAudio::SetPGain(int level)
525 {
526 setgain(level, *paf);
527 if (paf->soft) {
528 AFSetACAttributes attr;
529 attr.play_gain = paf->gain;
530 AFChangeACAttributes(paf->ac, ACPlayGain, &attr);
531 } else
532 AFSetOutputGain(paf->ac, paf->gain);
533 pgain = slidergain(*paf);
534 }
535
536 void AFAudio::OutputPort(int p)
537 {
538 oport = p;
539 #ifdef notyet
540 paf = oport? &haf : &saf;
541 #else
542 paf = &saf;
543 #endif
544 }
545
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.