root/rat/trunk/settings.c @ 2763

Revision 2763, 21.7 KB (checked in by ucacoxh, 15 years ago)

- More interface consistency fixes. audio_get_device_details(idx) now

returns a const audio_device_details* rather than filling in an
audio_device_details_t structure.

Nearly all interfaces are consistent in this regard now...

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2 * FILE:    settings.c
3 * PROGRAM: RAT
4 * AUTHORS: Colin Perkins
5 *
6 * $Revision$
7 * $Date$
8 *
9 * Copyright (c) 1999 University College London
10 * All rights reserved.
11 *
12 */
13
14#include "config_unix.h"
15#include "config_win32.h"
16#include "debug.h"
17#include "ts.h"
18#include "channel.h"
19#include "net_udp.h"
20#include "timers.h"
21#include "session.h"
22#include "repair.h"
23#include "transmit.h"
24#include "codec_types.h"
25#include "codec.h"
26#include "audio.h"
27#include "auddev.h"
28#include "version.h"
29#include "settings.h"
30#include "converter.h"
31#include "rtp.h"
32#include "util.h"
33
34typedef struct s_hash_tuple {
35        u_int32 hash;
36        char *key;
37        char *value;
38        struct s_hash_tuple *next;
39} hash_tuple;
40
41typedef struct s_hash_chain {
42        u_int32 nelem;
43        hash_tuple *head;
44} hash_chain;
45
46#define SETTINGS_READ_SIZE 100
47#define SETTINGS_TABLE_SIZE 11
48
49#ifdef WIN32
50#define SETTINGS_BUF_SIZE 1500
51static HKEY cfgKey;
52#endif
53
54#ifndef WIN32
55static hash_chain *table;          /* Use hashtable to load settings    */
56static FILE       *settings_file;  /* Write direct to this file to save */
57#endif
58
59/* SETTINGS HASH CODE ********************************************************/
60
61static u_int32
62setting_hash(char *key)
63{
64#ifndef WIN32
65        u_int32 hash = 0;
66
67        while(*key != '\0') {
68                hash = hash * 31;
69                hash += ((u_int32)*key) + 1;
70                key++;
71        }
72
73        return hash;
74#endif
75}
76
77static void
78settings_table_add(char *key, char *value)
79{
80#ifndef WIN32
81        hash_tuple *t;
82        int row;
83
84        t = (hash_tuple*)xmalloc(sizeof(hash_tuple));
85        /* transfer values */
86        t->hash  = setting_hash(key);
87        t->key   = xstrdup(key);
88        t->value = xstrdup(value);
89
90        /* Add to table */
91        row      = t->hash % SETTINGS_TABLE_SIZE;
92        t->next  = table[row].head;
93        table[row].head = t;
94        table[row].nelem++;
95#endif
96}
97
98/* settings_table_lookup points value at actual value
99 * and return TRUE if key found */
100static int
101settings_table_lookup(char *key, char **value)
102{
103#ifndef WIN32
104        hash_tuple *t;
105        u_int32     hash;
106
107        hash = setting_hash(key);
108
109        t = table[hash % SETTINGS_TABLE_SIZE].head;
110        while(t != NULL) {
111                if (t->hash == hash && strcmp(key, t->key) == 0) {
112                        *value = t->value;
113                        return TRUE;
114                }
115                t = t->next;
116        }
117        *value = NULL;
118        return FALSE;
119#endif
120}
121
122static void
123settings_table_create()
124{
125#ifndef WIN32
126        table = (hash_chain*)xmalloc(sizeof(hash_chain) * SETTINGS_TABLE_SIZE);
127        memset(table, 0, sizeof(hash_chain) * SETTINGS_TABLE_SIZE);
128#endif
129}
130
131static void
132settings_table_destroy(void)
133{
134#ifndef WIN32
135        hash_tuple *t;
136        int i;
137
138        for(i = SETTINGS_TABLE_SIZE-1; i >= 0; i--) {
139                t = table[i].head;
140                while (t != NULL) {
141                        table[i].head = t->next;
142                        xfree(t->key);
143                        xfree(t->value);
144                        xfree(t);
145                        t = table[i].head;
146                }
147        }
148        xfree(table);
149        table = NULL;
150        xmemchk();
151#endif
152}
153
154/* SETTINGS CODE *************************************************************/
155
156#ifdef WIN32
157static void open_registry(void)
158{
159        HKEY                    key    = HKEY_CURRENT_USER;
160        LPCTSTR                 subKey = "Software\\Mbone Applications\\common";
161        DWORD                   disp;
162        char                    buffer[SETTINGS_BUF_SIZE];
163        LONG                    status;
164
165        status = RegCreateKeyEx(key, subKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &cfgKey, &disp);
166        if (status != ERROR_SUCCESS) {
167                FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, status, 0, buffer, SETTINGS_BUF_SIZE, NULL);
168                debug_msg("Unable to open registry: %s\n", buffer);
169                abort();
170        }
171        if (disp == REG_CREATED_NEW_KEY) {
172                debug_msg("Created new registry entry...\n");
173        } else {
174                debug_msg("Opened existing registry entry...\n");
175        }
176}
177
178static void close_registry(void)
179{
180        LONG status;
181        char buffer[SETTINGS_BUF_SIZE];
182       
183        status = RegCloseKey(cfgKey);
184        if (status != ERROR_SUCCESS) {
185                FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, status, 0, buffer, SETTINGS_BUF_SIZE, NULL);
186                debug_msg("Unable to close registry: %s\n", buffer);
187                abort();
188        }
189        debug_msg("Closed registry entry...\n");
190}
191#endif
192
193static void init_part_two(void)
194{
195#ifdef WIN32
196        HKEY                    key    = HKEY_CURRENT_USER;
197        LPCTSTR                 subKey = "Software\\Mbone Applications\\rat";
198        DWORD                   disp;
199        char                    buffer[SETTINGS_BUF_SIZE];
200        LONG                    status;
201
202        close_registry();
203        status = RegCreateKeyEx(key, subKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &cfgKey, &disp);
204        if (status != ERROR_SUCCESS) {
205                FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, status, 0, buffer, SETTINGS_BUF_SIZE, NULL);
206                debug_msg("Unable to open registry: %s\n", buffer);
207                abort();
208        }
209        if (disp == REG_CREATED_NEW_KEY) {
210                debug_msg("Created new registry entry...\n");
211        } else {
212                debug_msg("Opened existing registry entry...\n");
213        }
214#endif
215}
216
217
218static void load_init(void)
219{
220#ifndef WIN32
221        FILE            *sfile;
222        struct passwd   *p;     
223        char            *filen;
224        char            *buffer;
225        char            *key, *value;
226
227        /* The getpwuid() stuff is to determine the users home directory, into which we */
228        /* write the settings file. The struct returned by getpwuid() is statically     */
229        /* allocated, so it's not necessary to free it afterwards.                      */
230        p = getpwuid(getuid());
231        if (p == NULL) {
232                perror("Unable to get passwd entry");
233                abort();
234        }
235
236        settings_table_create();
237
238        filen = (char *) xmalloc(strlen(p->pw_dir) + 15);
239        sprintf(filen, "%s/.RTPdefaults", p->pw_dir);
240        sfile = fopen(filen, "r");
241        xfree(filen);
242
243        if (sfile == NULL) {
244                debug_msg("No file to open\n");
245                return;
246        }
247
248        buffer = xmalloc(SETTINGS_READ_SIZE+1);
249        buffer[100] = '\0';
250
251        while(fgets(buffer, SETTINGS_READ_SIZE, sfile) != NULL) {
252                if (buffer[0] != '*') {
253                        debug_msg("Garbage ignored: %s\n", buffer);
254                        continue;
255                }
256                key   = (char *) strtok(buffer, ":");
257                assert(key != NULL);
258                key = key + 1;               /* skip asterisk */
259                value = (char *) strtok(NULL, "\n");
260                assert(value != NULL);
261                while (*value != '\0' && isascii((int)*value) && isspace((int)*value)) {
262                        /* skip leading spaces, and stop skipping if
263                         * not ascii*/
264                        value++;             
265                }
266                settings_table_add(key, value);
267        }
268        fclose(sfile);
269        xfree(buffer);
270
271#else
272        open_registry();
273#endif
274}
275
276static void load_done(void)
277{
278#ifdef WIN32
279        close_registry();
280#else
281        settings_table_destroy();
282#endif
283}
284
285static int 
286setting_load(char *key, char **value)
287{
288#ifndef WIN32
289        return settings_table_lookup(key, value);
290#endif
291}
292
293static char *
294setting_load_str(char *name, char *default_value)
295{
296#ifndef WIN32
297        char *value;
298        if (setting_load(name, &value)) {
299                return value;
300        }
301        return default_value;
302#else
303        LONG status;
304        char buffer[SETTINGS_BUF_SIZE];
305        DWORD ValueType;
306        int val_len;
307        char *value;
308
309        ValueType = REG_SZ;
310        /* call RegQueryValueEx once first to get size of string */
311        status = RegQueryValueEx(cfgKey, name, NULL, &ValueType, NULL, &val_len);
312        if (status != ERROR_SUCCESS) {
313                FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, status, 0, buffer, SETTINGS_BUF_SIZE, NULL);
314                debug_msg("Unable to load setting: %s\n", buffer);
315                return default_value;
316        }       
317        /* now that we know size we can allocate memory and call RegQueryValueEx again */
318        value = (char*)xmalloc(val_len * sizeof(char));
319        status = RegQueryValueEx(cfgKey, name, NULL, &ValueType, value, &val_len);
320        if (status != ERROR_SUCCESS) {
321                FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, status, 0, buffer, SETTINGS_BUF_SIZE, NULL);
322                debug_msg("Unable to load setting %s: %s\n", name, buffer);
323                return default_value;
324        }       
325        return value;
326#endif
327}
328
329static int 
330setting_load_int(char *name, int default_value)
331{
332#ifndef WIN32
333        char *value;
334
335        if (setting_load(name, &value)) {
336                return atoi(value);
337        }
338        return default_value;
339#else
340        LONG status;
341        char buffer[SETTINGS_BUF_SIZE];
342        DWORD ValueType;
343        int value, val_len;
344
345        ValueType = REG_DWORD;
346        val_len = sizeof(int);
347        status = RegQueryValueEx(cfgKey, name, NULL, &ValueType, &(char)value, &val_len);
348        if (status != ERROR_SUCCESS) {
349                FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, status, 0, buffer, SETTINGS_BUF_SIZE, NULL);
350                debug_msg("Unable to load setting %s: %s\n", name, buffer);
351                return default_value;
352        }       
353        return value;
354#endif
355}
356
357void settings_load_early(session_t *sp)
358{
359        char                            *name, *primary_codec, *port;
360        int                              i, freq, chan, n;
361        cc_details                       ccd;
362        const audio_device_details_t    *add = NULL;
363        const audio_port_details_t      *apd = NULL;
364        const converter_details_t       *cod = NULL;
365        const repair_details_t          *r   = NULL;
366        codec_id_t                       cid;
367
368        load_init();            /* Initial settings come from the common prefs file... */
369        init_part_two();        /* Switch to pulling settings from the RAT specific prefs file... */
370
371        name = setting_load_str("audioDevice", "No Audio Device");
372        n = (int)audio_get_device_count();
373        for(i = 0; i < n; i++) {
374                add = audio_get_device_details(i);
375                if (strcmp(add->name, name) == 0) {
376                        audio_device_register_change_device(sp, add->descriptor);
377                        break;
378                }
379        }
380
381        freq = setting_load_int("audioFrequency", 8000);
382        chan = setting_load_int("audioChannelsIn", 1);
383        primary_codec = setting_load_str("audioPrimary", "GSM");
384
385        cid  = codec_get_matching(primary_codec, (u_int16)freq, (u_int16)chan);
386        if (codec_id_is_valid(cid) == FALSE) {
387                /* Codec name is garbage...should only happen on upgrades */
388                cid = codec_get_matching("GSM", (u_int16)freq, (u_int16)chan);
389        }
390
391        audio_device_register_change_primary(sp, cid);
392        audio_device_reconfigure(sp);
393
394        port = setting_load_str("audioOutputPort", "Headphone");
395        for(i = 0; i < audio_get_oport_count(sp->audio_device); i++) {
396                apd = audio_get_oport_details(sp->audio_device, i);
397                if (!strcasecmp(port, apd->name)) {
398                        break;
399                }
400        }
401        audio_set_oport(sp->audio_device, apd->port);
402       
403        port = setting_load_str("audioInputPort", "Microphone");
404        for(i = 0; i < audio_get_iport_count(sp->audio_device); i++) {
405                apd = audio_get_iport_details(sp->audio_device, i);
406                if (!strcasecmp(port, apd->name)) {
407                        break;
408                }
409        }
410        audio_set_iport(sp->audio_device, apd->port);
411
412        audio_set_ogain(sp->audio_device, setting_load_int("audioOutputGain", 75));
413        audio_set_igain(sp->audio_device, setting_load_int("audioInputGain",  75));
414        tx_igain_update(sp->tb);
415
416        name = setting_load_str("audioChannelCoding", "None");
417        n    = channel_get_coder_count();
418        for (i = 0; i < n; i++ ) {
419                channel_get_coder_details(i, &ccd);
420                if (strcmp(ccd.name, name) == 0) {
421                        channel_encoder_create(ccd.descriptor, &sp->channel_coder);
422                        break;
423                }
424        }
425
426        setting_load_int("audioInputMute", 1);
427        setting_load_int("audioOutputMute", 1);
428
429        channel_encoder_set_parameters(sp->channel_coder, setting_load_str("audioChannelParameters", "None"));
430        channel_encoder_set_units_per_packet(sp->channel_coder, (u_int16) setting_load_int("audioUnits", 1));
431
432        /* Set default repair to be first available */
433        r          = repair_get_details(0);
434        sp->repair = r->id;
435        name       = setting_load_str("audioRepair", "Pattern-Match");
436        n          = (int)repair_get_count();
437        for(i = 0; i < n; i++) {
438                r = repair_get_details(i);
439                if (strcasecmp(r->name, name) == 0) {
440                        sp->repair = r->id;
441                        break;
442                }
443        }
444
445        /* Set default converter to be first available */
446        cod           = converter_get_details(0);
447        sp->converter = cod->id;
448        name          = setting_load_str("audioAutoConvert", "High Quality");
449        n             = (int)converter_get_count();
450        /* If converter setting name matches then override existing choice */
451        for(i = 0; i < n; i++) {
452                cod = converter_get_details(i);
453                if (strcasecmp(cod->name, name) == 0) {
454                        sp->converter = cod->id;
455                        break;
456                }
457        }
458       
459        sp->limit_playout  = setting_load_int("audioLimitPlayout", 0);
460        sp->min_playout    = setting_load_int("audioMinPlayout", 0);
461        sp->max_playout    = setting_load_int("audioMaxPlayout", 2000);
462        sp->lecture        = setting_load_int("audioLecture", 0);
463        sp->render_3d      = setting_load_int("audio3dRendering", 0);
464        sp->detect_silence = setting_load_int("audioSilence", 1);
465        sp->agc_on         = setting_load_int("audioAGC", 0);
466        sp->loopback_gain  = setting_load_int("audioLoopback", 0);
467        sp->echo_suppress  = setting_load_int("audioEchoSuppress", 0);
468        sp->meter          = setting_load_int("audioPowermeters", 1);
469        sp->sync_on        = setting_load_int("audioLipSync", 0);
470        xmemchk();
471        load_done();
472}
473
474void settings_load_late(session_t *sp)
475{
476        u_int32 my_ssrc;
477        char   *field;
478        load_init();            /* Initial settings come from the common prefs file... */
479
480        my_ssrc = rtp_my_ssrc(sp->rtp_session[0]);
481        field = xstrdup(setting_load_str("rtpName", "Unknown"));
482        rtp_set_sdes(sp->rtp_session[0], my_ssrc, RTCP_SDES_NAME,  field, strlen(field));
483        field = xstrdup(setting_load_str("rtpEmail", ""));
484        rtp_set_sdes(sp->rtp_session[0], my_ssrc, RTCP_SDES_EMAIL, field, strlen(field));
485        field = xstrdup(setting_load_str("rtpPhone", ""));
486        rtp_set_sdes(sp->rtp_session[0], my_ssrc, RTCP_SDES_PHONE, field, strlen(field));
487        field = xstrdup(setting_load_str("rtpLoc", ""));
488        rtp_set_sdes(sp->rtp_session[0], my_ssrc, RTCP_SDES_LOC,   field, strlen(field));
489        field = xstrdup(RAT_VERSION);
490        rtp_set_sdes(sp->rtp_session[0], my_ssrc, RTCP_SDES_TOOL,  field, strlen(field));
491        init_part_two();        /* Switch to pulling settings from the RAT specific prefs file... */
492        load_done();
493}
494
495static void 
496save_init(void)
497{
498#ifndef WIN32
499        struct passwd   *p;     
500        char            *filen;
501
502        /* The getpwuid() stuff is to determine the users home directory, into which we */
503        /* write the settings file. The struct returned by getpwuid() is statically     */
504        /* allocated, so it's not necessary to free it afterwards.                      */
505        p = getpwuid(getuid());
506        if (p == NULL) {
507                perror("Unable to get passwd entry");
508                abort();
509        }
510        filen = (char *) xmalloc(strlen(p->pw_dir) + 15);
511        sprintf(filen, "%s/.RTPdefaults", p->pw_dir);
512        settings_file = fopen(filen, "w");
513        xfree(filen);
514#else
515        open_registry();
516#endif
517}
518
519static void save_done(void)
520{
521#ifndef WIN32
522        fclose(settings_file);
523        settings_file = NULL;
524#else
525        close_registry();
526#endif
527}
528
529static void 
530setting_save_str(const char *name, const char *val)
531{
532       
533#ifndef WIN32
534        if (val == NULL) {
535                val = "";
536        }
537        fprintf(settings_file, "*%s: %s\n", name, val);
538#else
539        int status;
540        char buffer[SETTINGS_BUF_SIZE];
541
542        if (val == NULL) {
543                val = "";
544        }
545
546        status = RegSetValueEx(cfgKey, name, 0, REG_SZ, val, strlen(val) + 1);
547        if (status != ERROR_SUCCESS) {
548                FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, status, 0, buffer, SETTINGS_BUF_SIZE, NULL);
549                debug_msg("Unable to save setting %s: %s\n", name, buffer);
550                abort();
551        }       
552#endif
553}
554
555static void setting_save_int(const char *name, const long val)
556{
557#ifndef WIN32
558        fprintf(settings_file, "*%s: %ld\n", name, val);
559#else
560        LONG status;
561        char buffer[SETTINGS_BUF_SIZE];
562
563        status = RegSetValueEx(cfgKey, name, 0, REG_DWORD, &(char)val, sizeof(val));
564        if (status != ERROR_SUCCESS) {
565                FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, status, 0, buffer, SETTINGS_BUF_SIZE, NULL);
566                debug_msg("Unable to save setting %s: %s\n", name, buffer);
567                abort();
568        }       
569#endif
570}
571
572void settings_save(session_t *sp)
573{
574        const codec_format_t            *pri_cf;
575        const audio_port_details_t      *iapd   = NULL;
576        const audio_port_details_t      *oapd   = NULL;
577        const audio_format              *af     = NULL;
578        const repair_details_t          *repair = NULL;
579        const converter_details_t       *converter = NULL;
580        const audio_device_details_t    *add    = NULL;
581        codec_id_t                       pri_id;
582        cc_details                       cd;
583        int                              cc_len;
584        char                            *cc_param;
585        int                              i;
586        u_int16                          j,n;
587        u_int32                          my_ssrc;
588
589        pri_id   = codec_get_by_payload(sp->encodings[0]);
590        pri_cf   = codec_get_format(pri_id);
591        cc_len   = 2 * (CODEC_LONG_NAME_LEN + 4) + 1;
592        cc_param = (char*) xmalloc(cc_len);
593        channel_encoder_get_parameters(sp->channel_coder, cc_param, cc_len);
594        channel_get_coder_identity(sp->channel_coder, &cd);
595
596        n = converter_get_count();
597        for (j = 0; j < n; j++) {
598                converter = converter_get_details(j);
599                if (sp->converter == converter->id) {
600                        break;
601                }
602        }
603       
604        n = repair_get_count();
605        for (j = 0; j < n; j++) {
606                repair = repair_get_details(j);
607                if (sp->repair == repair->id) {
608                        break;
609                }
610        }
611
612        n = (int)audio_get_device_count();
613        for (i = 0; i < n; i++) {
614                add = audio_get_device_details(i);
615                if (sp->audio_device == add->descriptor) {
616                        break;
617                }
618        }
619
620        af = audio_get_ifmt(sp->audio_device);
621
622        for(i = 0; i < audio_get_iport_count(sp->audio_device); i++) {
623                iapd = audio_get_iport_details(sp->audio_device, i);
624                if (iapd->port == audio_get_iport(sp->audio_device)) {
625                        break;
626                }
627        }
628
629        for(i = 0; i < audio_get_oport_count(sp->audio_device); i++) {
630                oapd = audio_get_oport_details(sp->audio_device, i);
631                if (oapd->port == audio_get_oport(sp->audio_device)) {
632                        break;
633                }
634        }
635
636        save_init();
637        my_ssrc = rtp_my_ssrc(sp->rtp_session[0]);
638        setting_save_str("rtpName",  rtp_get_sdes(sp->rtp_session[0], my_ssrc, RTCP_SDES_NAME));
639        setting_save_str("rtpEmail", rtp_get_sdes(sp->rtp_session[0], my_ssrc, RTCP_SDES_EMAIL));
640        setting_save_str("rtpPhone", rtp_get_sdes(sp->rtp_session[0], my_ssrc, RTCP_SDES_PHONE));
641        setting_save_str("rtpLoc",   rtp_get_sdes(sp->rtp_session[0], my_ssrc, RTCP_SDES_LOC));
642
643        init_part_two();
644        setting_save_str("audioTool", rtp_get_sdes(sp->rtp_session[0], my_ssrc, RTCP_SDES_TOOL));
645        setting_save_str("audioDevice",     add->name);
646        setting_save_int("audioFrequency",  af->sample_rate);
647        setting_save_int("audioChannelsIn", af->channels);
648       
649        /* If we save a dynamically mapped codec we crash when we reload on startup */
650        if (pri_cf->default_pt != CODEC_PAYLOAD_DYNAMIC) {
651                setting_save_str("audioPrimary",           pri_cf->short_name);
652                /* If vanilla channel coder don't save audioChannelParameters - it's rubbish */
653                if (strcmp(cd.name, "Vanilla") == 0) {
654                        setting_save_str("audioChannelParameters", cc_param);
655                }
656                else {
657                        setting_save_str("audioChannelParameters", "None");
658                }
659        }
660
661        setting_save_int("audioUnits", channel_encoder_get_units_per_packet(sp->channel_coder));
662        /* Don't save the layered channel coder - you need to start it from the command
663        line anyway */
664        if (strcmp(cd.name, "Layering") == 0) {
665                setting_save_str("audioChannelCoding", "Vanilla");
666        }
667        else setting_save_str("audioChannelCoding",     cd.name);
668        setting_save_str("audioChannelCoding",     cd.name);
669        setting_save_str("audioRepair",            repair->name);
670        setting_save_str("audioAutoConvert",       converter->name);
671        setting_save_int("audioLimitPlayout",      sp->limit_playout);
672        setting_save_int("audioMinPlayout",        sp->min_playout);
673        setting_save_int("audioMaxPlayout",        sp->max_playout);
674        setting_save_int("audioLecture",           sp->lecture);
675        setting_save_int("audio3dRendering",       sp->render_3d);
676        setting_save_int("audioSilence",           sp->detect_silence);
677        setting_save_int("audioAGC",               sp->agc_on);
678        setting_save_int("audioLoopback",          sp->loopback_gain);
679        setting_save_int("audioEchoSuppress",      sp->echo_suppress);
680        setting_save_int("audioOutputGain",        audio_get_ogain(sp->audio_device));
681        setting_save_int("audioInputGain",         audio_get_igain(sp->audio_device));
682        setting_save_str("audioOutputPort",        oapd->name);
683        setting_save_str("audioInputPort",         iapd->name);
684        setting_save_int("audioPowermeters",       sp->meter);
685        setting_save_int("audioLipSync",           sp->sync_on);
686        /* We do not save audioOutputMute and audioInputMute by default, but should */
687        /* recognize them when reloading.                                           */
688        save_done();
689        xfree(cc_param);
690}
Note: See TracBrowser for help on using the browser.