~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Open Mash Cross Reference
mash/audio/audio-af.cc

Component: ~ [ mash ] ~ [ apps ] ~ [ gsm ] ~ [ lib ] ~ [ otcl ] ~ [ srm ] ~ [ tcl8.3 ] ~ [ tclcl ] ~ [ tk8.3 ] ~ [ tutorials ] ~

  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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.