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

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

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

  1 /*
  2  * audio-freebsd.cc --
  3  *
  4  *      Old audio driver for FreeBSD 2 and 3.  This has been superseded by
  5  *      audio-oss.cc.
  6  */
  7 
  8 /*
  9  * Full Duplex audio module for the new sound driver and full duplex
 10  * cards. Luigi Rizzo, from original sources supplied by Amancio Hasty.
 11  *
 12  * This includes some enhancements:
 13  * - limit the maximum size of the playout queue to approx 4 frames;
 14  *   this is necessary if the write channel is slower than expected;
 15  *   the fix is based on two new ioctls, AIOGCAP and AIONWRITE,
 16  *   but the code should compile with the old driver as well.
 17  * - use whatever format is available from the card (included split
 18  *   format e.g. for the sb16);
 19  */
 20 
 21 
 22 #include <fcntl.h>
 23 #include <machine/soundcard.h>
 24 #include "audio.h"
 25 #include "mulaw.h"
 26 #include "tclcl.h"
 27 
 28 #define ULAW_ZERO 0x7f
 29 
 30 class FreeBSDAudio : public Audio {
 31     public:
 32         FreeBSDAudio();
 33         virtual int FrameReady();
 34         virtual u_char* Read();
 35         virtual void Write(u_char *);
 36         virtual void SetRGain(int);
 37         virtual void SetPGain(int);
 38         virtual void OutputPort(int);
 39         virtual void InputPort(int);
 40         virtual void Obtain();
 41         virtual void Release();
 42 /*      virtual int HalfDuplex() const; */
 43     protected:
 44 
 45         u_char* readbuf;
 46         u_short *s16_buf;
 47 
 48         int mixerfd;
 49 
 50 #if defined(AIOGCAP) /* new sound driver */
 51         int play_fmt, rec_fmt ; /* the sb16 has split format... */
 52         snd_capabilities soundcaps;
 53 #endif
 54 };
 55 
 56 static class FreeBSDAudioClass : public TclClass {
 57 public:
 58         FreeBSDAudioClass() : TclClass("Audio/FreeBSD") {}
 59         TclObject* create(int, const char*const*) {
 60             return (new FreeBSDAudio);
 61 
 62         }
 63 } freebsd_audio_class;
 64 
 65 FreeBSDAudio::FreeBSDAudio() : mixerfd(-1)
 66 {
 67     readbuf = new u_char[blksize_];
 68     s16_buf = new u_short[blksize_];
 69 
 70     memset(readbuf, ULAW_ZERO, blksize_);
 71 
 72     /*
 73      * The only way to determine if the device is full duplex
 74      * or not is by actually opening it.  Unfortunately, we might
 75      * not be able to open it because some other process is
 76      * using it.  Assume half-duplex.  Obtain() will override
 77      * if appropriate.
 78      */
 79     duplex_ = 0;
 80 
 81     input_names_ = "mike linein";
 82     output_names_ = "speaker lineout";
 83 }
 84 
 85 void
 86 FreeBSDAudio::Obtain()
 87 {
 88     char *thedev;
 89     char buf[64];
 90     int d = -1;
 91 
 92     if (haveaudio())
 93         abort();
 94     thedev=getenv("AUDIODEV");
 95     if (thedev==NULL)
 96         thedev="/dev/audio";
 97     else if (thedev[0]>='') {
 98         d = atoi(thedev);
 99         sprintf(buf,"/dev/audio%d", d);
100         thedev = buf ;
101     }
102     fd_ = open(thedev, O_RDWR );
103     thedev=getenv("MIXERDEV");
104     if (thedev == NULL)
105         if (d < 0)
106             thedev = "/dev/mixer";
107         else {
108             sprintf(buf,"/dev/mixer%d", d);
109             thedev = buf ;
110         }
111 
112     mixerfd = open(thedev, O_RDWR);
113     if (fd_ >= 0) {
114         snd_chan_param pa;
115         struct snd_size sz;
116 
117         ioctl(fd_, AIOGCAP, &soundcaps);
118 
119         // ErikM
120         printf("FreeBSDAudio::Obtain: soundcaps.\n");
121         printf("\tleft=%d\n\tright=%d\n", soundcaps.left, soundcaps.right);
122         printf("\tformats=0x%lx\n", soundcaps.formats);
123         printf("\tinputs=0x%lx\n", soundcaps.inputs);
124         printf("\tmixers=0x%lx\n", soundcaps.mixers);
125         printf("\trate_min=%ld\n", soundcaps.rate_min);
126         printf("\trate_max=%ld\n", soundcaps.rate_max);
127 
128         pa.play_rate = pa.rec_rate = 8000 ;
129         pa.play_format = pa.rec_format = AFMT_MU_LAW ;
130         switch (soundcaps.formats & (AFMT_FULLDUPLEX | AFMT_WEIRD)) {
131         case AFMT_FULLDUPLEX :
132 
133           // ErikM
134           printf("FreeBSDAudio::Obtain: in AFMT_FULLDUPLEX\n");
135 
136             /*
137              * this entry for cards with decent full duplex. Use s16
138              * preferably (some are broken in ulaw) or ulaw or u8 otherwise.
139              */
140             if (soundcaps.formats & AFMT_S16_LE)
141                 pa.play_format = pa.rec_format = AFMT_S16_LE ;
142             else if (soundcaps.formats & AFMT_MU_LAW)
143                 pa.play_format = pa.rec_format = AFMT_MU_LAW ;
144             else if (soundcaps.formats & AFMT_U8)
145                 pa.play_format = pa.rec_format = AFMT_U8 ;
146             else {
147                 printf("sorry, no supported formats\n");
148                 close(fd_);
149                 fd_ = -1 ;
150                 return;
151             }
152             break ;
153         case AFMT_FULLDUPLEX | AFMT_WEIRD :
154 
155           // ErikM
156           printf("FreeBSDAudio::Obtain: in AFMT_FULLDUPLEX | AFMT_WEIRD\n");
157 
158           /* this is the sb16... */
159             if (soundcaps.formats & AFMT_S16_LE) {
160                 pa.play_format = AFMT_S8 ;
161                 pa.rec_format = AFMT_S16_LE;
162             } else {
163                 printf("sorry, no supported formats\n");
164                 close(fd_);
165                 fd_ = -1 ;
166                 return;
167             }
168             break ;
169         default :
170 #if 0
171             printf("sorry don't know how to deal with this card\n");
172             close (fd_);
173             fd_ = -1;
174 #endif
175             break;
176         }
177 
178         // ErikM
179         printf("FreeBSDAudio::Obtain: pa.play_format=0x%lx, pa.rec_format=0x%lx\n",
180                pa.play_format, pa.rec_format);
181 
182 
183         ioctl(fd_, AIOSFMT, &pa);
184         play_fmt = pa.play_format ;
185         rec_fmt = pa.rec_format ;
186         sz.play_size = (play_fmt == AFMT_S16_LE) ? 2*blksize_ : blksize_;
187         sz.rec_size = (rec_fmt == AFMT_S16_LE) ? 2*blksize_ : blksize_;
188         ioctl(fd_, AIOSSIZE, &sz);
189 
190         /*
191          * note: this uses a modified function of the new driver,
192          * which will return AFMT_FULLDUPLEX set in SNDCTL_DSP_GETFMTS
193          * for full-duplex devices. In the old driver this was 0 so
194          * the default is to use half-duplex for them. Note also that I have
195          * not tested half-duplex operation.
196          */
197         int i;
198         ioctl(fd_, SNDCTL_DSP_GETFMTS, &i);
199         // ErikM
200 #if 1
201         printf("SNDCTL_DSP_GETFMTS returns 0x%08x %s duplex\n",
202                i, i & AFMT_FULLDUPLEX ? "full":"half");
203 #endif
204         duplex_ =  (i & AFMT_FULLDUPLEX) ? 1 : 0 ;
205 
206 // ErikM
207         printf("FreeBSDAudio::Obtain: duplex_ = %d\n", duplex_);
208 
209 
210         /*
211          * Set the line input level to 0 to shut
212          * off the analog side-tone gain between
213          * the line-in and line-out.  This would otherwise
214          * wreak havoc on an echo canceler, for example,
215          * plugged into the audio adaptor.
216          */
217         int v = 0;
218         (void)ioctl(mixerfd, MIXER_WRITE(SOUND_MIXER_LINE), &v);
219 
220         /*
221          * Restore the hardware settings in case
222          * some other vat changed them.
223          */
224         InputPort(iport_);
225         SetRGain(rgain_);
226         SetPGain(pgain_);
227 
228         if (duplex_)
229                 Audio::Obtain();
230         else
231                 notify();
232     }
233 }
234 
235 
236 void FreeBSDAudio::Release()
237 {
238     if (haveaudio()) {
239             if (mixerfd > 0) {
240                     close(mixerfd);
241             }
242             mixerfd = -1;
243             Audio::Release();
244     }
245 }
246 
247 void FreeBSDAudio::Write(u_char *cp)
248 {
249     long i = blksize_, l;
250 
251 #if 0 && defined(AIOGCAP)
252     int queued;
253 #endif
254 
255     if (play_fmt == AFMT_S16_LE) {
256         for (i=0; i< (long) blksize_; i++)
257             s16_buf[i] = mulawtolin[cp[i]] ;
258         cp = (u_char *)s16_buf;
259         i = 2 *blksize_ ;
260     }
261     else if (play_fmt == AFMT_S8) {
262         for (i=0; i< (long) blksize_; i++) {
263             int x = mulawtolin[cp[i]] ;
264             x =  (x >> 8 ) & 0xff;
265             cp[i] = (u_char)x ;
266         }
267         i = blksize_ ;
268     } else if (play_fmt == AFMT_U8) {
269         for (i=0; i< (long) blksize_; i++) {
270             int x = mulawtolin[cp[i]] ;
271             x =  (x >> 8 ) & 0xff;
272             x = (x ^ 0x80) & 0xff ;
273             cp[i] = (u_char)x ;
274         }
275         i = blksize_ ;
276     }
277 #if 0 && defined(AIOGCAP)
278     ioctl(fd_, AIONWRITE, &queued);
279     queued = soundcaps.bufsize - queued ;
280     if (play_fmt == AFMT_S16_LE) {
281         if (queued > 8*blksize_)
282             i -= 8 ;
283     } else {
284         if (queued > 4*blksize_)
285             i -= 4 ;
286     }
287 #endif
288     for ( ; i > 0 ; i -= l) {
289         l = write(fd_, cp, i);
290         cp += l;
291     }
292 }
293 
294 u_char* FreeBSDAudio::Read()
295 {
296     u_char* cp;
297     long l=0, l0 = blksize_,  i = blksize_;
298 
299     cp = readbuf;
300 
301     if (rec_fmt == AFMT_S16_LE) {
302         cp = (u_char *)s16_buf;
303         l0 = i = 2 *blksize_ ;
304     }
305     for ( ; i > 0 ; i -= l ) {
306         l = read(fd_, cp, i);
307         cp += l ;
308     }
309     if (rec_fmt == AFMT_S16_LE) {
310         for (i=0; i< (long) blksize_; i++)
311             readbuf[i] = lintomulaw[ s16_buf[i] & 0xffff ] ;
312     }
313     else if (rec_fmt == AFMT_S8) {
314         for (i=0; i< (long) blksize_; i++)
315             readbuf[i] = lintomulaw[ readbuf[i]<<8 ] ;
316     }
317     else if (rec_fmt == AFMT_U8) {
318         for (i=0; i< (long) blksize_; i++)
319             readbuf[i] = lintomulaw[ (readbuf[i]<<8) ^ 0x8000 ] ;
320     }
321     return readbuf;
322 }
323 
324 /*
325  * should check that I HaveAudio() before trying to set gain.
326  *
327  * In most mixer devices, there is only a master volume control on
328  * the capture channel, so the following code does not really work
329  * as expected. The only (partial) exception is the MIC line, where
330  * there is generally a 20dB boost which can be enabled or not
331  * depending on the type of device.
332  */
333 void FreeBSDAudio::SetRGain(int level)
334 {
335     rgain_ = level;
336     float x = level;
337     level = (int) (x/2.56);
338     int foo = (level<<8) | level;
339     switch (iport_) {
340     case 2:
341         if (ioctl(mixerfd, MIXER_WRITE(SOUND_MIXER_IGAIN), &foo) == -1)
342            perror("failed set input line volume");
343         break;
344     case 1:
345         if (ioctl(mixerfd, MIXER_WRITE(SOUND_MIXER_IGAIN), &foo) == -1)
346            perror("failed set input line volume");
347         break;
348     case 0:
349         if (ioctl(mixerfd, MIXER_WRITE(SOUND_MIXER_MIC), &foo) == -1)
350             perror("failed to set mic volume");
351         if (ioctl(mixerfd, MIXER_WRITE(SOUND_MIXER_IGAIN), &foo) == -1)
352            perror("failed set input line volume");
353         break;
354     }
355 }
356 
357 void FreeBSDAudio::SetPGain(int level)
358 {
359     pgain_ = level;
360     float x = level;
361     level = (int) (x/2.56);
362     int foo = (level<<8) | level;
363     if (mixerfd >= 0) {
364             if (ioctl(mixerfd, MIXER_WRITE(SOUND_MIXER_PCM), &foo) == -1) {
365                     perror("failed to output level");
366             }
367     }
368 }
369 
370 void FreeBSDAudio::OutputPort(int p)
371 {
372     oport_ = p;
373 }
374 
375 void FreeBSDAudio::InputPort(int p)
376 {
377     int   zero = 0;
378 
379     switch(p) {
380     case 2:
381         zero = 1 << SOUND_MIXER_CD;
382         break;
383     case 1:
384         zero = 1 << SOUND_MIXER_LINE;
385         break;
386     case 0 :
387         zero = 1 << SOUND_MIXER_MIC;
388         break;
389     }
390     if ( ioctl(mixerfd, SOUND_MIXER_WRITE_RECSRC, &zero) == -1 ) {
391         perror("failed to select input");
392         p = 0;
393     }
394     iport_ = p;
395 }
396 
397 /*
398  * FrameReady must return 0 every so often, or the system will keep
399  * processing mike data and not other events.
400  */
401 int FreeBSDAudio::FrameReady()
402 {
403     int i = 0;
404     int lim = blksize_;
405 
406     ioctl(fd_, FIONREAD, &i );
407     if (rec_fmt == AFMT_S16_LE) lim = 2*blksize_;
408     return (i >= lim) ? 1 : 0 ;
409 }
410 
411 /*** end of file ***/
412 

~ [ 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.