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

Open Mash Cross Reference
mash/tcl/net/agent-audio.tcl

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

  1 # agent-audio.tcl --
  2 #
  3 #       FIXME: This file needs a description here.
  4 #
  5 # Copyright (c) 1996-2002 The Regents of the University of California.
  6 # All rights reserved.
  7 #
  8 # Redistribution and use in source and binary forms, with or without
  9 # modification, are permitted provided that the following conditions are met:
 10 #
 11 # A. Redistributions of source code must retain the above copyright notice,
 12 #    this list of conditions and the following disclaimer.
 13 # B. Redistributions in binary form must reproduce the above copyright notice,
 14 #    this list of conditions and the following disclaimer in the documentation
 15 #    and/or other materials provided with the distribution.
 16 # C. Neither the names of the copyright holders nor the names of its
 17 #    contributors may be used to endorse or promote products derived from this
 18 #    software without specific prior written permission.
 19 #
 20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
 21 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 22 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 23 # ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
 24 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 25 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 26 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 27 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 28 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 30 #
 31 # @(#) $Header: /usr/mash/src/repository/mash/mash-1/tcl/net/agent-audio.tcl,v 1.72 2003/06/03 20:06:45 aswan Exp $
 32 
 33 
 34 import RTPAgent RTP/Audio Configuration \
 35         AnnounceListenManager/AS/Client/MeGa/Audio
 36 
 37 Module/AudioEncoder set nb_ 0
 38 if [TclObject is-class Audio] {
 39         Audio set duplex_ 1
 40 }
 41 AudioController set echo_thresh_ 0
 42 AudioController set echo_suppress_time_ 0
 43 AudioController set idle_drop_time_ 0
 44 
 45 
 46 #------------------------------------------------------------------
 47 # Class: 
 48 #   AudioAgent
 49 # Description:
 50 #
 51 # The AudioAgent class provides a coarse-grained interface
 52 # that abstracts away all the details of networked audio.
 53 # This agent is responsible for creating the underlying RTP
 54 # session for the audio channel, for opening and initializing
 55 # the audio codec hardware, and for creating the AudioController
 56 # that orchestrates the timing and synchronization of all
 57 # audio events.
 58 # <p>
 59 # Since AudioAgent is derived from the RTPAgent base class,
 60 # the standard RTP observer API is supported.  Any ``interesting''
 61 # events (e.g., the arrival of a new RTP flow) within the underlying
 62 # RTP sessions are relayed to all the observers attached to this agent.
 63 # (Note that the AudioAgent class catches and handles some of the
 64 # events on its own.)
 65 # <p>
 66 # Typically, a mash script will create a separate user interface
 67 # object and attach it to the AudioAgent as an Observer.
 68 #------------------------------------------------------------------
 69 Class AudioAgent -superclass { RTPAgent RTP/Audio } -configuration {
 70         megaAudioFormat gsm
 71         # multicast is default
 72         megaRecvAudioPort 0
 73         audioSessionBW 20
 74         megaAudioCtrl 224.4.5.24/50000/31
 75         audioServiceLocation urn:agw
 76 }
 77 
 78 #------------------------------------------------------------------
 79 # Method: 
 80 #   AudioAgent init
 81 # Description:
 82 #
 83 # Initialize a new instance of an AudioAgent.
 84 # initialize the network (alot of it is done
 85 # in the parent classes) and setup the audioStream
 86 #
 87 AudioAgent public init { app spec {callback {}} } {
 88         set ab [SessionAddress parse $spec]
 89         if { $ab != "" } {
 90                 set fmt [$ab fmt]
 91                 if { $fmt != {} } { $self add_option audioFormat $fmt }
 92         }
 93         
 94         $self next $ab $callback
 95         if { $ab != "" } {
 96                 delete $ab
 97         }
 98 
 99         # send back-to-back packets spaced out at 128kb/s
100         $self set-bandwidth 1280000
101 
102         #FIXME
103         $self site-drop-time [$self get_option siteDropTime]
104 
105 
106         $self instvar decoders_ audioDevice_ sampRate use16bit useStereo deviceName
107         set decoders_ ""
108 
109         #
110         # Set up a table to map lower-case RTP format
111         # names into the suffix of the class name
112         # that encodes or decodes this type
113         #
114         $self instvar classmap_
115         set classmap_(pcm) PCM
116         set classmap_(lpc) LPC
117         set classmap_(gsm) GSM
118         set classmap_(dvi) ADPCM
119         set classmap_(mp3) MP3
120         # added so vat supports linear 16, used with aserver
121         set classmap_(lin16) PCM
122 #       $self start_mega
123         $self instvar session_
124         if ![info exists sampRate] {
125             set sampRate 8000
126         }
127         if ![info exists use16bit] {
128             set use16bit 0
129         }
130         if ![info exists useStereo] {
131             set useStereo 0
132         }
133 
134         set srcid [$self get_local_srcid]
135         if ![info exists deviceName] {
136             set audioDevice_ [new AudioStream $srcid $session_ $sampRate $use16bit $useStereo]
137         } else {
138             set audioDevice_ [new AudioStream $srcid $session_ $sampRate $use16bit $useStereo $deviceName]
139         }
140 }
141 
142 # old code, no longer used
143 AudioAgent public start_mega { } {
144         $self instvar al_
145         if [info exists al_] { delete $al_ }
146         if { [$self get_option megaAudioSession] != "" } {
147                 set sname [$self get_option megaAudioSession]
148                 set sspec [$self get_option audioSessionSpec]
149                 set rportspec [$self get_option megaRecvAudioPort]
150                 set ofmt [$self get_option megaAudioFormat]
151                 set sbw [$self get_option audioSessionBW]
152                 set bw [expr 0.02*$sbw*1000]
153                 set megaspec [$self get_option megaAudioCtrl]
154                 set loc [$self get_option audioServiceLocation]
155 
156                 set ab [new AddressBlock $sspec]
157                 set sspec [$ab addr]/[$ab sport]:[$ab rport]/[$ab ttl]
158                 delete $ab
159                 set al_ [new AnnounceListenManager/AS/Client/MeGa/Audio \
160                                 $self $megaspec $bw [Application name] audio \
161                                 $sname $sspec $rportspec $ofmt $loc]
162                 $al_ start
163         }
164 }
165 
166 
167 #------------------------------------------------------------------
168 # Method: 
169 #   AudioAgent destroy
170 # Description:
171 #
172 # Deallocate all the resources associated with
173 # the AudioAgent class.  Close the audio device
174 # (and implicitly delete it) and free up the buffer pool.
175 #
176 AudioAgent public destroy {} {
177     $self instvar al_ audioDevice_
178     if [info exists audioDevice_] { delete $audioDevice_ } 
179     if [info exists al_] { delete $al_ }
180     $self next
181 }
182 
183 # old code, no longer used
184 # Used when we need to point the mega audio gateway we're controlling
185 # to listen to a new source.  This is kludgy, and needs a redesign.
186 #
187 AudioAgent public reset_mega {} {
188         $self instvar al_
189         if ![info exists al_] {
190                 $self start_mega
191         } else {
192                 $al_ reset_spec [$self get_option audioSessionSpec]
193         }
194 }
195 
196 #------------------------------------------------------------------
197 # Method: 
198 #   AudioAgent activate
199 # Description:
200 #
201 # Handle an activate event on source <i>src</i>.
202 # Called from C++ (through Source/RTP) when we start
203 # actively receiving data pkts from the given source.
204 # Create the decoder and dispatch the method to the
205 # observers as normal.
206 # Extends method in RTPAgent.
207 #
208 AudioAgent private activate src {
209         $self instvar decoders_
210         set d [$self create_decoder $src]
211         lappend decoders_ $d
212         $src handler $d
213         $self next $src
214 }
215 
216 #------------------------------------------------------------------
217 # Method: 
218 #   AudioAgent deactivate
219 # Description:
220 #
221 # Handle a deactivate event on source <i>src</i>.
222 # Called from C++ (through Source/RTP) when a source
223 # has left the session (either via an RTCP BYE message
224 # or via an expiration timer).  This method can also
225 # get called back if the Source object is explicitly
226 # deleted from the local program.
227 # Extends method in RTPAgent.
228 #
229 AudioAgent private deactivate src {
230         $self instvar decoders_
231         set d [$src handler]
232         set k [lsearch -exact $decoders_ $d]
233         set decoders_ [lreplace $decoders_ $k $k]
234         $self next $src
235         delete $d
236 }
237 
238 #
239 #------------------------------------------------------------------
240 # Method: 
241 #   AudioAgent trigger_media
242 # Description:
243 #
244 # Handle a media-trigger event on source <i>src</i>.
245 # Called from C++ (through Source/RTP) when a data packet
246 # arrives from source <i>src</i> and its media trigger is enabled.
247 # Extends method in RTPAgent.
248 #
249 
250 AudioAgent instproc trigger_media src {
251         $self instvar local_chan_
252         if [info exists local_chan_] {
253                 set cname [$src sdes cname]
254                 if { "$cname" != "" } {
255                         $local_chan_ send FOCUS_SPEAKER $cname
256                 }
257         }
258         $self next $src
259 }
260 
261 #
262 #------------------------------------------------------------------
263 # Method: 
264 #   AudioAgent attach_local_channel
265 #   AudioAgent attach_global_channel
266 # Description:
267 #
268 # Attach a CoordinationBus object to this agent.
269 # When present, the coordination bus enables a number
270 # of inter-agent interactions, e.g., voice-switched-video
271 # windows orchestrated between video and audio agents.
272 #
273 
274 AudioAgent instproc attach_local_channel lc {
275         $self set local_chan_ $lc
276 }
277 AudioAgent instproc attach_global_channel gc {
278         $self set glob_chan_ $gc
279 }
280 
281 #
282 #------------------------------------------------------------------
283 # Method: 
284 #   AudioAgent set_maxchannel
285 # Description:
286 #
287 # Called by the underlying network object when the number
288 # of media layers expected across all sources changes.
289 # For example, an underlying network protocol might adjust
290 # the number of multicast channels received to carry out
291 # congestion control.  When the level of subscription changes,
292 # the codecs must be informed so that they do not wait
293 # unnecessarily for packets that will never arrive (because
294 # the corresponding layer is not present).
295 # <p>
296 # Extends method in RTPAgent.
297 #
298 AudioAgent private set_maxchannel n {
299         global active
300         foreach s [array names active] {
301                 set d [$s handler]
302                 $d set maxChannel_ $n
303         }
304 }
305 
306 #
307 #------------------------------------------------------------------
308 # Method: 
309 #   AudioAgent create_decoder
310 # Description:
311 #
312 # Create an audio decoder that can decode the RTP flow
313 # from source <i>src</i>.  Assumes this source has received
314 # media packets so we can determine the RTP payload type
315 # and allocate an appropriate decoder.  If the system
316 # does not support the media type, a ``null decoder'' is
317 # created and returned.
318 #
319 AudioAgent public create_decoder src {
320     $self instvar classmap_ audioDevice_
321     if ![info exists classmap_([$src format_name])] {
322         # don't support this format
323         set decoder [new Module/AudioDecoder/Null]
324     } else {
325         set decoder [new Module/AudioDecoder/$classmap_([$src format_name])]
326     }
327     
328     if { $decoder == "" } {
329         # don't support this format
330         set decoder [new Module/AudioDecoder/Null]
331     }
332     #FIXME
333     set controller [$audioDevice_ get_controller]
334     if { $controller == 0 } {
335         puts stderr "AudioAgent: no audio controller."
336         exit 0
337     }
338     $decoder set agent_ $self
339     $decoder set src_ $src
340     $decoder controller $controller
341     $src handler $decoder
342     
343     return $decoder
344 }
345 
346 #
347 #------------------------------------------------------------------
348 # Method: 
349 #   AudioAgent create_session
350 # Description:
351 #
352 # Create a Session object that appropriate for this
353 # type of agent.  Called by the RTPAgent class when
354 # initializing the network state.
355 #
356 AudioAgent private create_session {} {
357         return [new Session/RTP/Audio]
358 }
359 
360 #------------------------------------------------------------------
361 # Method: 
362 #   AudioAgent reset
363 # Description:
364 #
365 # Assign a new address (or set of addresses) to the
366 # underlying communication session.  The address(es)
367 # must be in the AddressBlock object given by <i>ab</i>.
368 #
369 AudioAgent public reset ab {
370         $self next $ab
371         #
372         # audio shouldn't be looped back (unlike video),
373         # but if we might want to loop it back to other processes
374         # running on the local host (if the option is enabled)
375         #
376         $self app_loopback 0
377         $self net_loopback [$self get_option loopback]
378         $self set-bandwidth 128000
379 
380         #$self instvar set_pool_srcid_
381         #if ![info exists set_pool_srcid_] {
382             # not too sure when this is used, kind of an ad hoc way of doing it...
383             $self instvar audioDevice_
384             if [info exists audioDevice_] {
385                 set bufferPool [$audioDevice_ set bufferPool_]
386                 if ![info exists bufferPool]  {
387                     set bufferPool [new BufferPool/RTP]
388                 }
389                 $bufferPool srcid [$self get_local_srcid]
390             }
391             #set set_pool_srcid_ 1
392         #}
393 }
394 
395 #------------------------------------------------------------------
396 # Method: 
397 #   AudioAgent reset_source_offsets
398 # Description:
399 #
400 # Reset the clock offset of each Source object in the underlying
401 # communication session.  The clock offset is the skew between
402 # the sender's media timestamp and the local media time.
403 # Rather than run an elaborate synchronization scheme, the offset
404 # is computed and set at the start of each talk spurt.
405 # Since these offsets can drift when the Controller object
406 # is not running off the audio clock, this time base can
407 # shift pretty badly (especially on PC's where gettimeofday
408 # accuracy is low).  This hook allows the UI to note when
409 # the device is re-gained so that the offsets can be explicitly reset.
410 # FIXME - this is too much exposure of mechanism.  Figure out
411 # how to hide this.  The AudioAgent should be able to tell on
412 # its own thtat the device is re-obtained and should do the
413 # reset then...  Also, delay adaptation state
414 # should be kept in the source object... (and this method should
415 # live in RtpAgent)
416 #
417 AudioAgent instproc reset_source_offsets {} {
418         $self instvar decoders_
419         foreach d $decoders_ {
420                 $d reset-offset
421         }
422 }
423 
424 #------------------------------------------------------------------
425 # Method: 
426 #   AudioAgent bind_transducer
427 #   AudioAgent have_audio
428 #   AudioAgent set_input_mute
429 #   AudioAgent set_output_mute
430 #   AudioAgent set_input_gain
431 #   AudioAgent set_output_gain
432 #   AudioAgent get_input_gain
433 #   AudioAgent get_output_gain
434 #   AudioAgent set_input_port
435 #   AudioAgent set_output_port
436 #   AudioAgent get_input_port
437 #   AudioAgent get_output_port
438 #   AudioAgent get_input_ports
439 #   AudioAgent get_output_ports
440 #   AudioAgent get_input_portno
441 #   AudioAgent get_output_portno
442 #   AudioAgent set_speakerphone
443 #   AudioAgent audio_test
444 #   AudioAgent port_name_to_num
445 #   AudioAgent obtain
446 #   AudioAgent release
447 #   AudioAgent is_active
448 #   AudioAgent clear_active
449 #   AudioAgent select_format
450 #   AudioAgent set_silence_thresh
451 #   AudioAgent unix_time
452 #   AudioAgent ntp_time
453 # Description:
454 #
455 # A bunch of calls that just forward to the audioDevice_...
456 
457 AudioAgent public bind_transducer { which o } {
458     $self instvar audioDevice_
459     $audioDevice_ bind_transducer $which $o
460 }
461 
462 AudioAgent public have_audio {} {
463         $self instvar audioDevice_
464         if [info exists audioDevice_] {
465                 return [$audioDevice_ have_audio]
466         }
467         return 0
468 }
469 
470 AudioAgent public set_input_mute val {
471     $self instvar audioDevice_
472     $audioDevice_ set_input_mute $val
473 }
474 
475 AudioAgent public set_output_mute val {
476     $self instvar audioDevice_
477     $audioDevice_ set_output_mute $val
478 }
479 
480 AudioAgent public get_input_ports {} {
481     $self instvar audioDevice_
482     return [$audioDevice_ get_input_ports]
483 }
484 
485 AudioAgent public get_output_ports {} {
486     $self instvar audioDevice_
487     return [$audioDevice_ get_output_ports]
488 }
489 
490 AudioAgent public is_halfduplex {} {
491     $self instvar audioDevice_
492     return [$audioDevice_ is_halfduplex]
493 }
494 
495 AudioAgent public get_input_portno { } {
496     $self instvar audioDevice_
497     return [$audioDevice_ get_input_portno]
498 }
499 
500 AudioAgent public get_output_portno { } {
501     $self instvar audioDevice_
502     return [$audioDevice_ get_output_portno]
503 }
504 
505 AudioAgent public set_speakerphone { port mode } {
506     $self instvar audioDevice_
507     $audioDevice_ set_speakerphone $port $mode
508 }
509 
510 AudioAgent public audio_test type {
511     $self instvar audioDevice_
512     $audioDevice_ audio_test $type
513 }
514 
515 AudioAgent public port_name_to_num { which name } {
516     $self instvar audioDevice_
517     return [$audioDevice_ port_name_to_num $which $name]
518 }
519 
520 AudioAgent public set_input_port port {
521     $self instvar audioDevice_
522     $audioDevice_ set_input_port $port
523 }
524 
525 AudioAgent public set_output_port port {
526     $self instvar audioDevice_
527     $audioDevice_ set_output_port $port
528 }
529 
530 AudioAgent public set_input_gain gain {
531     $self instvar audioDevice_
532     return [$audioDevice_ set_input_gain $gain]
533 }
534 
535 AudioAgent public set_output_gain gain {
536     $self instvar audioDevice_
537     return [$audioDevice_ set_output_gain $gain]
538 }
539 
540 AudioAgent public get_input_gain {} {
541     $self instvar audioDevice_
542     return [$audioDevice_ get_input_gain]
543 }
544 
545 AudioAgent public get_output_gain {} {
546     $self instvar audioDevice_
547     return [$audioDevice_ get_output_gain]
548 }
549 
550 AudioAgent public is_active {} {
551     $self instvar audioDevice_
552     return [$audioDevice_ is_active]
553 }
554 
555 AudioAgent public clear_active {} {
556     $self instvar audioDevice_
557     $audioDevice_ clear_active
558 }
559 
560 AudioAgent public select_format { fmt BlksPerPkt } {
561     $self instvar audioDevice_
562     $audioDevice_ select_format $fmt $BlksPerPkt
563 }
564 
565 AudioAgent public set_silence_thresh thresh {
566     $self instvar audioDevice_
567     $audioDevice_ set_silence_thresh $thresh
568 }
569 
570 AudioAgent public release {} {
571     $self instvar audioDevice_
572     $audioDevice_ release
573 }
574 
575 AudioAgent public obtain {} {
576     $self instvar audioDevice_
577     $audioDevice_ obtain
578 }
579 
580 AudioAgent instproc unix_time {} {
581     $self instvar audioDevice_
582     set controller [$audioDevice_ get_controller]
583     if { $controller == 0 } {
584         return 0
585     }
586     return [$controller unix_time]
587 }
588 
589 AudioAgent instproc ntp_time {} {
590     $self instvar audioDevice_
591     set controller [$audioDevice_ get_controller]
592     if { $controller == 0 } {
593         return 0
594     }
595     return [$controller ntp_time]
596 }
597 
598 
599 
600 #
601 # The AudioStream class, basically it is the sound card's abstraction
602 #
603 
604 Class AudioStream -configuration {
605     inputGain 32
606     outputGain 180
607     maxPlayout 6
608     mikeAGCLevel 0
609     speakerAGCLevel 0
610     echoSuppressTime 400
611 }
612 
613 #------------------------------------------------------------------
614 # Method: 
615 #   AudioStream init
616 # Description:
617 #
618 # sets up the basic variables are then tries to find
619 # and open the sound card, then give it default settings.
620 #
621 AudioStream public init { srcid session sampRate use16bitPCM useStereo {deviceName {}} } {
622     $self instvar bufferPool_ session_ silenceThresh_ audioTest_ sampleRate_ use16bitPCM_ useStereo_
623 
624     set silenceThresh_ 20
625     set audioTest_ none
626     set session_ $session
627     set sampleRate_ $sampRate
628     set use16bitPCM_ $use16bitPCM
629     set useStereo_ $useStereo
630 
631     $self compute_controller_defaults
632     #
633     # FIXME current behavior is that agent always opens the underlying
634     # device.  The actual physical device may not be present or
635     # might not open, but in any event, the under C++ device object
636     # is created and initialized.
637     #
638 
639     # FIXME - shouldn't session and AudioAgent share the same buffer pool?
640     if ![info exists bufferPool_] {
641         set bufferPool_ [new BufferPool/RTP]
642         $bufferPool_ srcid $srcid
643     }
644 
645     # try to open the device
646     set devList [$self device_list]
647     if { $devList == "" } {
648         #FIXME
649         $self fatal "no suitable audio device found."
650     }
651 
652     if { $deviceName != "" } {
653         set d $deviceName
654     } else {
655         foreach d $devList {
656             if { "$d" != "AF" } {
657                 break
658             } elseif [$self yesno useAF] {
659                 break
660             }
661         }
662     }
663     $self open_device $d
664     # choose a reasonable default
665     $self select_format PCM 2
666     # initially mute input/mike and unmute output/speaker
667     $self set_input_mute 1
668     $self set_output_mute 0
669     $self set_output_gain 5
670     if ![$self have_audio] {
671         $self obtain
672     }
673 }
674 
675 #------------------------------------------------------------------
676 # Method: 
677 #   AudioStream get_controller
678 # Description:
679 #
680 AudioStream public get_controller { } {
681     $self instvar controller_
682     if [info exists controller_] {
683         return $controller_
684     }
685     return 0
686 }
687 
688 
689 #------------------------------------------------------------------
690 # Method: 
691 #   AudioStream destroy
692 # Description:
693 #   Close everything
694 #
695 AudioStream public destroy {} {
696     $self instvar bufferPool_
697     if [info exists bufferPool_] {
698         delete $bufferPool_
699     }
700     $self close_device
701 }
702 
703 #------------------------------------------------------------------
704 # Method: 
705 #   AudioStream compute_controller_defaults
706 # Description:
707 #
708 # Compute the max playout delay used by the (C++) audio controller
709 # from the configuration option "maxPlayout".  Install the
710 # result in the AudioController class variable (so when we
711 # create the object instance, the C++ max_playout_ variable
712 # is appropriately initialized.
713 #
714 AudioStream private compute_controller_defaults {} {
715     $self instvar sampleRate_ use16bitPCM_ useStereo_
716 
717         #FIXME compilation constants
718         set AUDIO_SPS [expr $sampleRate_ * [expr 1 + $useStereo_]]
719         set TALK_LEAD 4
720         set TALK_TAIL 32
721     #AUDIO_FRAMESIZE is the number of samples, not bytes
722         set AUDIO_FRAMESIZE 160
723         set SS_GRANULARITY 1440
724 
725         set v [expr [$self get_option maxPlayout] * $AUDIO_SPS]
726         if [expr $v < ($TALK_LEAD + $TALK_TAIL + 2) * $AUDIO_FRAMESIZE] {
727                 set w [expr (($TALK_LEAD + $TALK_TAIL + 2) * \
728                         $AUDIO_FRAMESIZE + $AUDIO_SPS - 1) / $AUDIO_SPS]
729                 puts stderr "max playout delay $v too short - using $w sec"
730                 set v [expr ($TALK_LEAD + $TALK_TAIL + 2) * $AUDIO_FRAMESIZE]
731         }
732         set v [expr ($v + ($SS_GRANULARITY - 1)) / $SS_GRANULARITY]
733         set v [expr $v * $SS_GRANULARITY / $AUDIO_FRAMESIZE]
734         AudioController set max_playout_ $v
735         AudioController set echo_suppress_time_ [expr \
736                 [$self get_option echoSuppressTime] / 20 * $AUDIO_FRAMESIZE]
737 }
738 
739 
740 
741 #------------------------------------------------------------------
742 # Method: 
743 #   AudioStream select_format
744 # Description:
745 #
746 # Set the audio format used by the underlying audio compression
747 # algorithm.  Note that this only affects outgoing audio.
748 # <i>fmt</i> is a string that represents the coding
749 # scheme and may be any of the values returned by
750 # the get_compression_formats method. (FIXME need to implement)
751 # <p>
752 # <i>blksPerPkt</i> specifies the number of audio blocks
753 # to include in each packet.  Typically 2 to 4 audio blocks are
754 # included in each packet.  Making this number larger decreases
755 # the per-packet network and system overhead, but increases
756 # latency since the audio sub-system has to wait longer before
757 # sending a packet.  Likewise, making it smaller increases interactive
758 # performance at the cost of performance overhead.
759 #
760 AudioStream public select_format { fmt blksPerPkt } {
761         $self instvar encoder_ bufferPool_ session_ \
762                         controller_ blksPerPkt_
763         if [info exists encoder_] {
764                 delete $encoder_
765         }
766         set encoder_ [new Module/AudioEncoder/$fmt]
767         set blksPerPkt_ $blksPerPkt
768         if [info exists controller_] {
769                 $controller_ encoder $encoder_
770                 $controller_ blocks-per-packet $blksPerPkt
771         }
772         if { "$encoder_" == "" } {
773                 return -1
774         }
775         $encoder_ target $session_
776         $encoder_ buffer-pool $bufferPool_
777 
778         return 0
779 }
780 
781 
782 #------------------------------------------------------------------
783 # Method: 
784 #   AudioStream device_list
785 # Description:
786 #
787 # Return a list of OTcl class that correspond to devices
788 # that are supported in this version of the mash platform.
789 #
790 AudioStream public device_list {} {
791     if ![TclObject is-class Audio] {
792         return ""
793     }
794     set temp [Audio info subclass]
795     set ix [lsearch -exact $temp Audio/RealAudioVirtualDevice]
796     if {$ix >= 0} {
797         return [lreplace $temp $ix $ix]
798     } else {
799         return $temp
800     }
801 }
802 
803 #------------------------------------------------------------------
804 # Method: 
805 #   AudioStream bind_transducer
806 # Description:
807 #
808 # Arrange for the audio agent to activate the transducer object named
809 # by <i>o</i> whenenter there is input or output, according to
810 # <i>which</i> (either "input" or "output").
811 # 
812 AudioStream public bind_transducer { which o } {
813     $self instvar meter_ controller_
814     set meter_($which) $o
815     if [info exists controller_] {
816         $controller_ $which-meter $o
817     }
818 }
819 
820 
821 #
822 # Create an audio device to manage the underlying hardware codec
823 # and deallocate state that depends on the old device (if it
824 # was open).  <i>dev</i> is the OTcl class name of the Audio
825 # device that is created.
826 #
827 AudioStream private open_device dev {
828         $self instvar audio_ meter_ controller_ silenceThresh_ sampleRate_ use16bitPCM_ useStereo_
829         if [info exists audio_] {
830                 delete $audio_
831                 unset audio_
832         }
833         if [info exists controller_] {
834                 delete $controller_
835                 unset controller_
836         }
837         set audio_ [new $dev]
838         if { $dev == "Audio/OSS" } {
839             $audio_ set_sample_rate $sampleRate_
840             if { $use16bitPCM_ == 1 } {
841                 $audio_ useRaw16PCM
842             }
843             if { $useStereo_ == 1 } {
844                 puts "setting card to do stereo sound"
845                 $audio_ useStereo
846             }
847         }
848         $self instvar gain_
849         #FIXME
850         set names [$self get_input_ports]
851         foreach port $names {
852                 if {[$self get_option $port\Gain]!={}} {
853                         set gain_($port) [$self get_option $port\Gain]
854                 } else {
855                         set gain_($port) [$self get_option inputGain]
856                 }
857         }
858         set names [$self get_output_ports]
859         foreach port $names {
860                 if {[$self get_option $port\Gain]!={}} {
861                         set gain_($port) [$self get_option $port\Gain]
862                 } else {
863                         set gain_($port) [$self get_option outputGain]
864                 }
865                 $self set_speakerphone $port [$self get_option $port\Mode]
866         }
867 
868         #FIXME
869         $self instvar port_
870         set port_(input) ""
871         set port_(output) ""
872 }
873 
874 
875 #------------------------------------------------------------------
876 # Method: 
877 #   AudioStream install_controller
878 # Description:
879 #
880 # Create, install, and initialize the AudioController object.  This
881 # operation must be deferred until after the audio device has been
882 # ``obtained'' and opened because we need to determine whether the
883 # underlying hardware operates in full- or half-duplex mode and create
884 # a controller object that is compatible with this mode.
885 #
886 AudioStream private install_controller {} {
887         $self instvar audio_ encoder_ blksPerPkt_ silenceThresh_ meter_ \
888                 controller_
889         if [$audio_ set duplex_] {
890                 set duplex FullDuplex
891         } else {
892                 set duplex HalfDuplex
893                 #
894                 # If we're half duplex, try obtaining the device
895                 # (i.e., actually opening it) to see if it's full
896                 # duplex.  Or, if an user option says to force
897                 # the use of full duplex then do that.
898                 #
899                 if ![$self have_audio] {
900                         $self obtain 0
901                 }
902                 if { [$self yesno forceFullDuplex] || [$audio_ set duplex_] } {
903                         set duplex FullDuplex
904                 }
905         }
906         set controller_ [new AudioController/$duplex]
907         $controller_ audio $audio_
908         if [info exists meter_(input)] {
909                 $controller_ input-meter $meter_(input)
910         }
911         if [info exists meter_(output)] {
912                 $controller_ output-meter $meter_(output)
913         }
914         $controller_ silence-thresh $silenceThresh_
915         if [info exists encoder_] {
916                 $controller_ encoder $encoder_
917                 $controller_ blocks-per-packet $blksPerPkt_
918         }
919         $controller_ agc-input [$self get_option mikeAGCLevel]
920         $controller_ agc-output [$self get_option speakerAGCLevel]
921         $controller_ silence-thresh [$self get_option silenceThresh]
922 }
923 
924 #------------------------------------------------------------------
925 # Method: 
926 #   AudioStream obtain
927 # Description:
928 #
929 # Attempt to open and intialize the underlying audio hardware.
930 # The creation of an AudioAgent does not immediately mean that
931 # the underlying audio hardware is opened and initialized because
932 # multiple applications might want to simultaneously access the
933 # device but many such devices cannot be shared because of
934 # the operating system's contraints on the API.  Thus, we have
935 # explicit methods for obtaining and releasing the underlying
936 # hardware resources.  After <i>obtain</i> is called, the <i>haveaudio</i>
937 # method can be queried to see if the device was successfully
938 # opened.  Once obtained, the device can be released with the
939 # AudioAgent::release.
940 #
941 AudioStream public obtain { {should_install_controller 1} } {
942         $self instvar audio_ controller_
943         $audio_ obtain
944 
945         # check if a controller exists; make sure that the controller
946         # is of the correct "duplex" mode
947         if [info exists controller_] {
948                 set cl [$controller_ info class]
949                 if [string match *FullDuplex* $cl] {
950                         set duplex 1
951                 } else {
952                         set duplex 0
953                 }
954 
955                 if { [$audio_ set duplex_] != $duplex } {
956                         delete $controller_
957                         unset controller_
958                 }
959         }
960 
961         if { ![info exists controller_] && $should_install_controller } {
962                 $self install_controller
963         }
964 }
965 
966 #------------------------------------------------------------------
967 # Method: 
968 #   AudioStream release
969 # Description:
970 #
971 # Release the underlying audio hardware without deallocating
972 # the AudioAgent object.  This allow
973 # The creation of an AudioAgent does not immediately mean that
974 # the underlying audio hardware is opened and initialized because
975 # multiple applications might want to simultaneously access the
976 # device but many such devices cannot be shared because of
977 # the operating system's contraints on the API.  Thus, we have
978 # explicit methods for obtaining and releasing the underlying
979 # hardware resources.  After <i>obtain</i> is called, the <i>haveaudio</i>
980 # method can be queried to see if the device was successfully
981 # opened.  Once obtained, the device can be released with the
982 # AudioAgent::release.
983 #
984 AudioStream public release {} {
985         # audio_ represents the audio device
986         [$self set audio_] release
987 }
988 
989 #------------------------------------------------------------------
990 # Method: 
991 #   AudioStream set_silence_thresh
992 # Description:
993 #
994 # Set the threshold for the silence suppression algorithm
995 # used by the AudioController to <i>thresh</i>.
996 # FIXME: need to define units.  Should be probably be dB.
997 #
998 AudioStream public set_silence_thresh thresh {
999         $self instvar silenceThresh_ controller_
1000         set silenceThresh_ $thresh
1001         if [info exists controller_] {
1002                 $controller_ silence-thresh $silenceThresh_
1003         }
1004 }
1005 
1006 #------------------------------------------------------------------
1007 # Method: 
1008 #   AudioStream have_audio
1009 # Description:
1010 # Return true iff the AudioAgent has opened and currently ``owns''
1011 # the underlying hardware audio device.
1012 #
1013 AudioStream public have_audio {} {
1014         $self instvar audio_
1015         if [info exists audio_] {
1016                 return [$audio_ have]
1017         }
1018         return 0
1019 }
1020 
1021 #------------------------------------------------------------------
1022 # Method: 
1023 #   AudioStream set_input_mute
1024 # Description:
1025 #
1026 # Set the mute attribute on the input port of the underlying
1027 # audio device (i.e., the mike, linein, etc) as indicated
1028 # by <i>val</i>.  When muted, audio samples from the input
1029 # device are ignored and no network traffic is generated.
1030 #
1031 AudioStream public set_input_mute val {
1032         $self instvar audio_
1033         $audio_ set_input_mute $val
1034 }
1035 
1036 #------------------------------------------------------------------
1037 # Method: 
1038 #   AudioStream set_output_mute
1039 # Description:
1040 #
1041 # Set the mute attribute on the output port of the underlying
1042 # audio device (i.e., the speaker, headphones, etc) as indicated
1043 # by <i>val</i>.  When muted, audio packets from the network
1044 # are processed but not output to the audio hardware.
1045 #
1046 AudioStream public set_output_mute val {
1047         $self instvar audio_
1048         $audio_ set_output_mute $val
1049 }
1050 
1051 #------------------------------------------------------------------
1052 # Method: 
1053 #   AudioStream get_input_ports
1054 # Description:
1055 #
1056 # Return the list of available input ports supported
1057 # by the underlying audio hardware.  Each name in the
1058 # list is unique so names can be used as keys into a table.
1059 # The list consists of a sequence of names,
1060 # where the name is the nick name of the port
1061 # (e.g., "mike").
1062 #
1063 AudioStream public get_input_ports {} {
1064         $self instvar audio_
1065         return [string tolower [$audio_ get_input_ports]]
1066 }
1067 
1068 #------------------------------------------------------------------
1069 # Method: 
1070 #   AudioStream get_output_ports
1071 # Description:
1072 #
1073 # Return the list of available output ports supported
1074 # by the underlying audio hardware.  Each name in the
1075 # list is unique so names can be used as keys into a table.
1076 # The list consists of a sequence of names,
1077 # where the name is the nick name of the port
1078 # (e.g., "speaker").
1079 #
1080 AudioStream public get_output_ports {} {
1081         $self instvar audio_
1082         return [string tolower [$audio_ get_output_ports]]
1083 }
1084 
1085 #------------------------------------------------------------------
1086 # Method: