root/rat/trunk/ui_audiotool.tcl @ 2873

Revision 2873, 93.3 KB (checked in by ucacoxh, 14 years ago)

- More ui consistency updates. agc, playout limits, etc

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1catch {
2#
3# Copyright (c) 1995-99 University College London
4# All rights reserved.
5#
6# $Revision$
7#
8# Full terms and conditions of the copyright appear below.
9#
10
11#wm withdraw .
12
13if {[string compare [info commands registry] "registry"] == 0} {
14        set win32 1
15} else {
16        set win32 0
17        option add *Menu*selectColor            forestgreen
18        option add *Radiobutton*selectColor     forestgreen
19        option add *Checkbutton*selectColor     forestgreen
20        option add *Entry.background            gray70
21}
22
23set statsfont     [font actual {helvetica 10}]
24set titlefont     [font actual {helvetica 10}]
25set infofont      [font actual {helvetica 10}]
26set smallfont     [font actual {helvetica  8}]
27set verysmallfont [font actual {helvetica  8}]
28
29set speaker_highlight white
30
31option add *Entry.relief       sunken
32option add *borderWidth        1
33option add *highlightThickness 0
34#option add *Button*padX        4         
35#option add *Button*padY        0         
36option add *font               $infofont
37option add *Menu*tearOff       0
38
39set V(class) "Mbone Applications"
40set V(app)   "rat"
41
42set iht                 16
43set iwd                 250
44set cancel_info_timer   0
45set num_ssrc            0
46set fw                  .l.t.list.f
47set input_ports         [list]
48set output_ports        [list]
49
50proc init_source {ssrc} {
51        global CNAME NAME EMAIL LOC PHONE TOOL NOTE SSRC num_ssrc
52        global CODEC DURATION PCKTS_RECV PCKTS_LOST PCKTS_MISO PCKTS_DUP JITTER \
53                LOSS_TO_ME LOSS_FROM_ME INDEX JIT_TOGED BUFFER_SIZE PLAYOUT_DELAY \
54                GAIN MUTE
55
56        # This is a debugging test -- old versions of the mbus used the
57        # cname to identify participants, whilst the newer version uses
58        # the ssrc.  This check detects if old style commands are being
59        # used and raises an error if so.
60        if [regexp {.*@[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+} "$ssrc"] {
61                error "ssrc $ssrc invalid"
62        }
63
64        if {[array names INDEX $ssrc] != [list $ssrc]} {
65                # This is a source we've not seen before...
66                set         CNAME($ssrc) ""
67                set          NAME($ssrc) $ssrc
68                set         EMAIL($ssrc) ""
69                set         PHONE($ssrc) ""
70                set           LOC($ssrc) ""
71                set          TOOL($ssrc) ""
72                set          NOTE($ssrc) ""
73                set         CODEC($ssrc) unknown
74                set          GAIN($ssrc) 1.0
75                set          MUTE($ssrc) 0
76                set      DURATION($ssrc) ""
77                set   BUFFER_SIZE($ssrc) 0
78                set PLAYOUT_DELAY($ssrc) 0
79                set    PCKTS_RECV($ssrc) 0
80                set    PCKTS_LOST($ssrc) 0
81                set    PCKTS_MISO($ssrc) 0
82                set     PCKTS_DUP($ssrc) 0
83                set        JITTER($ssrc) 0
84                set     JIT_TOGED($ssrc) 0
85                set    LOSS_TO_ME($ssrc) 0
86                set  LOSS_FROM_ME($ssrc) 0
87                set  HEARD_LOSS_TO_ME($ssrc) 0
88                set  HEARD_LOSS_FROM_ME($ssrc) 0
89                set         INDEX($ssrc) $num_ssrc
90                set          SSRC($ssrc) $ssrc
91                incr num_ssrc
92                chart_add   $ssrc
93        }
94}
95
96proc window_plist {ssrc} {
97        global fw
98        regsub -all {@|\.} $ssrc {-} foo
99        return $fw.source-$foo
100}
101
102proc window_stats {ssrc} {
103        regsub -all {[\. ]} $ssrc {-} foo
104        return .stats$foo
105}
106
107# Commands to send message over the conference bus...
108proc output_mute {state} {
109    mbus_send "R" "audio.output.mute" "$state"
110    bargraphState .r.c.vol.gra.b1 [expr ! $state]
111}
112
113proc input_mute {state} {
114    mbus_send "R" "audio.input.mute" "$state"
115    bargraphState .r.c.gain.gra.b2 [expr ! $state]
116}
117
118proc set_vol {new_vol} {
119    global volume
120    set volume $new_vol
121    mbus_send "R" "audio.output.gain" $volume
122}
123
124proc set_gain {new_gain} {
125    global gain
126    set gain $new_gain
127    mbus_send "R" "audio.input.gain" $gain
128}
129
130proc toggle_input_port {} {
131    global input_port input_ports
132
133    set len [llength $input_ports]
134# lsearch returns -1 if not found, index otherwise
135    set idx [lsearch -exact $input_ports $input_port]
136
137    if {$idx != -1} {
138        incr idx
139        set idx [expr $idx % $len]
140        set port [lindex $input_ports $idx]
141        mbus_send "R" "audio.input.port" [mbus_encode_str $port]
142    }
143}
144
145proc toggle_output_port {} {
146    global output_port output_ports
147
148    set len [llength $output_ports]
149# lsearch returns -1 if not found, index otherwise
150    set idx [lsearch -exact $output_ports $output_port]
151   
152    if {$idx != -1} {
153        incr idx
154        set idx [expr $idx % $len]
155        set port [lindex $output_ports $idx]
156        mbus_send "R" "audio.output.port" [mbus_encode_str $port]
157    }
158}
159
160proc mbus_heartbeat {} {
161}
162
163#############################################################################################################
164# Reception of Mbus messages...
165
166proc mbus_recv {cmnd args} {
167        # This is not the most efficient way of doing this, since we could call mbus_recv_...
168        # directly from the C code. It does, however, make it explicit which Mbus commands we
169        # understand.
170        switch $cmnd {
171                mbus.waiting                    {eval mbus_recv_mbus.waiting $args}
172                mbus.go                         {eval mbus_recv_mbus.go $args}
173                mbus.hello                      {eval mbus_recv_mbus.hello $args}
174                mbus.quit                       {eval mbus_recv_mbus.quit $args}
175                tool.rat.load.setting           {eval mbus_recv_tool.rat.load.setting $args}
176                tool.rat.sampling.supported     {eval mbus_recv_tool.rat.sampling.supported $args}
177                tool.rat.converter              {eval mbus_recv_tool.rat.converter $args}
178                tool.rat.converters.flush       {eval mbus_recv_tool.rat.converters.flush $args}
179                tool.rat.converters.add         {eval mbus_recv_tool.rat.converters.add $args}
180                tool.rat.repair                 {eval mbus_recv_tool.rat.repair $args}
181                tool.rat.repairs.flush          {eval mbus_recv_tool.rat.repairs.flush $args}
182                tool.rat.repairs.add            {eval mbus_recv_tool.rat.repairs.add $args}
183                tool.rat.powermeter             {eval mbus_recv_tool.rat.powermeter $args}
184                tool.rat.agc                    {eval mbus_recv_tool.rat.agc $args}
185                tool.rat.sync                   {eval mbus_recv_tool.rat.sync $args}
186                tool.rat.format.in              {eval mbus_recv_tool.rat.format.in $args}
187                tool.rat.codec                  {eval mbus_recv_tool.rat.codec $args}
188                tool.rat.codec.details          {eval mbus_recv_tool.rat.codec.details $args}
189                tool.rat.rate                   {eval mbus_recv_tool.rat.rate $args}
190                tool.rat.lecture.mode           {eval mbus_recv_tool.rat.lecture.mode $args}
191                tool.rat.disable.audio.ctls     {eval mbus_recv_tool.rat.disable.audio.ctls $args}
192                tool.rat.enable.audio.ctls      {eval mbus_recv_tool.rat.enable.audio.ctls $args}
193                tool.rat.audio.buffered         {eval mbus_recv_tool.rat.audio.buffered $args}
194                tool.rat.audio.delay            {eval mbus_recv_tool.rat.audio.delay $args}
195                tool.rat.3d.enabled             {eval mbus_recv_tool.rat.3d.enabled $args}
196                tool.rat.3d.azimuth.min         {eval mbus_recv_tool.rat.3d.azimuth.min $args}
197                tool.rat.3d.azimuth.max         {eval mbus_recv_tool.rat.3d.azimuth.max $args}
198                tool.rat.3d.filter.types        {eval mbus_recv_tool.rat.3d.filter.types   $args}
199                tool.rat.3d.filter.lengths      {eval mbus_recv_tool.rat.3d.filter.lengths $args}
200                tool.rat.3d.user.settings       {eval mbus_recv_tool.rat.3d.user.settings  $args}
201                tool.rat.playout.limit          {eval mbus_recv_tool.rat.playout.limit $args}
202                tool.rat.playout.min            {eval mbus_recv_tool.rat.playout.min   $args}
203                tool.rat.playout.max            {eval mbus_recv_tool.rat.playout.max   $args}
204                audio.suppress.silence          {eval mbus_recv_audio.suppress.silence $args}
205                audio.channel.coding            {eval mbus_recv_audio.channel.coding $args}
206                audio.channel.repair            {eval mbus_recv_audio.channel.repair $args}
207                audio.devices.flush             {eval mbus_recv_audio_devices_flush $args}
208                audio.devices.add               {eval mbus_recv_audio_devices_add $args}
209                audio.device                    {eval mbus_recv_audio_device $args}
210                audio.input.gain                {eval mbus_recv_audio.input.gain $args}
211                audio.input.port                {eval mbus_recv_audio.input.port $args}
212                audio.input.ports.add           {eval mbus_recv_audio.input.ports.add $args}
213                audio.input.ports.flush         {eval mbus_recv_audio.input.ports.flush $args}
214                audio.input.mute                {eval mbus_recv_audio.input.mute $args}
215                audio.input.powermeter          {eval mbus_recv_audio.input.powermeter $args}
216                audio.output.gain               {eval mbus_recv_audio.output.gain $args}
217                audio.output.port               {eval mbus_recv_audio.output.port $args}
218                audio.output.ports.add          {eval mbus_recv_audio.output.ports.add $args}
219                audio.output.ports.flush        {eval mbus_recv_audio.output.ports.flush $args}
220                audio.output.mute               {eval mbus_recv_audio.output.mute $args}
221                audio.output.powermeter         {eval mbus_recv_audio.output.powermeter $args}
222                audio.file.play.ready           {eval mbus_recv_audio.file.play.ready   $args}
223                audio.file.play.alive           {eval mbus_recv_audio.file.play.alive $args}
224                audio.file.record.ready         {eval mbus_recv_audio.file.record.ready $args}
225                audio.file.record.alive         {eval mbus_recv_audio.file.record.alive $args}
226                session.title                   {eval mbus_recv_session.title $args}
227                rtp.addr                        {eval mbus_recv_rtp.addr $args}
228                rtp.ssrc                        {eval mbus_recv_rtp.ssrc $args}
229                rtp.source.exists               {eval mbus_recv_rtp.source.exists $args}
230                rtp.source.remove               {eval mbus_recv_rtp.source.remove $args}
231                rtp.source.cname                {eval mbus_recv_rtp.source.cname $args}
232                rtp.source.name                 {eval mbus_recv_rtp.source.name $args}
233                rtp.source.email                {eval mbus_recv_rtp.source.email $args}
234                rtp.source.phone                {eval mbus_recv_rtp.source.phone $args}
235                rtp.source.loc                  {eval mbus_recv_rtp.source.loc $args}
236                rtp.source.tool                 {eval mbus_recv_rtp.source.tool $args}
237                rtp.source.note                 {eval mbus_recv_rtp.source.note $args}
238                rtp.source.codec                {eval mbus_recv_rtp.source.codec $args}
239                rtp.source.packet.duration      {eval mbus_recv_rtp.source.packet.duration $args}
240                rtp.source.packet.loss          {eval mbus_recv_rtp.source.packet.loss $args}
241                rtp.source.reception            {eval mbus_recv_rtp.source.reception $args}
242                rtp.source.active               {eval mbus_recv_rtp.source.active $args}
243                rtp.source.inactive             {eval mbus_recv_rtp.source.inactive $args}
244                rtp.source.mute                 {eval mbus_recv_rtp.source.mute $args}
245                rtp.source.gain                 {eval mbus_recv_rtp.source.gain $args}
246                security.encryption.key         {eval mbus_recv_security.encryption.key $args}
247                default                         {puts "Unknown mbus command $cmnd"}
248        }
249}
250
251proc mbus_recv_mbus.waiting {condition} {
252        if {$condition == "rat.ui.init"} {
253                mbus_send "U" "mbus.go" [mbus_encode_str rat.ui.init]
254        }
255}
256
257proc mbus_recv_mbus.go {condition} {
258}
259
260proc mbus_recv_mbus.hello {} {
261        # Ignore...
262}
263
264proc mbus_recv_tool.rat.load.setting {sname} {
265    global attr
266# Note when settings get loaded the get set in attr and not updated.  So we
267# use this as a cache for desired values.  This is necessary as when the
268# settings first get loaded we have null audio device and can't set
269# anything on it meaningfully - i.e. it only has 1 port for input and 1 for output.
270#
271    switch $sname {
272        audio.input.mute  { mbus_send "R" $sname $attr(audioInputMute) }
273        audio.input.gain  { mbus_send "R" $sname $attr(audioInputGain) }
274        audio.input.port  { mbus_send "R" $sname [mbus_encode_str $attr(audioInputPort)] }
275        audio.output.mute { mbus_send "R" $sname $attr(audioOutputMute) }
276        audio.output.gain { mbus_send "R" $sname $attr(audioOutputGain) }
277        audio.output.port { mbus_send "R" $sname [mbus_encode_str $attr(audioOutputPort)] }
278        default           { puts "setting requested has no handler"}
279    }
280}
281
282proc change_freq {new_freq} {
283    global freq
284
285    if {$freq != $new_freq} {
286        set freq $new_freq
287        update_channels_displayed
288        update_codecs_displayed
289        reset_encodings
290    }
291}
292
293proc change_channels {new_channels} {
294    global ichannels
295    if {$ichannels != $new_channels} {
296        set ichannels $new_channels
297        update_codecs_displayed
298        reset_encodings
299    }
300}
301
302proc update_channels_displayed {} {
303    global freq channel_support
304
305    set m1 .prefs.pane.audio.dd.sampling.ch_in.mb.m
306    $m1 delete 0 last
307    set s [lsearch -glob $channel_support *$freq*]
308   
309    foreach i [lrange [split [lindex $channel_support $s] ","] 1 2] {
310         $m1 add command -label "$i" -command "change_channels $i"
311    }
312}
313
314proc mbus_recv_tool.rat.sampling.supported {arg} {
315    global freq channel_support
316
317    #clear away old state of channel support
318    if [info exists channel_support] {
319        unset channel_support
320    }
321
322    set freqs [list]
323    set channel_support [list]
324
325    .prefs.pane.audio.dd.sampling.freq.mb.m delete 0 last
326
327    set mode [split $arg]
328    foreach m $mode {
329        lappend channel_support $m
330        set support [split $m ","]
331        set f [lindex $support 0]
332        lappend freqs $f
333        .prefs.pane.audio.dd.sampling.freq.mb.m add command -label $f -command "change_freq $f"
334    }
335    set freq [lindex $freqs 0]
336    update_channels_displayed
337}
338
339# CODEC HANDLING ##############################################################
340
341set codecs {}
342set prencs  {}
343set secencs {}
344set layerencs {}
345
346proc codec_get_name {nickname freq channels} {
347    global codecs codec_nick_name codec_rate codec_channels
348
349    foreach {c} $codecs {
350        if {$codec_nick_name($c)    == $nickname && \
351                $codec_rate($c)     == $freq && \
352                $codec_channels($c) == $channels} {
353            return $c
354        }
355    }
356}
357
358proc codecs_loosely_matching {freq channels} {
359    global codecs codec_nick_name codec_channels codec_rate codec_pt codec_state_size codec_data_size codec_block_size codec_desc
360   
361    set x {}
362
363    foreach {c} $codecs {
364        if {$codec_channels($c) == $channels && \
365        $codec_rate($c) == $freq && \
366        $codec_pt($c) != "-" } {
367            lappend x $c
368        }
369    }
370
371    return $x
372}
373
374proc codecs_matching {freq channels blocksize} {
375    global codec_block_size
376    set codecs [codecs_loosely_matching $freq $channels]
377
378    set x {}
379
380    foreach {c} $codecs {
381        if {$codec_block_size($c) == $blocksize} {
382            lappend x $c
383        }
384    }
385    return $x
386}
387
388proc mbus_recv_tool.rat.codec.details {args} {
389    catch {
390        global codecs codec_nick_name codec_channels codec_rate codec_pt codec_state_size codec_data_size codec_block_size codec_desc codec_caps codec_layers
391       
392        set name [lindex $args 1]
393        if {[lsearch $codecs $name] == -1} {
394            lappend codecs $name
395        }
396        set codec_pt($name)         [lindex $args 0]
397        set codec_nick_name($name)  [lindex $args 2]
398        set codec_channels($name)   [lindex $args 3]
399        set codec_rate($name)       [lindex $args 4]
400        set codec_block_size($name) [lindex $args 5]
401        set codec_state_size($name) [lindex $args 6]
402        set codec_data_size($name)  [lindex $args 7]
403        set codec_desc($name)       [lindex $args 8]
404        set codec_caps($name)       [lindex $args 9]
405        set codec_layers($name)         [lindex $args 10]
406        set stackup ""
407    } details_error
408
409    if { $details_error != "" } {
410        puts "Error: $details_error"
411        destroy .
412        exit -1
413    }
414}
415
416proc update_primary_list {arg} {
417    # We now have a list of codecs which this RAT supports...
418    global prenc prencs
419
420    .prefs.pane.transmission.dd.pri.m.menu delete 0 last
421    set prencs {}
422
423    set codecs [split $arg]
424    foreach c $codecs {
425        .prefs.pane.transmission.dd.pri.m.menu    add command -label $c -command "set prenc $c; update_codecs_displayed"
426        lappend prencs $c
427    }
428
429    if {[lsearch $codecs $prenc] == -1} {
430        #primary is not on list
431        set prenc [lindex $codecs 0]
432    }
433}
434
435proc update_redundancy_list {arg} {
436    global secenc secencs
437
438    .prefs.pane.transmission.cc.red.fc.m.menu delete 0 last
439    set secencs {}
440
441    set codecs [split $arg]
442    foreach c $codecs {
443        .prefs.pane.transmission.cc.red.fc.m.menu add command -label $c -command "set secenc \"$c\""
444        lappend secencs $c
445    }
446    if {[lsearch $codecs $secenc] == -1} {
447        #primary is not on list
448        set secenc [lindex $codecs 0]
449    }
450}
451
452proc update_layer_list {arg} {
453    global layerenc layerencs
454
455    .prefs.pane.transmission.cc.layer.fc.m.menu delete 0 last
456    set layerencs {}
457
458    for {set i 1} {$i <= $arg} {incr i} {
459        .prefs.pane.transmission.cc.layer.fc.m.menu add command -label $i -command "set layerenc \"$i\""
460        lappend layerencs $i
461        }
462        if {$layerenc > $arg} {
463        #new codec doesn't support as many layers
464        set layerenc $arg
465        }
466}
467
468proc reset_encodings {} {
469    global prenc prencs secenc secencs layerenc layerencs
470    set prenc  [lindex $prencs 0]
471    set secenc [lindex $secencs 0]
472        set layerenc [lindex $layerencs 0]
473}
474
475proc update_codecs_displayed { } {
476    global freq ichannels codec_nick_name prenc codec_block_size codec_caps codec_layers
477
478    if {[string match $ichannels Mono]} {
479        set sample_channels 1
480    } else {
481        set sample_channels 2
482    }
483
484    set sample_rate [string trimright $freq -kHz]
485    set sample_rate [expr $sample_rate * 1000]
486
487    set long_names [codecs_loosely_matching $sample_rate $sample_channels]
488
489    set friendly_names {}
490    foreach {n} $long_names {
491        # only interested in codecs that can (e)ncode
492        if {[string first $codec_caps($n) ncode]} {
493            lappend friendly_names $codec_nick_name($n)
494        }
495    }
496
497    update_primary_list $friendly_names
498
499    set long_name [codec_get_name $prenc $sample_rate $sample_channels]
500    set long_names [codecs_matching $sample_rate $sample_channels $codec_block_size($long_name)]
501
502    set friendly_names {}
503    set found 0
504    foreach {n} $long_names {
505        # Only display codecs of same or lower order as primary in primary list
506        if {$codec_nick_name($n) == $prenc} {
507            set found 1
508        }
509        if {$found} {
510            if {[string first $codec_caps($n) ncode]} {
511                lappend friendly_names $codec_nick_name($n)
512            }
513        }
514    }
515   
516    update_redundancy_list $friendly_names
517
518        # Assume that all codecs of one type support the same number of layers
519        foreach {n} $long_names {
520        if {$codec_nick_name($n) == $prenc} {
521        break
522        }
523        }
524        update_layer_list $codec_layers($n)
525}
526
527proc change_sampling { } {
528    update_channels_displayed
529    update_codecs_displayed
530}
531
532###############################################################################
533
534proc mbus_recv_tool.rat.converters.flush {} {
535    .prefs.pane.reception.r.ms.menu delete 0 last
536}
537
538proc mbus_recv_tool.rat.converters.add {arg} {
539    global convert_var
540    .prefs.pane.reception.r.ms.menu add command -label "$arg" -command "set convert_var \"$arg\""
541}
542
543proc mbus_recv_tool.rat.converter {arg} {
544    global convert_var
545    set convert_var $arg
546}
547
548proc mbus_recv_tool.rat.repairs.flush {} {
549    .prefs.pane.reception.r.m.menu delete 0 last
550}
551
552
553proc mbus_recv_tool.rat.repairs.add {arg} {
554    global repair_var
555    .prefs.pane.reception.r.m.menu add command -label "$arg" -command "set repair_var \"$arg\""
556}
557
558proc mbus_recv_tool.rat.repair {arg} {
559    global repair_var
560    set repair_var $arg
561}
562
563proc mbus_recv_audio_devices_flush {} {
564    .prefs.pane.audio.dd.device.mdev.menu delete 0 last
565}
566
567proc mbus_recv_audio_devices_add {arg} {
568    global audio_device
569
570    .prefs.pane.audio.dd.device.mdev.menu add command -label "$arg" -command "set audio_device \"$arg\""
571
572    set len [string length "$arg"]
573    set curr [.prefs.pane.audio.dd.device.mdev cget -width]
574
575    if {$len > $curr} {
576        .prefs.pane.audio.dd.device.mdev configure -width $len
577    }
578}
579
580proc mbus_recv_audio_device {arg} {
581        global audio_device
582        set audio_device $arg
583}
584
585proc mbus_recv_tool.rat.powermeter {arg} {
586        global meter_var
587        set meter_var $arg
588}
589
590proc mbus_recv_tool.rat.agc {arg} {
591  global agc_var
592  set agc_var $arg
593}
594
595proc mbus_recv_tool.rat.sync {arg} {
596  global sync_var
597  set sync_var $arg
598}
599
600proc mbus_recv_security.encryption.key {new_key} {
601        global key_var key
602        set key_var 1
603        set key     $new_key
604}
605
606proc mbus_recv_tool.rat.format.in {arg} {
607    global freq ichannels
608#expect arg to be <sample_type>,<sample rate>,<mono/stereo>
609    set e [split $arg ","]
610   
611    set freq      [lindex $e 1]
612    set ichannels [lindex $e 2]
613}
614
615proc mbus_recv_tool.rat.codec {arg} {
616  global prenc
617  set prenc $arg
618}
619
620proc mbus_recv_tool.rat.rate {arg} {
621    global upp
622    set upp $arg
623}
624
625proc mbus_recv_audio.channel.coding {args} {
626    global channel_var secenc red_off int_units int_gap prenc layerenc
627
628    set channel_var [lindex $args 0]
629
630    switch [string tolower $channel_var] {
631        redundancy {
632                set secenc  [lindex $args 1]
633                set red_off [lindex $args 2]
634        }
635        interleaved {
636                set int_units [lindex $args 1]
637                set int_gap   [lindex $args 2]
638        }
639        layering {
640#               should we be playing with primary encoding?
641#               set prenc         [lindex $args 1]
642                set layerenc  [lindex $args 2]
643        }
644    }
645}
646
647proc mbus_recv_audio.channel.repair {arg} {
648  global repair_var
649  set repair_var $arg
650}
651
652proc mbus_recv_audio.input.powermeter {level} {
653        global bargraphTotalHeight
654        bargraphSetHeight .r.c.gain.gra.b2 [expr ($level * $bargraphTotalHeight) / 100]
655}
656
657proc mbus_recv_audio.output.powermeter {level} {
658        global bargraphTotalHeight
659        bargraphSetHeight .r.c.vol.gra.b1  [expr ($level * $bargraphTotalHeight) / 100]
660}
661
662proc mbus_recv_audio.input.gain {new_gain} {
663    global gain
664    set gain $new_gain
665    .r.c.gain.gra.s2 set $gain
666}
667
668proc mbus_recv_audio.input.ports.flush {} {
669    global input_ports
670    set input_ports [list]
671}
672
673proc mbus_recv_audio.input.ports.add {port} {
674    global input_ports
675    lappend input_ports "$port"
676}
677
678proc mbus_recv_audio.input.port {device} {
679    set err ""
680    catch {
681        configure_input_port $device
682        set tmp ""
683    } err
684        if {$err != ""} {
685            puts "error: $err"
686        }
687}
688
689proc mbus_recv_audio.input.mute {val} {
690    global in_mute_var
691    set in_mute_var $val
692    bargraphState .r.c.gain.gra.b2 [expr ! $val]
693}
694
695proc mbus_recv_audio.output.gain {gain} {
696        .r.c.vol.gra.s1 set $gain
697}
698
699proc mbus_recv_audio.output.port {device} {
700        global output_port
701    set err ""
702    catch {
703        configure_output_port $device
704        set a ""
705    } err
706        if {$err != ""} {
707            puts "Output port error: $err"
708        }
709}
710
711proc mbus_recv_audio.output.ports.flush {} {
712    global output_ports
713    set output_ports [list]
714}
715
716proc mbus_recv_audio.output.ports.add {port} {
717    global output_ports
718    lappend output_ports "$port"
719}
720
721proc mbus_recv_audio.output.mute {val} {
722    global out_mute_var
723    set out_mute_var $val
724}
725
726proc mbus_recv_session.title {title} {
727    global session_title
728    set session_title $title
729    wm title . "RAT: $title"
730}
731
732proc mbus_recv_rtp.addr {addr rx_port tx_port ttl} {
733    global session_address group_addr
734    set group_addr $addr
735    set session_address "Address: $addr Port: $rx_port TTL: $ttl"
736}
737
738proc mbus_recv_tool.rat.lecture.mode {mode} {
739        global lecture_var
740        set lecture_var $mode
741}
742
743proc mbus_recv_audio.suppress.silence {mode} {
744        global silence_var
745        set silence_var $mode
746}
747
748proc mbus_recv_rtp.ssrc {ssrc} {
749        global my_ssrc
750
751        set my_ssrc $ssrc
752        init_source $ssrc
753        ssrc_update $ssrc
754}
755
756proc mbus_recv_rtp.source.exists {ssrc} {
757        init_source $ssrc
758        chart_add $ssrc
759        ssrc_update $ssrc
760}
761
762proc mbus_recv_rtp.source.cname {ssrc cname} {
763        global CNAME NAME SSRC
764        init_source $ssrc
765        set CNAME($ssrc) $cname
766        if {[string compare $NAME($ssrc) $SSRC($ssrc)] == 0} {
767                set NAME($ssrc) $cname
768        }
769        chart_label $ssrc $NAME($ssrc)
770        ssrc_update $ssrc
771}
772
773proc mbus_recv_rtp.source.name {ssrc name} {
774        global NAME rtcp_name my_ssrc
775        init_source $ssrc
776        set NAME($ssrc) $name
777        chart_label $ssrc $name
778        ssrc_update $ssrc
779        if {[string compare $ssrc $my_ssrc] == 0} {
780                set rtcp_name $name
781        }
782}
783
784proc mbus_recv_rtp.source.email {ssrc email} {
785        global EMAIL rtcp_email my_ssrc
786        init_source $ssrc
787        set EMAIL($ssrc) $email
788        if {[string compare $ssrc $my_ssrc] == 0} {
789                set rtcp_email $email
790        }
791}
792
793proc mbus_recv_rtp.source.phone {ssrc phone} {
794        global PHONE rtcp_phone my_ssrc
795        init_source $ssrc
796        set PHONE($ssrc) $phone
797        if {[string compare $ssrc $my_ssrc] == 0} {
798                set rtcp_phone $phone
799        }
800}
801
802proc mbus_recv_rtp.source.loc {ssrc loc} {
803        global LOC rtcp_loc my_ssrc
804        init_source $ssrc
805        set LOC($ssrc) $loc
806        if {[string compare $ssrc $my_ssrc] == 0} {
807                set rtcp_loc $loc
808        }
809}
810
811proc mbus_recv_rtp.source.tool {ssrc tool} {
812        global TOOL my_ssrc
813        init_source $ssrc
814        set TOOL($ssrc) $tool
815        if {[string compare $ssrc $my_ssrc] == 0} {
816            global tool_name
817            # tool name looks like RAT x.x.x platform ....
818            # lose the platform stuff
819            set tool_frag [split $tool]
820            set tool_name "UCL [lindex $tool_frag 0] [lindex $tool_frag 1]"
821        }
822}
823
824proc mbus_recv_rtp.source.note {ssrc note} {
825        global NOTE
826        init_source $ssrc
827        set NOTE($ssrc) $note
828}
829
830proc mbus_recv_rtp.source.codec {ssrc codec} {
831        global CODEC
832        init_source $ssrc
833        set CODEC($ssrc) $codec
834}
835
836proc mbus_recv_rtp.source.gain {ssrc gain} {
837        global GAIN
838        init_source $ssrc
839        set GAIN($ssrc) $gain
840}
841
842proc mbus_recv_rtp.source.packet.duration {ssrc packet_duration} {
843        global DURATION
844        init_source $ssrc
845        set DURATION($ssrc) $packet_duration
846}
847
848proc mbus_recv_tool.rat.audio.buffered {ssrc buffered} {
849        global BUFFER_SIZE
850        init_source $ssrc
851        set BUFFER_SIZE($ssrc) $buffered 
852# we don't update cname as source.packet.duration always follows
853}
854
855proc mbus_recv_tool.rat.audio.delay {ssrc len} {
856        global PLAYOUT_DELAY
857        init_source $ssrc
858        set PLAYOUT_DELAY($ssrc) $len 
859# we don't update cname as source.packet.duration always follows
860}
861
862proc mbus_recv_tool.rat.3d.enabled {mode} {
863        global 3d_audio_var
864        set 3d_audio_var $mode
865}
866
867proc mbus_recv_tool.rat.3d.azimuth.min {min} {
868    global 3d_azimuth
869    set 3d_azimuth(min) $min
870}
871
872proc mbus_recv_tool.rat.3d.azimuth.max {max} {
873    global 3d_azimuth
874    set 3d_azimuth(max) $max
875}
876
877proc mbus_recv_tool.rat.3d.filter.types {args} {
878    global 3d_filters
879    set 3d_filters [split $args ","]
880}
881
882proc mbus_recv_tool.rat.3d.filter.lengths {args} {
883    global 3d_filter_lengths
884    set 3d_filter_lengths [split $args ","]
885}
886
887proc mbus_recv_tool.rat.3d.user.settings {args} {
888    global filter_type filter_length azimuth
889    set ssrc                 [lindex $args 0]
890    set filter_type($ssrc)   [lindex $args 1]
891    set filter_length($ssrc) [lindex $args 2]
892    set azimuth($ssrc)       [lindex $args 3]
893}
894
895proc mbus_recv_rtp.source.packet.loss {dest srce loss} {
896        global my_ssrc LOSS_FROM_ME LOSS_TO_ME HEARD_LOSS_FROM_ME HEARD_LOSS_TO_ME
897        init_source $srce
898        init_source $dest
899        chart_set $srce $dest $loss
900        if {[string compare $dest $my_ssrc] == 0} {
901                set LOSS_TO_ME($srce) $loss
902                set HEARD_LOSS_TO_ME($srce) 1
903        }
904        if {[string compare $srce $my_ssrc] == 0} {
905                set LOSS_FROM_ME($dest) $loss
906                set HEARD_LOSS_FROM_ME($dest) 1
907        }
908        ssrc_update $srce
909        ssrc_update $dest
910}
911
912proc mbus_recv_rtp.source.reception {ssrc packets_recv packets_lost packets_miso packets_dup jitter jit_tog} {
913        global PCKTS_RECV PCKTS_LOST PCKTS_MISO PCKTS_DUP JITTER JIT_TOGED
914        init_source $ssrc 
915        set PCKTS_RECV($ssrc) $packets_recv
916        set PCKTS_LOST($ssrc) $packets_lost
917        set PCKTS_MISO($ssrc) $packets_miso
918        set PCKTS_DUP($ssrc)  $packets_dup
919        set JITTER($ssrc) $jitter
920        set JIT_TOGED($ssrc) $jit_tog
921}
922
923proc mbus_recv_rtp.source.active {ssrc} {
924        global speaker_highlight
925        init_source $ssrc 
926        ssrc_update $ssrc
927        [window_plist $ssrc] configure -background $speaker_highlight
928}
929
930proc mbus_recv_rtp.source.inactive {ssrc} {
931        init_source $ssrc 
932        ssrc_update $ssrc
933        [window_plist $ssrc] configure -background [.l.t.list cget -bg]
934}
935
936proc mbus_recv_rtp.source.remove {ssrc} {
937    global CNAME NAME EMAIL LOC PHONE TOOL NOTE CODEC DURATION PCKTS_RECV PCKTS_LOST PCKTS_MISO \
938            PCKTS_DUP JITTER BUFFER_SIZE PLAYOUT_DELAY LOSS_TO_ME LOSS_FROM_ME INDEX JIT_TOGED \
939            num_ssrc loss_to_me_timer loss_from_me_timer GAIN MUTE HEARD_LOSS_TO_ME HEARD_LOSS_FROM_ME
940
941    # Disable updating of loss diamonds. This has to be done before we destroy the
942    # window representing the participant, else the background update may try to
943    # access a window which has been destroyed...
944    catch {after cancel $loss_to_me_timer($ssrc)}
945    catch {after cancel $loss_from_me_timer($ssrc)}
946   
947    chart_remove $ssrc
948
949    catch [destroy [window_plist $ssrc]]
950    if { [info exists CNAME($ssrc)] } {
951        unset CNAME($ssrc) NAME($ssrc) EMAIL($ssrc) PHONE($ssrc) LOC($ssrc) TOOL($ssrc) NOTE($ssrc)
952        unset CODEC($ssrc) DURATION($ssrc) PCKTS_RECV($ssrc) PCKTS_LOST($ssrc) PCKTS_MISO($ssrc) PCKTS_DUP($ssrc)
953        unset JITTER($ssrc) LOSS_TO_ME($ssrc) LOSS_FROM_ME($ssrc) INDEX($ssrc) JIT_TOGED($ssrc) BUFFER_SIZE($ssrc)
954        unset PLAYOUT_DELAY($ssrc) GAIN($ssrc) MUTE($ssrc)
955        incr num_ssrc -1
956    }
957    if { [info exists HEARD_LOSS_TO_ME($ssrc)] } {
958        unset HEARD_LOSS_TO_ME($ssrc)
959    }
960    if { [info exists HEARD_LOSS_FROM_ME($ssrc)] } {
961        unset HEARD_LOSS_FROM_ME($ssrc)
962    }
963}
964
965proc mbus_recv_rtp.source.mute {ssrc val} {
966        global iht MUTE
967        set MUTE($ssrc) $val
968        if {$val} {
969                [window_plist $ssrc] create line [expr $iht + 2] [expr $iht / 2] 500 [expr $iht / 2] -tags a -width 2.0 -fill gray95
970        } else {
971                catch [[window_plist $ssrc] delete a]
972        }
973}
974
975proc mbus_recv_audio.file.play.ready {name} {
976    global play_file
977    set    play_file(name) $name
978
979    if {$play_file(state) != "play"} {
980        file_enable_play
981    }
982}
983
984proc mbus_recv_audio.file.play.alive {alive} {
985   
986    global play_file
987
988    if {$alive} {
989        after 200 file_play_live
990    } else {
991        set play_file(state) end
992        file_enable_play
993    }
994}
995
996proc mbus_recv_audio.file.record.ready {name} {
997    global rec_file
998    set    rec_file(name) $name
999    if {$rec_file(state) != "record"} {
1000        file_enable_record
1001    }
1002}
1003
1004proc mbus_recv_audio.file.record.alive {alive} {
1005        global rec_file
1006        if {$alive} {
1007                after 200 file_rec_live
1008        } else {
1009                set rec_file(state) end
1010                file_enable_record                                         
1011        }
1012}
1013
1014proc mbus_recv_tool.rat.playout.limit {do_limit} {
1015    global limit_var
1016    set limit_var $do_limit
1017}
1018
1019proc mbus_recv_tool.rat.playout.min {lower_bound} {
1020    global min_var
1021    set min_var $lower_bound
1022}
1023
1024proc mbus_recv_tool.rat.playout.max {upper_bound} {
1025    global max_var
1026    set max_var $upper_bound
1027}
1028
1029proc mbus_recv_mbus.quit {} {
1030        destroy .
1031}
1032
1033#############################################################################################################
1034
1035proc set_loss_to_me {ssrc loss} {
1036        global prev_loss_to_me loss_to_me_timer
1037
1038        catch {after cancel $loss_to_me_timer($ssrc)}
1039        set loss_to_me_timer($ssrc) [after 7500 catch \"[window_plist $ssrc] itemconfigure h -fill grey\"]
1040
1041        if {$loss < 5} {
1042                catch [[window_plist $ssrc] itemconfigure m -fill green]
1043        } elseif {$loss < 10} {
1044                catch [[window_plist $ssrc] itemconfigure m -fill orange]
1045        } elseif {$loss <= 100} {
1046                catch [[window_plist $ssrc] itemconfigure m -fill red]
1047        } else {
1048                catch [[window_plist $ssrc] itemconfigure m -fill grey]
1049        }
1050}
1051
1052proc set_loss_from_me {ssrc loss} {
1053        global prev_loss_from_me loss_from_me_timer
1054
1055        catch {after cancel $loss_from_me_timer($ssrc)}
1056        set loss_from_me_timer($ssrc) [after 7500 catch \"[window_plist $ssrc] itemconfigure h -fill grey\"]
1057
1058        if {$loss < 5} {
1059                catch [[window_plist $ssrc] itemconfigure h -fill green]
1060        } elseif {$loss < 10} {
1061                catch [[window_plist $ssrc] itemconfigure h -fill orange]
1062        } elseif {$loss <= 100} {
1063                catch [[window_plist $ssrc] itemconfigure h -fill red]
1064        } else {
1065                catch [[window_plist $ssrc] itemconfigure h -fill grey]
1066        }
1067}
1068
1069proc ssrc_update {ssrc} {
1070        # This procedure updates the on-screen representation of
1071        # a participant.
1072        global NAME LOSS_TO_ME LOSS_FROM_ME HEARD_LOSS_FROM_ME HEARD_LOSS_TO_ME
1073        global fw iht iwd my_ssrc
1074
1075        set cw [window_plist $ssrc]
1076
1077        if {[winfo exists $cw]} {
1078                $cw itemconfigure t -text $NAME($ssrc)
1079        } else {
1080                # Add this participant to the list...
1081                set thick 0
1082                set l $thick
1083                set h [expr $iht / 2 + $thick]
1084                set f [expr $iht + $thick]
1085                canvas $cw -width $iwd -height $f -highlightthickness $thick
1086                $cw create text [expr $f + 2] $h -anchor w -text $NAME($ssrc) -fill black -tag t
1087                $cw create polygon $l $h $h $l $h $f -outline black -fill grey -tag m
1088                $cw create polygon $f $h $h $l $h $f -outline black -fill grey -tag h
1089
1090                bind $cw <Button-1>         "toggle_stats \"$ssrc\""
1091                bind $cw <Button-2>         "toggle_mute $cw \"$ssrc\""
1092                bind $cw <Control-Button-1> "toggle_mute $cw \"$ssrc\""
1093
1094                if {[info exists my_ssrc] && ([string compare $ssrc $my_ssrc] == 0) && ([pack slaves $fw] != "")} {
1095                        pack $cw -before [lindex [pack slaves $fw] 0] -fill x
1096                }
1097                pack $cw -fill x
1098                fix_scrollbar
1099        }
1100
1101        if {[info exists HEARD_LOSS_TO_ME($ssrc)] && $HEARD_LOSS_TO_ME($ssrc) && $ssrc != $my_ssrc} {
1102            set_loss_to_me $ssrc $LOSS_TO_ME($ssrc)
1103        }
1104        if {[info exists HEARD_LOSS_FROM_ME($ssrc)] && $HEARD_LOSS_FROM_ME($ssrc) && $ssrc != $my_ssrc} {
1105            set_loss_from_me $ssrc $LOSS_FROM_ME($ssrc)
1106        }
1107}
1108
1109#power meters
1110
1111# Colors
1112set bargraphLitColors [list #00cc00 #00cc00 #00cc00 #00cc00 #00cc00 #00cc00 #00cc00 #00cc00 #00cc00 #00cc00 #00cc00 #2ccf00 #58d200 #84d500 #b0d800 #dddd00 #ddb000 #dd8300 #dd5600 #dd2900]
1113
1114set bargraphUnlitColors [list #006600 #006600 #006600 #006600 #006600 #006600 #006600 #006600 #006600 #006600 #006600 #166700 #2c6900 #426a00 #586c00 #6e6e00 #6e5800 #6e4100 #6e2b00 #6e1400]
1115set bargraphTotalHeight [llength $bargraphLitColors]
1116
1117proc bargraphCreate {bgraph} {
1118        global oh$bgraph bargraphTotalHeight bargraphUnlitColors
1119
1120        frame $bgraph -relief sunk -bg black
1121        for {set i 0} {$i < $bargraphTotalHeight} {incr i} {
1122                frame $bgraph.inner$i -bg "[lindex $bargraphUnlitColors $i]" -width 4 -height 8
1123                pack $bgraph.inner$i -side left -fill both -expand true -padx 1 -pady 1
1124        }
1125        set oh$bgraph 0
1126}
1127
1128proc bargraphSetHeight {bgraph height} {
1129        upvar #0 oh$bgraph oh
1130        global bargraphTotalHeight bargraphLitColors bargraphUnlitColors
1131
1132        if {$oh > $height} {
1133                for {set i [expr $height]} {$i <= $oh} {incr i} {
1134                        $bgraph.inner$i config -bg "[lindex $bargraphUnlitColors $i]"
1135                }
1136        } else {
1137            for {set i [expr $oh]} {$i <= $height} {incr i} {
1138                $bgraph.inner$i config -bg  "[lindex $bargraphLitColors $i]"
1139            }
1140        }
1141        set oh $height
1142}
1143
1144proc bargraphState {bgraph state} {
1145    upvar #0 oh$bgraph oh
1146    if {[winfo exists $bgraph]} {
1147        global bargraphTotalHeight bargraphUnlitColors
1148        if {$state} {
1149            for { set i 0 } { $i < $bargraphTotalHeight} {incr i} {
1150                $bgraph.inner$i config -bg "[lindex $bargraphUnlitColors $i]"
1151            }
1152        } else {
1153            for { set i 0 } { $i < $bargraphTotalHeight} {incr i} {
1154                $bgraph.inner$i config -bg black
1155            }
1156        }
1157    }
1158    set oh 0
1159}
1160
1161proc toggle {varname} {
1162    upvar 1 $varname local
1163    set local [expr !$local]
1164}
1165
1166proc toggle_plist {} {
1167        global plist_on
1168        if {$plist_on} {
1169                pack .l.t  -side top -fill both -expand 1
1170        } else {
1171                pack forget .l.t
1172        }
1173        update
1174        wm deiconify .
1175}
1176
1177proc toggle_mute {cw ssrc} {
1178        global iht
1179        if {[$cw gettags a] == ""} {
1180                mbus_send "R" "rtp.source.mute" "[mbus_encode_str $ssrc] 1"
1181        } else {
1182                mbus_send "R" "rtp.source.mute" "[mbus_encode_str $ssrc] 0"
1183        }
1184}
1185
1186proc send_gain_and_mute {ssrc} {
1187        global GAIN MUTE
1188        mbus_send "R" "rtp.source.gain" "[mbus_encode_str $ssrc] $GAIN($ssrc)"
1189        mbus_send "R" "rtp.source.mute" "[mbus_encode_str $ssrc] $MUTE($ssrc)"
1190}
1191
1192proc fix_scrollbar {} {
1193        global iht iwd fw
1194
1195        set ch [expr $iht * ([llength [pack slaves $fw]] + 2)]
1196        set bh [winfo height .l.t.scr]
1197        if {$ch > $bh} {set h $ch} else {set h $bh}
1198        .l.t.list configure -scrollregion "0.0 0.0 $iwd $h"
1199}
1200
1201proc info_timer {} {
1202        global cancel_info_timer
1203        if {$cancel_info_timer == 1} {
1204                set cancel_info_timer 0
1205        } else {
1206                update_rec_info
1207                after 1000 info_timer
1208        }
1209}
1210
1211proc stats_add_field {widget label watchVar} {
1212    global statsfont
1213    frame $widget -relief sunk
1214    label $widget.l -text $label -font $statsfont -anchor w
1215    label $widget.w -textvariable $watchVar -font $statsfont
1216    pack $widget -side top -fill x -expand 1
1217    pack $widget.l -side left  -fill x -expand 1
1218    pack $widget.w -side right
1219}
1220
1221proc ssrc_set_gain {ssrc gain} {
1222    global GAIN
1223    set    GAIN($ssrc) [format "%.2f " [expr pow (2, $gain)]]
1224    send_gain_and_mute $ssrc
1225}
1226
1227set 3d_azimuth(min) 0
1228set 3d_azimuth(max) 0
1229set 3d_filters        [list "Not Available"]
1230set 3d_filter_lengths [list "0"]
1231
1232proc toggle_stats {ssrc} {
1233    global statsfont
1234    set win [window_stats $ssrc]
1235    if {[winfo exists $win]} {
1236        destroy $win
1237    } else {
1238        global stats_pane
1239        # Window does not exist so create it
1240        toplevel $win 
1241        frame $win.mf
1242        pack $win.mf -padx 0 -pady 0
1243        label $win.mf.l -text "Category:"
1244       
1245        menubutton $win.mf.mb -menu $win.mf.mb.menu -indicatoron 1 -textvariable stats_pane($win) -relief raised -width 16
1246        pack $win.mf.l $win.mf.mb -side left
1247        menu $win.mf.mb.menu -tearoff 0
1248        $win.mf.mb.menu add command -label "Personal Details" -command "set_pane stats_pane($win) $win.df \"Personal Details\""
1249        $win.mf.mb.menu add command -label "Reception"        -command "set_pane stats_pane($win) $win.df Reception"
1250        $win.mf.mb.menu add command -label "Audio"            -command "set_pane stats_pane($win) $win.df Audio"
1251        $win.mf.mb.menu add command -label "3D Positioning"   -command "set_pane stats_pane($win) $win.df \"3D Positioning\""
1252
1253        set stats_pane($win) "Personal Details"
1254        frame $win.df
1255        frame $win.df.personal
1256        pack  $win.df $win.df.personal -fill x
1257
1258        global NAME EMAIL PHONE LOC NOTE CNAME TOOL SSRC
1259        stats_add_field $win.df.personal.1 "Name: "     NAME($ssrc)
1260        stats_add_field $win.df.personal.2 "Email: "    EMAIL($ssrc)
1261        stats_add_field $win.df.personal.3 "Phone: "    PHONE($ssrc)
1262        stats_add_field $win.df.personal.4 "Location: " LOC($ssrc)
1263        stats_add_field $win.df.personal.5 "Note: "     NOTE($ssrc)
1264        stats_add_field $win.df.personal.6 "Tool: "     TOOL($ssrc)
1265        stats_add_field $win.df.personal.7 "CNAME: "    CNAME($ssrc)
1266        stats_add_field $win.df.personal.8 "SSRC: "     SSRC($ssrc)
1267
1268        frame $win.df.reception
1269        global CODEC DURATION BUFFER_SIZE PLAYOUT_DELAY PCKTS_RECV PCKTS_LOST PCKTS_MISO \
1270               PCKTS_DUP LOSS_FROM_ME LOSS_TO_ME JITTER JIT_TOGED
1271        stats_add_field $win.df.reception.1 "Audio encoding: "         CODEC($ssrc)
1272        stats_add_field $win.df.reception.2 "Packet duration (ms): "   DURATION($ssrc)
1273        stats_add_field $win.df.reception.3 "Playout delay (ms): "     PLAYOUT_DELAY($ssrc)
1274        stats_add_field $win.df.reception.5 "Arrival jitter (ms): "    JITTER($ssrc)
1275        stats_add_field $win.df.reception.6 "Loss from me (%): "       LOSS_FROM_ME($ssrc)
1276        stats_add_field $win.df.reception.7 "Loss to me (%): "         LOSS_TO_ME($ssrc)
1277        stats_add_field $win.df.reception.8 "Packets received: "       PCKTS_RECV($ssrc)
1278        stats_add_field $win.df.reception.9 "Packets lost: "           PCKTS_LOST($ssrc)
1279        stats_add_field $win.df.reception.a "Packets misordered: "     PCKTS_MISO($ssrc)
1280        stats_add_field $win.df.reception.b "Packets duplicated: "     PCKTS_DUP($ssrc)
1281        stats_add_field $win.df.reception.c "Units dropped (jitter): " JIT_TOGED($ssrc)
1282
1283# Audio settings
1284        global GAIN MUTE
1285        frame $win.df.audio -relief sunk       
1286        label $win.df.audio.advice -text "The signal from the participant can\nbe scaled and muted with the controls below."
1287        pack  $win.df.audio.advice
1288
1289        checkbutton $win.df.audio.mute -text "Mute" -variable MUTE($ssrc) -command "send_gain_and_mute $ssrc"
1290        pack $win.df.audio.mute
1291
1292        frame $win.df.audio.opts
1293        pack  $win.df.audio.opts -side top
1294        label $win.df.audio.opts.title -text "Gain"
1295        scale $win.df.audio.opts.gain_scale -showvalue 0 -orient h -from -3 -to +3 -resolution 0.25 -command "ssrc_set_gain $ssrc"
1296        label $win.df.audio.opts.gain_text -textvariable GAIN($ssrc) -width 4
1297        pack  $win.df.audio.opts.title $win.df.audio.opts.gain_scale $win.df.audio.opts.gain_text -side left
1298
1299        $win.df.audio.opts.gain_scale set [expr log10($GAIN($ssrc)) / log10(2)]
1300
1301        button $win.df.audio.default -text "Default" -command "set MUTE($ssrc) 0; $win.df.audio.opts.gain_scale set 0.0; send_gain_and_mute $ssrc" 
1302        pack   $win.df.audio.default -side right  -anchor e -padx 2 -pady 2
1303
1304# 3D settings
1305        # Trigger engine to send details for this participant
1306        mbus_send "R" "tool.rat.3d.user.settings.request" [mbus_encode_str $ssrc]
1307
1308        frame $win.df.3d -relief sunk
1309        label $win.df.3d.advice -text "These options allow the rendering of the\nparticipant to be altered when 3D\nrendering is enabled."
1310        checkbutton $win.df.3d.ext -text "3D Audio Rendering" -variable 3d_audio_var
1311        pack $win.df.3d.advice
1312        pack $win.df.3d.ext
1313
1314        frame $win.df.3d.opts
1315        pack $win.df.3d.opts -side top
1316
1317        frame $win.df.3d.opts.filters
1318        label $win.df.3d.opts.filters.l -text "Filter Type:"
1319        pack $win.df.3d.opts.filters.l -side top -fill x -expand 1 -anchor w
1320        global 3d_filters 3d_filter_lengths
1321       
1322        global filter_type
1323        set filter_type($ssrc) [lindex $3d_filters 0]
1324
1325        set cnt 0
1326        foreach i $3d_filters {
1327            radiobutton $win.df.3d.opts.filters.$cnt \
1328                    -value "$i" -variable filter_type($ssrc) \
1329                    -text "$i"
1330                pack $win.df.3d.opts.filters.$cnt -side top -anchor w
1331            incr cnt
1332        }
1333
1334        frame $win.df.3d.opts.lengths
1335        label $win.df.3d.opts.lengths.l -text "Filter Length:" -width 16
1336        pack $win.df.3d.opts.lengths.l -side top -fill x -expand 1
1337       
1338        global filter_length
1339        set filter_length($ssrc) [lindex $3d_filter_lengths 0]
1340       
1341        set cnt 0
1342        foreach i $3d_filter_lengths {
1343            radiobutton $win.df.3d.opts.lengths.$cnt \
1344                    -value "$i" -variable filter_length($ssrc) \
1345                    -text "$i"
1346            pack $win.df.3d.opts.lengths.$cnt -side top -anchor w
1347            incr cnt
1348        }
1349        pack $win.df.3d.opts.filters -side left -expand 1 -anchor n
1350        pack $win.df.3d.opts.lengths -side left -expand 1 -anchor n
1351       
1352        global 3d_azimuth azimuth
1353        scale $win.df.3d.azimuth -from $3d_azimuth(min) -to $3d_azimuth(max) \
1354                -orient horizontal -label "Azimuth" -variable azimuth($ssrc)
1355        pack  $win.df.3d.azimuth -fill x -expand 1
1356
1357        button $win.df.3d.apply -text "Apply" -command "3d_send_parameters $ssrc"
1358        pack   $win.df.3d.apply -side bottom  -anchor e -padx 2 -pady 2
1359
1360# Window Magic
1361        frame  $win.dis
1362        button $win.dis.b -text "Dismiss" -command "destroy $win; 3d_delete_parameters $ssrc"
1363        pack   $win.dis   -side bottom -anchor s -fill x -expand 1
1364        pack   $win.dis.b -side right -anchor e -padx 2 -pady 2
1365        wm title $win "Participant $NAME($ssrc)"
1366        wm resizable $win 1 0
1367        constrain_window $win $statsfont 36 27
1368    }
1369}
1370
1371proc 3d_send_parameters {ssrc} {
1372    global azimuth filter_type filter_length 3d_audio_var
1373
1374    mbus_send "R" "tool.rat.3d.enabled"   $3d_audio_var
1375    mbus_send "R" "tool.rat.3d.user.settings" "[mbus_encode_str $ssrc] [mbus_encode_str $filter_type($ssrc)] $filter_length($ssrc) $azimuth($ssrc)"
1376}
1377
1378proc 3d_delete_parameters {ssrc} {
1379    global filter_type filter_length azimuth
1380   
1381# None of these should ever fail, but you can't be too defensive...
1382    catch {
1383        unset filter_type($ssrc)
1384        unset filter_length($ssrc)
1385        unset azimuth($ssrc)
1386    }
1387}
1388
1389proc bitmap_input_port {port} {
1390    set port [string tolower $port]
1391    return ""
1392        switch -glob $port {
1393        mic* {return "microphone"}
1394        lin* {return "line_in"}
1395        cd*  {return "cd"}
1396        default {return ""}
1397    }
1398}
1399
1400proc bitmap_output_port {port} {
1401    set port [string tolower $port]
1402    return ""
1403        switch -glob $port {
1404        speak* {return "speaker"}
1405        lin*   {return "line_out"}
1406        head*  {return "headphone"}
1407        default {return ""}
1408    }
1409}
1410
1411proc configure_input_port {port} {
1412    global input_port
1413    set bitmap [bitmap_input_port $port]
1414    if {$bitmap != ""} {
1415        .r.c.gain.but.l2 configure -bitmap $bitmap
1416    } else {
1417        .r.c.gain.but.l2 configure -bitmap ""
1418        .r.c.gain.but.l2 configure -text $port
1419    }
1420    set input_port $port
1421}
1422
1423proc configure_output_port {port} {
1424    global output_port
1425    set bitmap [bitmap_output_port $port]
1426
1427    if {$bitmap != ""} {
1428        .r.c.vol.but.l1 configure -bitmap $bitmap
1429    } else {
1430        .r.c.vol.but.l1 configure -bitmap ""
1431        .r.c.vol.but.l1 configure -text $port
1432    }
1433    set output_port $port
1434}
1435
1436proc do_quit {} {
1437        catch {
1438                profile off pdat
1439                profrep pdat cpu
1440        }
1441        destroy .
1442        mbus_send "R" "mbus.quit" ""
1443}
1444
1445# Initialise RAT MAIN window
1446frame .r
1447frame .l
1448frame .l.t -relief sunken
1449scrollbar .l.t.scr -relief flat -highlightthickness 0 -command ".l.t.list yview"
1450canvas .l.t.list -highlightthickness 0 -bd 0 -relief sunk -width $iwd -height 160 -yscrollcommand ".l.t.scr set" -yscrollincrement $iht
1451frame .l.t.list.f -highlightthickness 0 -bd 0
1452.l.t.list create window 0 0 -anchor nw -window .l.t.list.f
1453
1454frame .l.f -relief flat -bd 0
1455label .l.f.title -bd 0 -textvariable session_title
1456label .l.f.addr  -bd 0 -textvariable session_address
1457
1458frame  .st -bd 0
1459label  .st.tool -textvariable tool_name
1460button .st.opts  -text "Options"   -command {wm deiconify .prefs; raise .prefs}
1461button .st.about -text "About"     -command {jiggle_credits; wm deiconify .about}
1462button .st.quit  -text "Quit"      -command do_quit
1463
1464frame .r.c -bd 0
1465frame .r.c.vol -bd 0
1466frame .r.c.gain -bd 0
1467
1468pack .st -side bottom -fill x
1469pack .st.tool -side left -anchor w
1470pack .st.quit .st.about .st.opts -side right -anchor w -padx 2 -pady 2
1471
1472pack .r -side top -fill x
1473pack .r.c -side top -fill x -expand 1
1474pack .r.c.vol  -side top -fill x
1475pack .r.c.gain -side top -fill x
1476
1477pack .l -side top -fill both -expand 1
1478pack .l.f -side bottom -fill x -padx 2 -pady 2
1479pack .l.f.title .l.f.addr -side top -pady 2 -anchor w
1480pack .l.t  -side top -fill both -expand 1 -padx 2
1481pack .l.t.scr -side left -fill y
1482pack .l.t.list -side left -fill both -expand 1
1483bind .l.t.list <Configure> {fix_scrollbar}
1484
1485# Device output controls
1486set out_mute_var 0
1487frame .r.c.vol.but
1488frame .r.c.vol.gra
1489checkbutton .r.c.vol.but.t1 -highlightthickness 0 -text "Receive" -onvalue 0 -offvalue 1 -variable out_mute_var -command {output_mute $out_mute_var} -font $infofont -width 8 -anchor w -relief raised
1490button .r.c.vol.but.l1 -highlightthickness 0 -command toggle_output_port -font $infofont -width 10
1491bargraphCreate .r.c.vol.gra.b1
1492scale .r.c.vol.gra.s1 -highlightthickness 0 -from 0 -to 99 -command set_vol -orient horizontal -showvalue false -width 8 -variable volume
1493
1494pack .r.c.vol.but -side left -fill both
1495pack .r.c.vol.but.t1 -side left -fill y
1496pack .r.c.vol.but.l1 -side left -fill y
1497
1498pack .r.c.vol.gra -side left -fill both -expand 1
1499pack .r.c.vol.gra.b1 -side top  -fill both -expand 1 -padx 1 -pady 1
1500pack .r.c.vol.gra.s1 -side bottom  -fill x -anchor s
1501
1502# Device input controls
1503set in_mute_var 1
1504
1505frame .r.c.gain.but
1506checkbutton .r.c.gain.but.t2 -highlightthickness 0 -text "Transmit" -variable in_mute_var -onvalue 0 -offvalue 1 -command {input_mute $in_mute_var} -font $infofont -width 8 -anchor w -relief raised
1507button .r.c.gain.but.l2 -highlightthickness 0 -command toggle_input_port -font $infofont -width 10
1508
1509frame .r.c.gain.gra
1510bargraphCreate .r.c.gain.gra.b2
1511scale .r.c.gain.gra.s2 -highlightthickness 0 -from 0 -to 99 -command set_gain -orient horizontal -showvalue false -width 8 -variable gain -font $smallfont
1512
1513pack .r.c.gain.but    -side left
1514pack .r.c.gain.but.t2 -side left -fill y
1515pack .r.c.gain.but.l2 -side left -fill y
1516
1517pack .r.c.gain.gra -side left -fill both -expand 1
1518pack .r.c.gain.gra.b2 -side top  -fill both -expand 1 -padx 1 -pady 1
1519pack .r.c.gain.gra.s2 -side top  -fill x -anchor s
1520
1521proc mbus_recv_tool.rat.disable.audio.ctls {} {
1522#       .r.c.vol.but.t1 configure -state disabled
1523        .r.c.vol.but.l1 configure -state disabled
1524        .r.c.vol.gra.s1 configure -state disabled
1525#       .r.c.gain.but.t2 configure -state disabled
1526        .r.c.gain.but.l2 configure -state disabled
1527        .r.c.gain.gra.s2 configure -state disabled
1528}
1529
1530proc mbus_recv_tool.rat.enable.audio.ctls {} {
1531#       .r.c.vol.but.t1 configure -state normal
1532        .r.c.vol.but.l1 configure -state normal
1533        .r.c.vol.gra.s1 configure -state normal
1534#       .r.c.gain.but.t2 configure -state normal
1535        .r.c.gain.but.l2 configure -state normal
1536        .r.c.gain.gra.s2 configure -state normal
1537}
1538bind all <ButtonPress-3>   {toggle in_mute_var; input_mute $in_mute_var}
1539bind all <ButtonRelease-3> {toggle in_mute_var; input_mute $in_mute_var}
1540bind all <q>               {+if {[winfo class %W] != "Entry"} {do_quit}}
1541
1542# Override default tk behaviour
1543wm protocol . WM_DELETE_WINDOW do_quit
1544
1545if {$win32 == 0} {
1546        wm iconbitmap . rat_small
1547}
1548wm resizable . 0 1
1549if ([info exists geometry]) {
1550        wm geometry . $geometry
1551}
1552
1553proc averageCharacterWidth {font} {
1554    set sample "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
1555    set slen [string length $sample]
1556    set wpc  [expr [font measure $font $sample] / $slen + 1]
1557    return $wpc
1558}
1559
1560# constrain_window - window, font, characters wide, characters high
1561proc constrain_window {w font cW cH} {
1562   
1563    catch {
1564            set wpc [averageCharacterWidth $font]
1565            set hpc [font metrics $font -ascent]
1566   
1567        # Calculate dimensions
1568            set width [expr $cW * $wpc]
1569            set height [expr $cH * $hpc]
1570            wm geometry $w [format "%sx%s" $width $height]
1571            set dummy ""
1572    } err
1573    if {$err != ""} {
1574        puts "Error: $err"
1575    }
1576}
1577
1578proc tk_optionCmdMenu {w varName firstValue args} {
1579    upvar #0 $varName var
1580 
1581    if ![info exists var] {
1582        set var $firstValue
1583    }
1584    menubutton $w -textvariable $varName -indicatoron 1 -menu $w.menu \
1585            -relief raised -bd 2 -highlightthickness 2 -anchor c
1586
1587    menu $w.menu -tearoff 0
1588    $w.menu add command -label $firstValue -command "set $varName \"$firstValue\""
1589    foreach i $args {
1590        $w.menu add command -label $i -command "set $varName \"$i\""
1591    }
1592    return $w.menu
1593}
1594
1595
1596###############################################################################
1597# Preferences Panel
1598#
1599
1600set prefs_pane "Personal"
1601toplevel .prefs
1602wm title .prefs "Preferences"
1603wm resizable .prefs 0 0
1604wm withdraw  .prefs
1605
1606frame .prefs.m
1607pack .prefs.m -side top -fill x -expand 0 -padx 2 -pady 2
1608frame .prefs.m.f
1609pack .prefs.m.f -padx 0 -pady 0
1610label .prefs.m.f.t -text "Category: "
1611pack .prefs.m.f.t -pady 2 -side left
1612menubutton .prefs.m.f.m -menu .prefs.m.f.m.menu -indicatoron 1 -textvariable prefs_pane -relief raised -width 14
1613pack .prefs.m.f.m -side top
1614menu .prefs.m.f.m.menu -tearoff 0
1615.prefs.m.f.m.menu add command -label "Personal"     -command {set_pane prefs_pane .prefs.pane "Personal"}
1616.prefs.m.f.m.menu add command -label "Transmission" -command {set_pane prefs_pane .prefs.pane "Transmission"; update_codecs_displayed}
1617.prefs.m.f.m.menu add command -label "Reception"    -command {set_pane prefs_pane .prefs.pane "Reception"}
1618.prefs.m.f.m.menu add command -label "Audio"        -command {set_pane prefs_pane .prefs.pane "Audio"}
1619.prefs.m.f.m.menu add command -label "Codecs"        -command {set_pane prefs_pane .prefs.pane "Codecs"; codecs_panel_fill}
1620.prefs.m.f.m.menu add command -label "Security"     -command {set_pane prefs_pane .prefs.pane "Security"}
1621.prefs.m.f.m.menu add command -label "Interface"    -command {set_pane prefs_pane .prefs.pane "Interface"}
1622
1623frame  .prefs.buttons
1624pack   .prefs.buttons       -side bottom -fill x
1625button .prefs.buttons.bye   -text "Cancel" -command {sync_ui_to_engine; wm withdraw .prefs}
1626button .prefs.buttons.apply -text "Apply" -command {wm withdraw .prefs; sync_engine_to_ui}
1627#button .prefs.buttons.save  -text "Save & Apply" -command {save_settings; wm withdraw .prefs; sync_engine_to_ui}
1628#pack   .prefs.buttons.bye .prefs.buttons.apply .prefs.buttons.save -side right -padx 2 -pady 2
1629pack   .prefs.buttons.bye .prefs.buttons.apply -side right -padx 2 -pady 2
1630
1631wm protocol .prefs WM_DELETE_WINDOW {sync_ui_to_engine; wm withdraw .prefs}
1632
1633frame .prefs.pane -relief sunken
1634pack  .prefs.pane -side left -fill both -expand 1 -padx 4 -pady 2
1635
1636# setup width of prefs panel
1637constrain_window .prefs $infofont 56 30
1638
1639# Personal Info Pane
1640set i .prefs.pane.personal
1641frame $i
1642pack $i -fill both -expand 1 -pady 2 -padx 2
1643
1644frame $i.a -relief sunken
1645frame $i.a.f
1646pack $i.a -side top -fill both -expand 1
1647pack $i.a.f -side left -fill x -expand 1
1648
1649frame $i.a.f.f
1650pack $i.a.f.f
1651
1652label $i.a.f.f.l -width 40 -height 2 -text "The personal details below are conveyed\nto the other conference participants." -justify left -anchor w
1653pack $i.a.f.f.l -side top -anchor w -fill x
1654
1655frame $i.a.f.f.lbls
1656frame $i.a.f.f.ents
1657pack  $i.a.f.f.lbls -side left -fill y
1658pack  $i.a.f.f.ents -side right
1659
1660label $i.a.f.f.lbls.name  -text "Name:"     -anchor w
1661label $i.a.f.f.lbls.email -text "Email:"    -anchor w
1662label $i.a.f.f.lbls.phone -text "Phone:"    -anchor w
1663label $i.a.f.f.lbls.loc   -text "Location:" -anchor w
1664pack $i.a.f.f.lbls.name $i.a.f.f.lbls.email $i.a.f.f.lbls.phone $i.a.f.f.lbls.loc -fill x -anchor w -side top
1665
1666entry $i.a.f.f.ents.name  -width 28 -highlightthickness 0 -textvariable rtcp_name
1667entry $i.a.f.f.ents.email -width 28 -highlightthickness 0 -textvariable rtcp_email
1668entry $i.a.f.f.ents.phone -width 28 -highlightthickness 0 -textvariable rtcp_phone
1669entry $i.a.f.f.ents.loc   -width 28 -highlightthickness 0 -textvariable rtcp_loc
1670pack $i.a.f.f.ents.name $i.a.f.f.ents.email $i.a.f.f.ents.phone $i.a.f.f.ents.loc -anchor n -expand 0
1671
1672# Transmission Pane ###########################################################
1673set i .prefs.pane.transmission
1674frame $i
1675frame $i.dd
1676frame $i.cc
1677frame $i.cc.van
1678frame $i.cc.red
1679frame $i.cc.layer
1680frame $i.cc.int
1681label $i.intro -text "This panel allows you to select codecs for transmission.  The choice\nof codecs available depends on the sampling rate and channels\nin the audio panel."
1682label $i.title1 -relief raised -text "Audio Encoding"
1683pack $i.intro $i.title1 $i.dd -side top -fill x
1684
1685#pack $i.dd -fill x -side top -anchor n
1686
1687label $i.title2 -relief raised -text "Channel Coding Options"
1688pack $i.title2 -fill x -side top
1689pack $i.cc -fill x -anchor w -pady 1
1690
1691pack $i.cc.van $i.cc.red $i.cc.layer -fill x -anchor w -pady 0
1692# interleaving panel $i.cc.int not packed since interleaving isn't support in this release
1693frame $i.dd.units
1694frame $i.dd.pri
1695
1696pack $i.dd.units $i.dd.pri -side right -fill x
1697
1698label $i.dd.pri.l -text "Encoding:"
1699menubutton $i.dd.pri.m -menu $i.dd.pri.m.menu -indicatoron 1 -textvariable prenc -relief raised -width 13
1700pack $i.dd.pri.l $i.dd.pri.m -side top
1701# fill in codecs
1702menu $i.dd.pri.m.menu -tearoff 0
1703
1704label $i.dd.units.l -text "Units:"
1705tk_optionCmdMenu $i.dd.units.m upp 1 2 4 8
1706$i.dd.units.m configure -width 13 -highlightthickness 0 -bd 1
1707pack $i.dd.units.l $i.dd.units.m -side top -fill x
1708
1709radiobutton $i.cc.van.rb -text "No Loss Protection" -justify right -value none        -variable channel_var
1710radiobutton $i.cc.red.rb -text "Redundancy"         -justify right -value redundancy  -variable channel_var
1711radiobutton $i.cc.layer.rb -text "Layering"                     -justify right -value layering    -variable channel_var
1712radiobutton $i.cc.int.rb -text "Interleaving"       -justify right -value interleaved -variable channel_var -state disabled
1713pack $i.cc.van.rb $i.cc.red.rb $i.cc.layer.rb $i.cc.int.rb -side left -anchor nw -padx 2
1714
1715frame $i.cc.red.fc
1716label $i.cc.red.fc.l -text "Encoding:"
1717menubutton $i.cc.red.fc.m -textvariable secenc -indicatoron 1 -menu $i.cc.red.fc.m.menu -relief raised -width 13
1718menu $i.cc.red.fc.m.menu -tearoff 0
1719
1720frame $i.cc.red.u
1721label $i.cc.red.u.l -text "Offset in Pkts:"
1722tk_optionCmdMenu $i.cc.red.u.m red_off "1" "2" "4" "8" 
1723$i.cc.red.u.m configure -width 13 -highlightthickness 0 -bd 1
1724pack $i.cc.red.u -side right -anchor e -fill y
1725pack $i.cc.red.u.l $i.cc.red.u.m -fill x
1726pack $i.cc.red.fc -side right
1727pack $i.cc.red.fc.l $i.cc.red.fc.m
1728
1729frame $i.cc.layer.fc
1730label $i.cc.layer.fc.l -text "Layers:"
1731menubutton $i.cc.layer.fc.m -textvariable layerenc -indicatoron 1 -menu $i.cc.layer.fc.m.menu -relief raised -width 13
1732menu $i.cc.layer.fc.m.menu -tearoff 0
1733pack $i.cc.layer.fc -side right
1734pack $i.cc.layer.fc.l $i.cc.layer.fc.m
1735
1736frame $i.cc.int.zz
1737label $i.cc.int.zz.l -text "Units:"
1738tk_optionCmdMenu $i.cc.int.zz.m int_units 2 4 6 8
1739$i.cc.int.zz.m configure -width 13 -highlightthickness 0 -bd 1 -state disabled
1740
1741frame $i.cc.int.fc
1742label $i.cc.int.fc.l -text "Separation:" 
1743tk_optionCmdMenu $i.cc.int.fc.m int_gap 2 4 6 8
1744$i.cc.int.fc.m configure -width 13 -highlightthickness 0 -bd 1 -state disabled
1745
1746pack $i.cc.int.fc $i.cc.int.zz -side right
1747pack $i.cc.int.fc.l $i.cc.int.fc.m -fill x -expand 1
1748pack $i.cc.int.zz.l $i.cc.int.zz.m -fill x -expand 1
1749
1750# Reception Pane ##############################################################
1751set i .prefs.pane.reception
1752frame $i 
1753frame $i.r -relief sunken
1754frame $i.o -relief sunken
1755frame $i.c -relief sunken
1756pack $i.r -side top -fill x -pady 0 -ipady 1
1757pack $i.o -side top -fill both  -pady 1
1758pack $i.c -side top -fill both  -pady 1 -expand 1
1759label $i.r.l -text "Repair Scheme:"
1760tk_optionCmdMenu $i.r.m repair_var {}
1761
1762label $i.r.ls -text "Sample Rate Conversion"
1763tk_optionCmdMenu $i.r.ms convert_var {}
1764
1765$i.r.m  configure -width 20 -bd 1
1766$i.r.ms configure -width 20 -bd 1
1767pack $i.r.l $i.r.m $i.r.ls $i.r.ms -side top
1768
1769frame $i.o.f
1770checkbutton $i.o.f.cb -text "Limit Playout Delay" -variable limit_var
1771frame $i.o.f.fl
1772label $i.o.f.fl.l1 -text "Minimum Delay (ms)" 
1773scale $i.o.f.fl.scmin -orient horizontal -from 0 -to 1000    -variable min_var -font $smallfont
1774frame $i.o.f.fr
1775label $i.o.f.fr.l2 -text "Maximum Delay (ms)"           
1776scale $i.o.f.fr.scmax -orient horizontal -from 1000 -to 2000 -variable max_var -font $smallfont
1777pack $i.o.f
1778pack $i.o.f.cb -side top -fill x
1779pack $i.o.f.fl $i.o.f.fr -side left
1780pack $i.o.f.fl.l1 $i.o.f.fl.scmin $i.o.f.fr.l2 $i.o.f.fr.scmax -side top -fill x -expand 1
1781
1782frame $i.c.f
1783frame $i.c.f.f
1784checkbutton $i.c.f.f.lec -text "Lecture Mode"       -variable lecture_var
1785checkbutton $i.c.f.f.ext -text "3D Audio Rendering" -variable 3d_audio_var
1786
1787pack $i.c.f -fill x -side left -expand 1
1788pack $i.c.f.f
1789pack $i.c.f.f.lec -side top  -anchor w
1790pack $i.c.f.f.ext -side top  -anchor w
1791
1792# Audio #######################################################################
1793set i .prefs.pane.audio
1794frame $i 
1795frame $i.dd -relief sunken
1796pack $i.dd -fill both -expand 1 -anchor w -pady 1
1797
1798label $i.dd.title -height 2 -width 40 -text "This panel allows for the selection of alternate audio devices\nand the configuring of device related options." -justify left
1799pack $i.dd.title -fill x
1800
1801frame $i.dd.device
1802pack $i.dd.device -side top
1803
1804label $i.dd.device.l -text "Audio Device:"
1805pack  $i.dd.device.l -side top -fill x
1806menubutton $i.dd.device.mdev -menu $i.dd.device.mdev.menu -indicatoron 1 \
1807                                -textvariable audio_device -relief raised -width 5
1808pack $i.dd.device.mdev -fill x -expand 1
1809menu $i.dd.device.mdev.menu -tearoff 0
1810
1811frame $i.dd.sampling 
1812pack  $i.dd.sampling
1813
1814frame $i.dd.sampling.freq
1815frame $i.dd.sampling.ch_in
1816pack $i.dd.sampling.freq $i.dd.sampling.ch_in -side left -fill x
1817
1818label $i.dd.sampling.freq.l   -text "Sample Rate:   "
1819label $i.dd.sampling.ch_in.l  -text "Channels:"
1820pack $i.dd.sampling.freq.l $i.dd.sampling.ch_in.l -fill x
1821
1822menubutton $i.dd.sampling.freq.mb -menu $i.dd.sampling.freq.mb.m -indicatoron 1 \
1823                                  -textvariable freq -relief raised
1824pack $i.dd.sampling.freq.mb -side left -fill x -expand 1
1825menu $i.dd.sampling.freq.mb.m
1826
1827menubutton $i.dd.sampling.ch_in.mb -menu $i.dd.sampling.ch_in.mb.m -indicatoron 1 \
1828                                  -textvariable ichannels -relief raised
1829pack $i.dd.sampling.ch_in.mb -side left -fill x -expand 1
1830menu $i.dd.sampling.ch_in.mb.m
1831
1832frame $i.dd.cks
1833pack $i.dd.cks -fill both -expand 1
1834frame $i.dd.cks.f
1835frame $i.dd.cks.f.f
1836checkbutton $i.dd.cks.f.f.silence  -text "Silence Suppression"    -variable silence_var
1837checkbutton $i.dd.cks.f.f.agc      -text "Automatic Gain Control" -variable agc_var
1838checkbutton $i.dd.cks.f.f.loop     -text "Audio Loopback"         -variable audio_loop_var
1839checkbutton $i.dd.cks.f.f.suppress -text "Echo Suppression"       -variable echo_var
1840pack $i.dd.cks.f -fill x -side top -expand 1
1841pack $i.dd.cks.f.f
1842pack $i.dd.cks.f.f.silence $i.dd.cks.f.f.agc $i.dd.cks.f.f.loop $i.dd.cks.f.f.suppress -side top -anchor w
1843
1844# Codecs pane #################################################################
1845set i .prefs.pane.codecs
1846frame $i 
1847frame $i.of -relief sunken
1848pack  $i.of -fill both -expand 1 -anchor w -pady 1
1849
1850label $i.of.l -height 2 -width 40 -justify left -text "This panel shows the available codecs, their properties and allows\n their RTP payload types to be re-mapped." 
1851pack $i.of.l -side top -fill x
1852
1853frame   $i.of.codecs
1854
1855pack    $i.of.codecs -side left -padx 2 -fill y
1856label   $i.of.codecs.l    -text "Codec" -relief raised
1857listbox $i.of.codecs.lb -width 20 -yscrollcommand "$i.of.codecs.scroll set"
1858scrollbar $i.of.codecs.scroll -command "$i.of.codecs.lb yview"
1859pack    $i.of.codecs.l -side top -fill x
1860pack    $i.of.codecs.scroll $i.of.codecs.lb -side left -fill both
1861
1862frame   $i.of.details
1863pack    $i.of.details -side left -fill both -expand 1
1864
1865frame $i.of.details.upper
1866pack $i.of.details.upper -fill x
1867
1868frame $i.of.details.desc
1869pack $i.of.details.desc -side top -fill x
1870
1871frame $i.of.details.pt
1872pack $i.of.details.pt -side bottom -fill x -anchor s
1873label $i.of.details.pt.l -anchor w -text "RTP payload:"
1874pack  $i.of.details.pt.l -side left -anchor w
1875
1876entry $i.of.details.pt.e -width 4
1877pack  $i.of.details.pt.e -side left -padx 4
1878
1879button $i.of.details.pt.b -text "Map Codec" -command map_codec
1880pack  $i.of.details.pt.b -side left -padx 4
1881
1882label $i.of.details.upper.l0 -text "Details" -relief raised
1883pack $i.of.details.upper.l0 -side top -fill x -expand 1
1884
1885frame $i.of.details.upper.l
1886pack $i.of.details.upper.l -side left
1887label $i.of.details.upper.l.0 -text "Short name:"  -anchor w
1888label $i.of.details.upper.l.1 -text "Sample Rate (Hz):" -anchor w
1889label $i.of.details.upper.l.2 -text "Channels:"    -anchor w
1890label $i.of.details.upper.l.3 -text "Bitrate (kbps):"     -anchor w
1891label $i.of.details.upper.l.4 -text "RTP Payload:" -anchor w
1892label $i.of.details.upper.l.5 -text "Capability:" -anchor w
1893label $i.of.details.upper.l.6 -text "Layers:" -anchor w
1894
1895for {set idx 0} {$idx < 7} {incr idx} {
1896    pack $i.of.details.upper.l.$idx -side top -fill x
1897}
1898
1899frame $i.of.details.upper.r
1900pack $i.of.details.upper.r -side left -fill x -expand 1
1901label $i.of.details.upper.r.0 -anchor w
1902label $i.of.details.upper.r.1 -anchor w
1903label $i.of.details.upper.r.2 -anchor w
1904label $i.of.details.upper.r.3 -anchor w
1905label $i.of.details.upper.r.4 -anchor w
1906label $i.of.details.upper.r.5 -anchor w
1907label $i.of.details.upper.r.6 -anchor w
1908
1909for {set idx 0} {$idx < 7} {incr idx} {
1910    pack $i.of.details.upper.r.$idx -side top -fill x
1911}
1912
1913set descw [expr [averageCharacterWidth $infofont] * 30]
1914label $i.of.details.desc.l -text "Description:" -anchor w -wraplength $descw -justify left
1915pack $i.of.details.desc.l -side left -fill x
1916unset descw
1917
1918bind $i.of.codecs.lb <1> {
1919    codecs_panel_select [%W index @%x,%y]
1920}
1921
1922bind $i.of.codecs.lb <ButtonRelease-1> {
1923    codecs_panel_select [%W index @%x,%y]
1924}
1925proc codecs_panel_fill {} {
1926    global codecs
1927
1928    .prefs.pane.codecs.of.codecs.lb delete 0 end
1929
1930    foreach {c} $codecs {
1931        .prefs.pane.codecs.of.codecs.lb insert end $c
1932    }
1933}
1934
1935set last_selected_codec -1
1936
1937proc codecs_panel_select { idx } {
1938    global codecs codec_nick_name codec_rate codec_channels codec_pt codec_block_size codec_data_size codec_desc codec_caps codec_layers
1939    global last_selected_codec
1940
1941    set last_selected_codec $idx
1942
1943    set codec [lindex $codecs $idx]
1944    set root  .prefs.pane.codecs.of.details.upper.r
1945    $root.0 configure -text $codec_nick_name($codec)
1946    $root.1 configure -text $codec_rate($codec)
1947    $root.2 configure -text $codec_channels($codec)
1948
1949    set fps [expr $codec_rate($codec) * 2 * $codec_channels($codec) / $codec_block_size($codec) ]
1950    set kbps [expr 8 * $fps * $codec_data_size($codec) / 1000.0]
1951    $root.3 configure -text [format "%.1f" $kbps]
1952
1953    $root.4 configure -text $codec_pt($codec)
1954    $root.5 configure -text $codec_caps($codec)
1955    $root.6 configure -text $codec_layers($codec)
1956   
1957    .prefs.pane.codecs.of.details.desc.l configure -text "Description: $codec_desc($codec)"
1958
1959}
1960
1961proc map_codec {} {
1962    global codecs last_selected_codec
1963
1964    set idx $last_selected_codec
1965
1966    if {$last_selected_codec == -1} {
1967        return
1968    }
1969
1970    set pt [.prefs.pane.codecs.of.details.pt.e get]
1971    .prefs.pane.codecs.of.details.pt.e delete 0 end
1972
1973    set ptnot [string trim $pt 1234567890]
1974    if {$ptnot != ""} {
1975        return
1976    }
1977
1978    set codec [lindex $codecs $idx]
1979
1980    mbus_send "R" "tool.rat.payload.set" "[mbus_encode_str $codec] $pt"
1981    after 1000 codecs_panel_select $idx
1982}
1983
1984# Security Pane ###############################################################
1985set i .prefs.pane.security
1986frame $i 
1987frame $i.a -relief sunken
1988frame $i.a.f
1989frame $i.a.f.f
1990label $i.a.f.f.l -anchor w -justify left -text "Your communication can be secured with\nDES encryption.  Only conference participants\nwith the same key can receive audio data when\nencryption is enabled."
1991pack $i.a.f.f.l
1992pack $i.a -side top -fill both -expand 1
1993label $i.a.f.f.lbl -text "Key:"
1994entry $i.a.f.f.e -width 28 -textvariable key
1995checkbutton $i.a.f.f.cb -text "Enabled" -variable key_var
1996pack $i.a.f -fill x -side left -expand 1
1997pack $i.a.f.f
1998pack $i.a.f.f.lbl $i.a.f.f.e $i.a.f.f.cb -side left -pady 4 -padx 2 -fill x
1999
2000# Interface Pane ##############################################################
2001set i .prefs.pane.interface
2002frame $i 
2003frame $i.a -relief sunken
2004frame $i.a.f
2005frame $i.a.f.f
2006label $i.a.f.f.l -anchor w -justify left -text "The following features may be\ndisabled to conserve processing\npower."
2007pack $i.a -side top -fill both -expand 1
2008pack $i.a.f -fill x -side left -expand 1
2009checkbutton $i.a.f.f.power   -text "Powermeters active"       -variable meter_var
2010checkbutton $i.a.f.f.video   -text "Video synchronization"    -variable sync_var
2011checkbutton $i.a.f.f.balloon -text "Balloon help"             -variable help_on
2012checkbutton $i.a.f.f.matrix  -text "Reception quality matrix" -variable matrix_on -command chart_show
2013checkbutton $i.a.f.f.plist   -text "Participant list"         -variable plist_on  -command toggle_plist
2014checkbutton $i.a.f.f.fwin    -text "File Control Window"      -variable files_on  -command file_show
2015pack $i.a.f.f $i.a.f.f.l
2016pack $i.a.f.f.power $i.a.f.f.video $i.a.f.f.balloon $i.a.f.f.matrix $i.a.f.f.plist $i.a.f.f.fwin -side top -anchor w
2017
2018proc set_pane {p base desc} {
2019    upvar 1 $p pane
2020    set tpane [string tolower [lindex [split $pane] 0]]
2021    pack forget $base.$tpane
2022    set tpane [string tolower [lindex [split $desc] 0]]
2023    pack $base.$tpane -fill both -expand 1 -padx 2 -pady 2
2024    set pane $desc
2025}
2026
2027# Initialise "About..." toplevel window
2028toplevel   .about
2029frame      .about.rim -relief sunken
2030frame      .about.m
2031frame      .about.rim.d
2032pack .about.m -fill x
2033pack .about.rim -padx 4 -pady 2 -side top -fill both -expand 1
2034pack .about.rim.d -side top -fill both -expand 1
2035
2036frame .about.m.f
2037label .about.m.f.l -text "Category:"
2038menubutton .about.m.f.mb -menu .about.m.f.mb.menu -indicatoron 1 -textvariable about_pane -relief raised -width 10
2039menu .about.m.f.mb.menu -tearoff 0
2040.about.m.f.mb.menu add command -label "Credits"   -command {set_pane about_pane .about.rim.d "Credits"   }
2041.about.m.f.mb.menu add command -label "Feedback"  -command {set_pane about_pane .about.rim.d "Feedback"  }
2042.about.m.f.mb.menu add command -label "Copyright" -command {set_pane about_pane .about.rim.d "Copyright" }
2043
2044pack .about.m.f
2045pack .about.m.f.l .about.m.f.mb -side left
2046
2047#label     .about.rim.t.d.logo  -highlightthickness 0 -bitmap rat_med
2048
2049frame     .about.rim.d.copyright
2050frame     .about.rim.d.copyright.f
2051frame     .about.rim.d.copyright.f.f
2052text      .about.rim.d.copyright.f.f.blurb -height 14 -yscrollcommand ".about.rim.d.copyright.f.f.scroll set"
2053scrollbar .about.rim.d.copyright.f.f.scroll -command ".about.rim.d.copyright.f.f.blurb yview"
2054
2055pack      .about.rim.d.copyright.f -expand 1 -fill both
2056pack      .about.rim.d.copyright.f.f
2057pack      .about.rim.d.copyright.f.f.scroll -side right -fill y -expand 1
2058pack      .about.rim.d.copyright.f.f.blurb -side left -expand 1
2059
2060frame     .about.rim.d.credits
2061frame     .about.rim.d.credits.f -relief sunken
2062frame     .about.rim.d.credits.f.f
2063pack      .about.rim.d.credits.f -fill both -expand 1
2064pack      .about.rim.d.credits.f.f -side left -fill x -expand 1
2065label     .about.rim.d.credits.f.f.1                  -text "The Robust-Audio Tool was developed in the Department of\nComputer Science, University College London.\n\nProject Supervision:"
2066label     .about.rim.d.credits.f.f.2 -foreground blue -text Good
2067label     .about.rim.d.credits.f.f.3                  -text "\nDevelopment Team:"
2068label     .about.rim.d.credits.f.f.4 -foreground blue -text Bad
2069label     .about.rim.d.credits.f.f.5                  -text "Additional Contributions:"
2070label     .about.rim.d.credits.f.f.6 -foreground blue -text Ugly
2071for {set i 1} {$i<=6} {incr i} {
2072    pack  .about.rim.d.credits.f.f.$i -side top -fill x
2073}
2074
2075button    .about.dismiss -text Dismiss -command "wm withdraw .about"
2076pack      .about.dismiss -side bottom -anchor e -padx 2 -pady 2
2077
2078frame     .about.rim.d.feedback
2079frame     .about.rim.d.feedback.f -relief sunken
2080frame     .about.rim.d.feedback.f.f
2081pack      .about.rim.d.feedback.f -fill both -expand 1
2082pack      .about.rim.d.feedback.f.f -side left -fill x -expand 1
2083label     .about.rim.d.feedback.f.f.1                  -text "Comments, suggestions, and bug-reports should be sent to:"
2084label     .about.rim.d.feedback.f.f.2 -foreground blue -text "rat-trap@cs.ucl.ac.uk\n"
2085label     .about.rim.d.feedback.f.f.3                  -text "Further information is available on the world-wide web at:"
2086label     .about.rim.d.feedback.f.f.4 -foreground blue -text "http://www-mice.cs.ucl.ac.uk/multimedia/software/rat/\n"
2087for {set i 1} {$i<=4} {incr i} {
2088    pack  .about.rim.d.feedback.f.f.$i -side top -fill x
2089}
2090#pack .about.rim.t.logo .about.rim.t.blurb .about.rim.t.scroll -side right -fill y
2091
2092wm withdraw  .about
2093wm title     .about "About RAT"
2094wm resizable .about 0 0
2095set about_pane Copyright
2096set_pane about_pane .about.rim.d "Credits" 
2097constrain_window .about $infofont 64 25
2098
2099.about.rim.d.copyright.f.f.blurb insert end {
2100Copyright (C) 1995-1999 University College London
2101All rights reserved.
2102
2103Redistribution and use in source and binary forms, with or without
2104modification, is permitted, provided that the following conditions
2105are met:
2106
21071. Redistributions of source code must retain the above
2108   copyright notice, this list of conditions and the
2109   following disclaimer.
2110
21112. Redistributions in binary form must reproduce the above
2112   copyright notice, this list of conditions and the
2113   following disclaimer in the documentation and/or other
2114   materials provided with the distribution.
2115
21163. All advertising materials mentioning features or use of
2117   this software must display the following acknowledgement:
2118
2119     "This product includes software developed by the
2120     Computer Science Department at University College
2121     London."
2122
21234. Neither the name of the University nor of the Department
2124   may be used to endorse or promote products derived from
2125   this software without specific prior written permission.
2126
2127THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
2128"AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
2129BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
2130AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
2131SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
2132INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2133DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2134SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
2135OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
2136LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2137(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
2138THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
2139OF SUCH DAMAGE.
2140
2141This software is derived, in part, from publically available
2142source code with the following copyright:
2143
2144Copyright (C) 1991-1993 Regents of the University of California
2145Copyright (C) 1992 Stichting Mathematisch Centrum, Amsterdam
2146Copyright (C) 1991-1992 RSA Data Security, Inc
2147Copyright (C) 1992 Jutta Degener and Carsten Bormann, TU Berlin
2148Copyright (C) 1994 Paul Stewart
2149Copyright (C) 1996 Regents of the University of California
2150
2151This product includes software developed by the Computer
2152Systems Engineering Group and by the Network Research Group
2153at Lawrence Berkeley Laboratory.
2154
2155The WB-ADPCM algorithm was developed by British Telecommunications
2156plc.  Permission has been granted to use it for non-commercial
2157research and development projects.  BT retain the intellectual
2158property rights to this algorithm.
2159
2160Encryption features of this software use the RSA Data
2161Security, Inc. MD5 Message-Digest Algorithm.
2162}
2163
2164proc shuffle_rats {args} {
2165    # This should really animate the movement and play fruit-machine sounds.... :-)
2166    set r ""
2167    set end [llength $args]
2168    set l 0
2169    while { $l < $end } {
2170        set toget [expr abs([clock clicks]) % [llength $args]]
2171        set r [format "%s%s  " $r [lindex $args $toget]]
2172        set args [lreplace $args $toget $toget]
2173        lappend used $toget
2174        if {$l >0 && [expr ($l + 1) % 3] == 0} {
2175            set r "$r\n"
2176        }
2177        incr l
2178    }
2179    return $r
2180}
2181
2182proc jiggle_credits {} {
2183# Software really developed by the Socialist Department of Computer Science
2184    .about.rim.d.credits.f.f.2 configure -text [shuffle_rats "Angela Sasse" "Vicky Hardman"]
2185    .about.rim.d.credits.f.f.4 configure -text [shuffle_rats "Colin Perkins" "Orion Hodson"]
2186    .about.rim.d.credits.f.f.6 configure -text [shuffle_rats "Isidor Kouvelas" "Darren Harris" "Anna Watson" "Mark Handley" "Jon Crowcroft" "Marcus Iken" "Kris Hasler" "Tristan Henderson"]
2187}
2188
2189proc sync_ui_to_engine {} {
2190    # the next time the display is shown, it needs to reflect the
2191    # state of the audio engine.
2192    mbus_send "R" "tool.rat.settings" ""
2193}
2194
2195proc sync_engine_to_ui {} {
2196    # make audio engine concur with ui
2197    global my_ssrc rtcp_name rtcp_email rtcp_phone rtcp_loc
2198    global prenc upp channel_var secenc layerenc red_off int_gap int_units
2199    global silence_var agc_var audio_loop_var echo_var
2200    global repair_var limit_var min_var max_var lecture_var 3d_audio_var convert_var 
2201    global meter_var sync_var gain volume input_port output_port
2202    global in_mute_var out_mute_var ichannels freq key key_var
2203    global audio_device
2204
2205    #rtcp details
2206    mbus_send "R" "rtp.source.name"  "[mbus_encode_str $my_ssrc] [mbus_encode_str $rtcp_name]"
2207    mbus_send "R" "rtp.source.email" "[mbus_encode_str $my_ssrc] [mbus_encode_str $rtcp_email]"
2208    mbus_send "R" "rtp.source.phone" "[mbus_encode_str $my_ssrc] [mbus_encode_str $rtcp_phone]"
2209    mbus_send "R" "rtp.source.loc"   "[mbus_encode_str $my_ssrc] [mbus_encode_str $rtcp_loc]"
2210   
2211    #transmission details
2212    mbus_send "R" "tool.rat.codec"      "[mbus_encode_str $prenc] [mbus_encode_str $ichannels] [mbus_encode_str $freq]"
2213    mbus_send "R" "tool.rat.rate"         $upp
2214
2215    switch $channel_var {
2216        none         {mbus_send "R" "audio.channel.coding" "[mbus_encode_str $channel_var]"}
2217        redundancy   {mbus_send "R" "audio.channel.coding" "[mbus_encode_str $channel_var] [mbus_encode_str $secenc] $red_off"}
2218        interleaved {mbus_send "R" "audio.channel.coding" "[mbus_encode_str $channel_var] $int_gap $int_units"}
2219        layering        {mbus_send "R" "audio.channel.coding" "[mbus_encode_str $channel_var] [mbus_encode_str $prenc] [mbus_encode_str $ichannels] [mbus_encode_str $freq] $layerenc"}
2220        *           {error "unknown channel coding scheme $channel_var"}
2221    }
2222
2223    mbus_send "R" "tool.rat.silence"       $silence_var
2224    mbus_send "R" "tool.rat.agc"           $agc_var
2225    mbus_send "R" "tool.rat.loopback"      $audio_loop_var
2226    mbus_send "R" "tool.rat.echo.suppress" $echo_var
2227
2228    #Reception Options
2229    mbus_send "R" "audio.channel.repair"   [mbus_encode_str $repair_var]
2230    mbus_send "R" "tool.rat.playout.limit" $limit_var
2231    mbus_send "R" "tool.rat.playout.min"   $min_var
2232    mbus_send "R" "tool.rat.playout.max"   $max_var
2233    mbus_send "R" "tool.rat.lecture"       $lecture_var
2234    mbus_send "R" "tool.rat.3d.enabled"    $3d_audio_var
2235    mbus_send "R" "tool.rat.converter"     [mbus_encode_str $convert_var]
2236
2237    #Security
2238    if {$key_var==1 && [string length $key]!=0} {
2239        mbus_send "R" "security.encryption.key" [mbus_encode_str $key]
2240    } else {
2241        mbus_send "R" "security.encryption.key" [mbus_encode_str ""]
2242    }
2243
2244    #Interface
2245    mbus_send "R" "tool.rat.powermeter"   $meter_var
2246    mbus_send "R" "tool.rat.sync"         $sync_var
2247
2248    #device
2249    mbus_send "R" "audio.device"        [mbus_encode_str "$audio_device"]
2250    mbus_send "R" "audio.input.gain"    $gain
2251    mbus_send "R" "audio.output.gain"   $volume
2252    mbus_send "R" "audio.input.port"    [mbus_encode_str $input_port]
2253    mbus_send "R" "audio.output.port"   [mbus_encode_str $output_port]
2254    mbus_send "R" "audio.input.mute"    $in_mute_var
2255    mbus_send "R" "audio.output.mute"   $out_mute_var
2256}
2257
2258#
2259# Routines to display the "chart" of RTCP RR statistics...
2260#
2261
2262toplevel  .chart
2263canvas    .chart.c  -xscrollcommand {.chart.sb set} -yscrollcommand {.chart.sr set}
2264frame     .chart.c.f
2265scrollbar .chart.sr -orient vertical   -command {.chart.c yview}
2266scrollbar .chart.sb -orient horizontal -command {.chart.c xview}
2267
2268pack .chart.sb -side bottom -fill x    -expand 0 -anchor s
2269pack .chart.sr -side right  -fill y    -expand 0 -anchor e
2270pack .chart.c  -side left   -fill both -expand 1 -anchor n
2271
2272.chart.c create window 0 0 -anchor nw -window .chart.c.f
2273
2274proc mtrace_callback {fd src dst} {
2275        if [winfo exists .mtrace-$src-$dst] {
2276                .mtrace-$src-$dst.t insert end [read $fd]
2277        } else {
2278                # The user has closed the mtrace window before the trace has completed
2279                close $fd
2280        }
2281        if [eof $fd] {
2282                close $fd
2283        }
2284}
2285
2286proc mtrace {src dst} {
2287        global CNAME group_addr
2288        regsub {.*@([0-9]+.[0-9]+.[0-9]+.[0-9]+)} $CNAME($src) {\1} src_addr
2289        regsub {.*@([0-9]+.[0-9]+.[0-9]+.[0-9]+)} $CNAME($dst) {\1} dst_addr
2290
2291        toplevel  .mtrace-$src-$dst
2292        text      .mtrace-$src-$dst.t -background white -font "Courier 8" -wrap none \
2293                                      -yscrollcommand ".mtrace-$src-$dst.sr set" \
2294                                      -xscrollcommand ".mtrace-$src-$dst.sb set"
2295        scrollbar .mtrace-$src-$dst.sb -command ".mtrace-$src-$dst.t xview" -orient horizontal
2296        scrollbar .mtrace-$src-$dst.sr -command ".mtrace-$src-$dst.t yview" -orient vertical
2297        pack .mtrace-$src-$dst.sb -fill x    -expand 0 -side bottom -anchor s
2298        pack .mtrace-$src-$dst.sr -fill y    -expand 0 -side right  -anchor e
2299        pack .mtrace-$src-$dst.t  -fill both -expand 1 -side left   -anchor w
2300
2301        wm title .mtrace-$src-$dst "mtrace from $src_addr to $dst_addr via group $group_addr"
2302
2303        set fd [open "|mtrace $src_addr $dst_addr $group_addr" "r"]
2304        fconfigure $fd -blocking 0
2305        fileevent  $fd readable "mtrace_callback $fd $src $dst"
2306}
2307
2308toplevel .chart_popup       -bg black
2309label    .chart_popup.text  -bg lavender -justify left
2310pack .chart_popup.text -side top -anchor w -fill x
2311wm transient        .chart_popup .
2312wm withdraw         .chart_popup
2313wm overrideredirect .chart_popup true
2314
2315proc chart_popup_show {window} {
2316        global chart_popup_src chart_popup_dst chart_popup_id NAME
2317        .chart_popup.text  configure -text "From: $NAME($chart_popup_src($window))\nTo: $NAME($chart_popup_dst($window))"
2318        # Beware! Don't put the popup under the cursor! Else we get window enter
2319        # for .help and leave for $window, making us hide_help which removes the
2320        # help window, giving us a window enter for $window making us popup the
2321        # help again.....
2322        if {[winfo width $window] > [winfo height $window]} {
2323            set xpos [expr [winfo pointerx $window] + 10]
2324            set ypos [expr [winfo rooty    $window] + [winfo height $window] + 4]
2325        } else {
2326            set xpos [expr [winfo rootx    $window] + [winfo width $window] + 4]
2327            set ypos [expr [winfo pointery $window] + 10]
2328        }
2329       
2330        wm geometry  .chart_popup +$xpos+$ypos
2331        set chart_popup_id [after 100 wm deiconify .chart_popup]
2332        raise .chart_popup $window
2333}
2334
2335proc chart_popup_hide {window} {
2336        global chart_popup_id
2337        if {[info exists chart_popup_id]} {
2338                after cancel $chart_popup_id
2339        }
2340        wm withdraw .chart_popup
2341}
2342
2343proc chart_popup_add {window src dst} {
2344        global chart_popup_src chart_popup_dst
2345        set chart_popup_src($window) $src
2346        set chart_popup_dst($window) $dst
2347        bind $window <Enter>    "+chart_popup_show $window"
2348        bind $window <Leave>    "+chart_popup_hide $window"
2349}
2350
2351proc chart_add {ssrc} {
2352        global NAME
2353        frame  .chart.c.f.$ssrc
2354        frame  .chart.c.f.$ssrc.f
2355        button .chart.c.f.$ssrc.l -width 25 -text $ssrc -padx 0 -pady 0 -anchor w -relief flat -command "toggle_stats $ssrc"
2356        pack   .chart.c.f.$ssrc.f -expand 0 -anchor e -side right
2357        pack   .chart.c.f.$ssrc.l -expand 1 -anchor w -fill x -side left
2358        pack   .chart.c.f.$ssrc   -expand 1 -anchor n -fill x -side top
2359        foreach s [pack slaves .chart.c.f] {
2360                regsub {.chart.c.f.(.*)} $s {\1} s
2361                button .chart.c.f.$s.f.$ssrc -width 4 -text "" -background white -padx 0 -pady 0 -command "mtrace $s $ssrc"
2362                pack   .chart.c.f.$s.f.$ssrc -expand 0 -side left
2363                chart_popup_add .chart.c.f.$s.f.$ssrc $s $ssrc
2364                if {![winfo exists .chart.c.f.$ssrc.f.$s]} {
2365                        button .chart.c.f.$ssrc.f.$s -width 4 -text "" -background white -padx 0 -pady 0 -command "mtrace $ssrc $s"
2366                        pack   .chart.c.f.$ssrc.f.$s -expand 0 -side left
2367                        chart_popup_add .chart.c.f.$ssrc.f.$s $ssrc $s
2368                }
2369        }
2370        update
2371        .chart.c configure -scrollregion "0.0 0.0 [winfo width .chart.c.f] [winfo height .chart.c.f]"
2372}
2373
2374proc chart_remove {ssrc} {
2375        destroy .chart.c.f.$ssrc
2376        foreach s [pack slaves .chart.c.f] {
2377                regsub {.chart.c.f.(.*)} $s {\1} s
2378                destroy .chart.c.f.$s.f.$ssrc
2379        }
2380        update
2381        .chart.c configure -scrollregion "0.0 0.0 [winfo width .chart.c.f] [winfo height .chart.c.f]"
2382}
2383
2384proc chart_label {ssrc label} {
2385        .chart.c.f.$ssrc.l configure -text $label
2386}
2387
2388proc chart_set {src dst val} {
2389        if {$val < 5} {
2390                set colour green
2391                set txtval "$val%"
2392        } elseif {$val < 10} {
2393                set colour orange
2394                set txtval "$val%"
2395        } elseif {$val <= 100} {
2396                set colour red
2397                set txtval "$val%"
2398        } else {
2399                set colour white
2400                set txtval { }
2401        }
2402        .chart.c.f.$src.f.$dst configure -background $colour -text "$txtval"
2403}
2404
2405proc chart_show {} {
2406        global matrix_on
2407        if {$matrix_on} {
2408                wm deiconify .chart
2409        } else {
2410                wm withdraw .chart
2411        }
2412}
2413
2414wm withdraw .chart
2415wm title    .chart "Reception quality matrix"
2416wm geometry .chart 320x200
2417wm protocol .chart WM_DELETE_WINDOW    {set matrix_on 0; chart_show}
2418
2419chart_show
2420
2421#
2422# End of RTCP RR chart routines
2423#
2424
2425#
2426# File Control Window
2427#
2428
2429set play_file(state) end
2430set rec_file(state) end
2431
2432catch {
2433    toplevel .file
2434    wm protocol .file WM_DELETE_WINDOW {set files_on 0; file_show}
2435    frame .file.play -relief ridge
2436    frame .file.rec  -relief ridge
2437    pack  .file.play -side top -pady 2 -padx 2 -fill x -expand 1
2438    pack  .file.rec  -side top -pady 2 -padx 2 -fill x -expand 1
2439   
2440    label .file.play.l -text "Playback"
2441    pack  .file.play.l -side top -fill x
2442    label .file.rec.l -text "Record"
2443    pack  .file.rec.l -side top -fill x
2444
2445    button .file.dismiss -text Dismiss -command "set files_on 0; file_show"
2446    pack   .file.dismiss -side bottom -anchor e -padx 2 -pady 2
2447   
2448    wm withdraw .file
2449    wm title    .file "RAT File Control"
2450   
2451    foreach action { play rec } {
2452        frame  .file.$action.buttons
2453        pack   .file.$action.buttons
2454        button .file.$action.buttons.disk -bitmap disk -command "fileDialog $action"
2455        pack   .file.$action.buttons.disk -side left -padx 2 -pady 2 -anchor n
2456       
2457        foreach cmd "$action pause stop" {
2458            button .file.$action.buttons.$cmd -bitmap $cmd -state disabled -command file_$action\_$cmd
2459            pack   .file.$action.buttons.$cmd -side left -padx 2 -pady 2 -anchor n -fill x
2460        }
2461
2462        label  .file.$action.buttons.status -text "No file selected." -relief sunk -width 16 -anchor w
2463        pack   .file.$action.buttons.status -side bottom -fill both -expand 1 -padx 2 -pady 2
2464    }
2465} fwinerr
2466
2467if {$fwinerr != {}} {
2468        puts stderr $fwinerr
2469}
2470
2471proc fileDialog {cmdbox} {
2472    global win32 tcl_platform
2473   
2474        set defaultExtension au
2475        set defaultLocation  .
2476
2477    switch -glob $tcl_platform(os) {
2478        SunOS    {
2479                if [file exists /usr/demo/SOUND/sounds] { set defaultLocation /usr/demo/SOUND/sounds }
2480                }
2481        Windows* {
2482                if [file exists C:/Windows/Media]       { set defaultLocation C:/Windows/Media }
2483                set defaultExtension wav
2484                }
2485        }
2486   
2487    set types {
2488                {"NeXT/Sun Audio files" "au"}
2489                {"Microsoft RIFF files" "wav"}
2490                {"All files"            "*"}
2491    }
2492   
2493    if {![string compare $cmdbox "play"]} {
2494                catch { asFileBox .playfilebox  -defaultextension $defaultExtension -command file_open_$cmdbox -directory $defaultLocation -extensions $types } asferror
2495    } else {
2496                catch { asFileBox .recfilebox   -defaultextension $defaultExtension -command file_open_$cmdbox  -extensions $types -force_extension 1 } asferror
2497    }
2498       
2499        if {$asferror != ""} {
2500                puts stderr asferror
2501        }
2502}
2503
2504proc file_show {} {
2505    global files_on
2506   
2507    if {$files_on} {
2508                wm deiconify .file
2509    } else {
2510                wm withdraw .file
2511    }
2512}
2513
2514proc file_play_live {} {
2515# Request heart beat to determine if file is valid
2516        mbus_send "R" audio.file.play.live ""
2517}
2518
2519proc file_rec_live {} {
2520# Request heart beat to determine if file is valid
2521        mbus_send "R" audio.file.record.live ""
2522}
2523
2524proc file_open_play {path} {
2525    global play_file
2526
2527    mbus_send "R" "audio.file.play.open" [mbus_encode_str $path]
2528    mbus_send "R" "audio.file.play.pause" 1
2529    set play_file(state) paused
2530    set play_file(name) $path
2531   
2532    # Test whether file is still playing/valid
2533    after 200 file_play_live
2534}
2535
2536proc file_open_rec {path} {
2537    global rec_file
2538
2539    mbus_send "R" "audio.file.record.open" [mbus_encode_str $path]
2540    mbus_send "R" "audio.file.record.pause" 1
2541
2542    set rec_file(state) paused
2543    set rec_file(name)  $path
2544
2545    # Test whether file is still recording/valid
2546    after 200 file_rec_live
2547}
2548
2549proc file_enable_play { } {
2550    .file.play.buttons.play   configure -state normal
2551    .file.play.buttons.pause  configure -state disabled
2552    .file.play.buttons.stop   configure -state disabled
2553    .file.play.buttons.status configure -text "Ready to play."
2554}       
2555
2556proc file_enable_record { } {
2557    .file.rec.buttons.rec configure -state normal
2558    .file.rec.buttons.pause  configure -state disabled
2559    .file.rec.buttons.stop   configure -state disabled
2560    .file.rec.buttons.status configure -text "Ready to record."
2561}
2562
2563proc file_play_play {} {
2564        global play_file
2565       
2566        catch {
2567                puts stderr $play_file(state)
2568                if {$play_file(state) == "paused"} {
2569                        mbus_send "R" "audio.file.play.pause" 0
2570                        puts stderr "unpaused"
2571                } else {
2572                        mbus_send "R" "audio.file.play.open" [mbus_encode_str $play_file(name)]
2573                        puts stderr "re-opening"
2574                }
2575                set play_file(state) play
2576        } pferr
2577
2578        if { $pferr != "play" } { puts stderr "pferr: $pferr" }
2579
2580        .file.play.buttons.play   configure -state disabled
2581        .file.play.buttons.pause  configure -state normal
2582        .file.play.buttons.stop   configure -state normal
2583        .file.play.buttons.status configure -text "Playing."
2584        after 200 file_play_live
2585}
2586
2587proc file_play_pause {} {
2588    global play_file
2589   
2590    .file.play.buttons.play   configure -state normal
2591    .file.play.buttons.pause  configure -state disabled
2592    .file.play.buttons.stop   configure -state normal
2593    .file.play.buttons.status configure -text "Paused."
2594    set play_file(state) paused
2595    mbus_send "R" "audio.file.play.pause" 1
2596}
2597
2598proc file_play_stop {} {
2599        global play_file
2600       
2601        set play_file(state) end
2602        file_enable_play
2603        mbus_send "R" "audio.file.play.stop" ""
2604}
2605
2606proc file_rec_rec {} {
2607        global rec_file
2608       
2609        catch {
2610                puts stderr $rec_file(state)
2611                if {$rec_file(state) == "paused"} {
2612                        mbus_send "R" "audio.file.record.pause" 0
2613                } else {
2614                        mbus_send "R" "audio.file.record.open" [mbus_encode_str $rec_file(name)]
2615                }
2616                set rec_file(state) record
2617        } prerr
2618
2619        if { $prerr != "record" } { puts stderr "prerr: $prerr" }
2620
2621        .file.rec.buttons.rec    configure -state disabled
2622        .file.rec.buttons.pause  configure -state normal
2623        .file.rec.buttons.stop   configure -state normal
2624        .file.rec.buttons.status configure -text "Recording."
2625        after 200 file_rec_live
2626}
2627
2628proc file_rec_pause {} {
2629    global rec_file
2630   
2631    .file.rec.buttons.rec    configure -state normal
2632    .file.rec.buttons.pause  configure -state disabled
2633    .file.rec.buttons.stop   configure -state normal
2634    .file.rec.buttons.status configure -text "Paused."
2635    set rec_file(state) paused
2636    mbus_send "R" "audio.file.record.pause" 1
2637}
2638
2639proc file_rec_stop {} {
2640        global rec_file
2641       
2642        set rec_file(state) end
2643        file_enable_record
2644        mbus_send "R" "audio.file.record.stop" ""
2645}
2646
2647#
2648# End of File Window routines
2649#
2650
2651proc show_help {window} {
2652        global help_text help_on help_id
2653        if {$help_on} {
2654                .help.text  configure -text $help_text($window)
2655                # Beware! Don't put the popup under the cursor! Else we get window enter
2656                # for .help and leave for $window, making us hide_help which removes the
2657                # help window, giving us a window enter for $window making us popup the
2658                # help again.....
2659                if {[winfo width $window] > [winfo height $window]} {
2660                    set xpos [expr [winfo pointerx $window] + 10]
2661                    set ypos [expr [winfo rooty    $window] + [winfo height $window] + 4]
2662                } else {
2663                    set xpos [expr [winfo rootx    $window] + [winfo width $window] + 4]
2664                    set ypos [expr [winfo pointery $window] + 10]
2665                }
2666               
2667                wm geometry  .help +$xpos+$ypos
2668                set help_id [after 100 wm deiconify .help]
2669                raise .help $window
2670        }
2671}
2672
2673proc hide_help {window} {
2674        global help_id help_on
2675        if {[info exists help_id]} {
2676                after cancel $help_id
2677        }
2678        wm withdraw .help
2679}
2680
2681proc add_help {window text} {
2682        global help_text
2683        set help_text($window)  $text
2684        bind $window <Enter>    "+show_help $window"
2685        bind $window <Leave>    "+hide_help $window"
2686}
2687
2688bind Entry   <KeyPress> "+hide_help %W"
2689toplevel .help       -bg black
2690label    .help.text  -bg lavender -justify left
2691pack .help.text -side top -anchor w -fill x
2692wm transient        .help .
2693wm withdraw         .help
2694wm overrideredirect .help true
2695
2696add_help .r.c.gain.gra.s2       "This slider controls the volume\nof the sound you send."
2697add_help .r.c.gain.but.l2       "Click to change input device."
2698add_help .r.c.gain.but.t2       "If this button is pushed in, you are are transmitting, and\nmay be\
2699                         heard by other participants. Holding down the\nright mouse button in\
2700                         any RAT window will temporarily\ntoggle the state of this button,\
2701                         allowing for easy\npush-to-talk operation."
2702add_help .r.c.gain.gra.b2       "Indicates the loudness of the\nsound you are sending. If this\nis\
2703                         moving, you may be heard by\nthe other participants."
2704
2705add_help .r.c.vol.gra.s1        "This slider controls the volume\nof the sound you hear."
2706add_help .r.c.vol.but.l1        "Click to change output device."
2707add_help .r.c.vol.but.t1        "If pushed in, reception is muted."
2708add_help .r.c.vol.gra.b1        "Indicates the loudness of the\nsound you are hearing."
2709
2710add_help .l.f           "Name of the session, and the IP address, port\n&\
2711                         TTL used to transmit the audio data."
2712add_help .l.t           "The participants in this session with you at the top.\nClick on a name\
2713                         with the left mouse button to display\ninformation on that participant,\
2714                         and with the middle\nbutton to mute that participant (the right button\nwill\
2715                         toggle the transmission mute button, as usual)."
2716
2717add_help .st.opts       "Brings up another window allowing\nthe control of various options."
2718add_help .st.about      "Brings up another window displaying\ncopyright & author information."
2719add_help .st.quit       "Press to leave the session."
2720
2721# preferences help
2722add_help .prefs.m.f.m  "Click here to change the preference\ncategory."
2723set i .prefs.buttons
2724add_help $i.bye         "Cancel changes."
2725add_help $i.apply       "Apply changes."
2726#add_help $i.save        "Save and apply changes."
2727
2728# user help
2729set i .prefs.pane.personal.a.f.f.ents
2730add_help $i.name        "Enter your name for transmission\nto other participants."
2731add_help $i.email       "Enter your email address for transmission\nto other participants."
2732add_help $i.phone       "Enter your phone number for transmission\nto other participants."
2733add_help $i.loc         "Enter your location for transmission\nto other participants."
2734
2735#audio help
2736set i .prefs.pane.audio
2737add_help $i.dd.device.mdev "Selects preferred audio device."
2738add_help $i.dd.sampling.freq.mb \
2739                        "Sets the sampling rate of the audio device.\nThis changes the available codecs."
2740add_help $i.dd.sampling.ch_in.mb \
2741                        "Changes between mono and stereo audio input."
2742add_help $i.dd.cks.f.f.silence\
2743                         "Prevents silence from being transmitted when the speaker is silent\n\
2744                          and the input is unmuted."
2745add_help $i.dd.cks.f.f.agc       "Enables automatic control of the volume\nof the sound you send."
2746add_help $i.dd.cks.f.f.loop "Enables hardware for loopback of audio input."
2747add_help $i.dd.cks.f.f.suppress \
2748                         "Mutes microphone when playing audio."
2749
2750# transmission help
2751set i .prefs.pane.transmission
2752
2753add_help $i.dd.units.m  "Sets the duration of each packet sent.\nThere is a fixed per-packet\
2754                         overhead, so\nmaking this larger will reduce the total\noverhead.\
2755                                                 The effects of packet loss are\nmore noticable with large packets."
2756add_help $i.dd.pri.m    "Changes the primary audio compression\nscheme. The list is arranged\
2757                         with high-\nquality, high-bandwidth choices at the\ntop, and\
2758                                                 poor-quality, lower-bandwidth\nchoices at the bottom."
2759add_help $i.cc.van.rb   "Sets no channel coding."
2760add_help $i.cc.red.rb   "Piggybacks earlier units of audio into packets\n\
2761                         to protect against packet loss. Some audio\n\
2762                         tools (eg: vat-4.0) are not able to receive\n\
2763                         audio sent with this option."
2764add_help $i.cc.red.fc.m \
2765                        "Sets the format of the piggybacked data."
2766add_help $i.cc.red.u.m \
2767                        "Sets the offset of the piggybacked data."
2768add_help $i.cc.layer.fc.m  "Sets the number of discrete layers which will\nbe sent. You need\
2769                                                    to start RAT with the options\n-l n <address>/<port> <address>/<port>,\nwhere\
2770                                                    n is the number of layers and there is an\naddress and port for each layer.\
2771                                                        NB: this is only\nsupported by the WBS codec at present."
2772add_help $i.cc.int.fc.m \
2773                        "Sets the separation of adjacent units within\neach packet. Larger values correspond\
2774                         to longer\ndelays."
2775add_help $i.cc.int.zz.m "Number of compound units per packet."
2776add_help $i.cc.int.rb   "Enables interleaving which exchanges latency\n\
2777                         for protection against burst losses.  No other\n\
2778                         audio tools can decode this format (experimental)."
2779
2780# Reception Help
2781set i .prefs.pane.reception
2782add_help $i.r.m         "Sets the type of repair applied when packets are\nlost. The schemes\
2783                         are listed in order of increasing\ncomplexity and quality of repair."
2784add_help $i.r.ms        "Sets the type of sample rate conversion algorithm\n\
2785                         that will be applied to streams that differ in rate\n\
2786                         to the audio device rate."
2787add_help $i.o.f.cb      "Enforce playout delay limits set below.\nThis is not usually desirable."
2788add_help $i.o.f.fl.scmin   "Sets the minimum playout delay that will be\napplied to incoming\
2789                         audio streams."
2790add_help $i.o.f.fr.scmax   "Sets the maximum playout delay that will be\napplied to incoming\
2791                         audio streams."
2792add_help $i.c.f.f.lec   "If enabled, extra delay is added at both sender and receiver.\nThis allows\
2793                         the receiver to better cope with host scheduling\nproblems, and the sender\
2794                         to perform better silence suppression.\nAs the name suggests, this option\
2795                         is intended for scenarios such\nas transmitting a lecture, where interactivity\
2796                         is less important\nthan quality."
2797
2798# security...too obvious for balloon help!
2799add_help .prefs.pane.security.a.f.f.e "Due to government export restrictions\nhelp\
2800                                       for this option is not available."
2801
2802# interface...ditto!
2803set i .prefs.pane.interface
2804add_help $i.a.f.f.power "Disable display of audio powermeters. This\nis only\
2805                         useful if you have a slow machine"
2806add_help $i.a.f.f.video "Enable lip-synchronisation, if\nyou\
2807                         have a compatible video tool"
2808add_help $i.a.f.f.balloon "If you can see this, balloon help\nis enabled. If not, it isn't."
2809add_help $i.a.f.f.matrix  "Displays a chart showing the reception\nquality reported by all participants"
2810add_help $i.a.f.f.plist   "Hides the list of participants"
2811
2812add_help .chart         "This chart displays the reception quality reported\n by all session\
2813                         participants. Looking along a row\n gives the quality with which that\
2814                         participant was\n received by all other participants in the session:\n green\
2815                         is good quality, orange medium quality, and\n red poor quality audio."
2816
2817} script_error
2818
2819if { $script_error != "" } {
2820    puts "Error: \"$script_error\""
2821    destroy .
2822    exit -1
2823}
2824
2825wm deiconify .
Note: See TracBrowser for help on using the browser.