root/rat/trunk/settings.c @ 2765

Revision 2765, 21.9 KB (checked in by ucacoxh, 15 years ago)

- Consistency fixes for channel coder query functions.

  • 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                              freq, chan;
361        u_int32                          i, n;
362        const cc_details_t              *ccd;
363        const audio_device_details_t    *add = NULL;
364        const audio_port_details_t      *apd = NULL;
365        const converter_details_t       *cod = NULL;
366        const repair_details_t          *r   = NULL;
367        codec_id_t                       cid;
368
369        load_init();            /* Initial settings come from the common prefs file... */
370        init_part_two();        /* Switch to pulling settings from the RAT specific prefs file... */
371
372        name = setting_load_str("audioDevice", "No Audio Device");
373        n = (int)audio_get_device_count();
374        for(i = 0; i < n; i++) {
375                add = audio_get_device_details(i);
376                if (strcmp(add->name, name) == 0) {
377                        audio_device_register_change_device(sp, add->descriptor);
378                        break;
379                }
380        }
381
382        freq = setting_load_int("audioFrequency", 8000);
383        chan = setting_load_int("audioChannelsIn", 1);
384        primary_codec = setting_load_str("audioPrimary", "GSM");
385
386        cid  = codec_get_matching(primary_codec, (u_int16)freq, (u_int16)chan);
387        if (codec_id_is_valid(cid) == FALSE) {
388                /* Codec name is garbage...should only happen on upgrades */
389                cid = codec_get_matching("GSM", (u_int16)freq, (u_int16)chan);
390        }
391
392        audio_device_register_change_primary(sp, cid);
393        audio_device_reconfigure(sp);
394
395        port = setting_load_str("audioOutputPort", "Headphone");
396        n    = audio_get_oport_count(sp->audio_device);
397        for(i = 0; i < n; i++) {
398                apd = audio_get_oport_details(sp->audio_device, i);
399                if (!strcasecmp(port, apd->name)) {
400                        break;
401                }
402        }
403        audio_set_oport(sp->audio_device, apd->port);
404       
405        port = setting_load_str("audioInputPort", "Microphone");
406        n    = audio_get_iport_count(sp->audio_device);
407        for(i = 0; i < n; i++) {
408                apd = audio_get_iport_details(sp->audio_device, i);
409                if (!strcasecmp(port, apd->name)) {
410                        break;
411                }
412        }
413        audio_set_iport(sp->audio_device, apd->port);
414
415        audio_set_ogain(sp->audio_device, setting_load_int("audioOutputGain", 75));
416        audio_set_igain(sp->audio_device, setting_load_int("audioInputGain",  75));
417        tx_igain_update(sp->tb);
418
419        name = setting_load_str("audioChannelCoding", "None");
420        n    = channel_get_coder_count();
421        for (i = 0; i < n; i++ ) {
422                ccd = channel_get_coder_details(i);
423                if (strcmp(ccd->name, name) == 0) {
424                        channel_encoder_create(ccd->descriptor, &sp->channel_coder);
425                        break;
426                }
427        }
428
429        setting_load_int("audioInputMute", 1);
430        setting_load_int("audioOutputMute", 1);
431
432        channel_encoder_set_parameters(sp->channel_coder, setting_load_str("audioChannelParameters", "None"));
433        channel_encoder_set_units_per_packet(sp->channel_coder, (u_int16) setting_load_int("audioUnits", 1));
434
435        /* Set default repair to be first available */
436        r          = repair_get_details(0);
437        sp->repair = r->id;
438        name       = setting_load_str("audioRepair", "Pattern-Match");
439        n          = (int)repair_get_count();
440        for(i = 0; i < n; i++) {
441                r = repair_get_details(i);
442                if (strcasecmp(r->name, name) == 0) {
443                        sp->repair = r->id;
444                        break;
445                }
446        }
447
448        /* Set default converter to be first available */
449        cod           = converter_get_details(0);
450        sp->converter = cod->id;
451        name          = setting_load_str("audioAutoConvert", "High Quality");
452        n             = (int)converter_get_count();
453        /* If converter setting name matches then override existing choice */
454        for(i = 0; i < n; i++) {
455                cod = converter_get_details(i);
456                if (strcasecmp(cod->name, name) == 0) {
457                        sp->converter = cod->id;
458                        break;
459                }
460        }
461       
462        sp->limit_playout  = setting_load_int("audioLimitPlayout", 0);
463        sp->min_playout    = setting_load_int("audioMinPlayout", 0);
464        sp->max_playout    = setting_load_int("audioMaxPlayout", 2000);
465        sp->lecture        = setting_load_int("audioLecture", 0);
466        sp->render_3d      = setting_load_int("audio3dRendering", 0);
467        sp->detect_silence = setting_load_int("audioSilence", 1);
468        sp->agc_on         = setting_load_int("audioAGC", 0);
469        sp->loopback_gain  = setting_load_int("audioLoopback", 0);
470        sp->echo_suppress  = setting_load_int("audioEchoSuppress", 0);
471        sp->meter          = setting_load_int("audioPowermeters", 1);
472        sp->sync_on        = setting_load_int("audioLipSync", 0);
473        xmemchk();
474        load_done();
475}
476
477void settings_load_late(session_t *sp)
478{
479        u_int32 my_ssrc;
480        char   *field;
481        load_init();            /* Initial settings come from the common prefs file... */
482
483        my_ssrc = rtp_my_ssrc(sp->rtp_session[0]);
484        field = xstrdup(setting_load_str("rtpName", "Unknown"));
485        rtp_set_sdes(sp->rtp_session[0], my_ssrc, RTCP_SDES_NAME,  field, strlen(field));
486        field = xstrdup(setting_load_str("rtpEmail", ""));
487        rtp_set_sdes(sp->rtp_session[0], my_ssrc, RTCP_SDES_EMAIL, field, strlen(field));
488        field = xstrdup(setting_load_str("rtpPhone", ""));
489        rtp_set_sdes(sp->rtp_session[0], my_ssrc, RTCP_SDES_PHONE, field, strlen(field));
490        field = xstrdup(setting_load_str("rtpLoc", ""));
491        rtp_set_sdes(sp->rtp_session[0], my_ssrc, RTCP_SDES_LOC,   field, strlen(field));
492        field = xstrdup(RAT_VERSION);
493        rtp_set_sdes(sp->rtp_session[0], my_ssrc, RTCP_SDES_TOOL,  field, strlen(field));
494        init_part_two();        /* Switch to pulling settings from the RAT specific prefs file... */
495        load_done();
496}
497
498static void 
499save_init(void)
500{
501#ifndef WIN32
502        struct passwd   *p;     
503        char            *filen;
504
505        /* The getpwuid() stuff is to determine the users home directory, into which we */
506        /* write the settings file. The struct returned by getpwuid() is statically     */
507        /* allocated, so it's not necessary to free it afterwards.                      */
508        p = getpwuid(getuid());
509        if (p == NULL) {
510                perror("Unable to get passwd entry");
511                abort();
512        }
513        filen = (char *) xmalloc(strlen(p->pw_dir) + 15);
514        sprintf(filen, "%s/.RTPdefaults", p->pw_dir);
515        settings_file = fopen(filen, "w");
516        xfree(filen);
517#else
518        open_registry();
519#endif
520}
521
522static void save_done(void)
523{
524#ifndef WIN32
525        fclose(settings_file);
526        settings_file = NULL;
527#else
528        close_registry();
529#endif
530}
531
532static void 
533setting_save_str(const char *name, const char *val)
534{
535       
536#ifndef WIN32
537        if (val == NULL) {
538                val = "";
539        }
540        fprintf(settings_file, "*%s: %s\n", name, val);
541#else
542        int status;
543        char buffer[SETTINGS_BUF_SIZE];
544
545        if (val == NULL) {
546                val = "";
547        }
548
549        status = RegSetValueEx(cfgKey, name, 0, REG_SZ, val, strlen(val) + 1);
550        if (status != ERROR_SUCCESS) {
551                FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, status, 0, buffer, SETTINGS_BUF_SIZE, NULL);
552                debug_msg("Unable to save setting %s: %s\n", name, buffer);
553                abort();
554        }       
555#endif
556}
557
558static void setting_save_int(const char *name, const long val)
559{
560#ifndef WIN32
561        fprintf(settings_file, "*%s: %ld\n", name, val);
562#else
563        LONG status;
564        char buffer[SETTINGS_BUF_SIZE];
565
566        status = RegSetValueEx(cfgKey, name, 0, REG_DWORD, &(char)val, sizeof(val));
567        if (status != ERROR_SUCCESS) {
568                FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, status, 0, buffer, SETTINGS_BUF_SIZE, NULL);
569                debug_msg("Unable to save setting %s: %s\n", name, buffer);
570                abort();
571        }       
572#endif
573}
574
575void settings_save(session_t *sp)
576{
577        const codec_format_t            *pri_cf;
578        const audio_port_details_t      *iapd      = NULL;
579        const audio_port_details_t      *oapd      = NULL;
580        const audio_format              *af        = NULL;
581        const repair_details_t          *repair    = NULL;
582        const converter_details_t       *converter = NULL;
583        const audio_device_details_t    *add       = NULL;
584        const cc_details_t              *ccd       = NULL;
585        codec_id_t                       pri_id;
586   
587        int                              cc_len;
588        char                            *cc_param;
589        int                              i;
590        u_int16                          j,n;
591        u_int32                          my_ssrc;
592
593        pri_id   = codec_get_by_payload(sp->encodings[0]);
594        pri_cf   = codec_get_format(pri_id);
595        cc_len   = 2 * (CODEC_LONG_NAME_LEN + 4) + 1;
596        cc_param = (char*) xmalloc(cc_len);
597        channel_encoder_get_parameters(sp->channel_coder, cc_param, cc_len);
598        ccd = channel_get_coder_identity(sp->channel_coder);
599
600        n = converter_get_count();
601        for (j = 0; j < n; j++) {
602                converter = converter_get_details(j);
603                if (sp->converter == converter->id) {
604                        break;
605                }
606        }
607       
608        n = repair_get_count();
609        for (j = 0; j < n; j++) {
610                repair = repair_get_details(j);
611                if (sp->repair == repair->id) {
612                        break;
613                }
614        }
615
616        n = (int)audio_get_device_count();
617        for (i = 0; i < n; i++) {
618                add = audio_get_device_details(i);
619                if (sp->audio_device == add->descriptor) {
620                        break;
621                }
622        }
623
624        af = audio_get_ifmt(sp->audio_device);
625
626        for(i = 0; i < audio_get_iport_count(sp->audio_device); i++) {
627                iapd = audio_get_iport_details(sp->audio_device, i);
628                if (iapd->port == audio_get_iport(sp->audio_device)) {
629                        break;
630                }
631        }
632
633        for(i = 0; i < audio_get_oport_count(sp->audio_device); i++) {
634                oapd = audio_get_oport_details(sp->audio_device, i);
635                if (oapd->port == audio_get_oport(sp->audio_device)) {
636                        break;
637                }
638        }
639
640        save_init();
641        my_ssrc = rtp_my_ssrc(sp->rtp_session[0]);
642        setting_save_str("rtpName",  rtp_get_sdes(sp->rtp_session[0], my_ssrc, RTCP_SDES_NAME));
643        setting_save_str("rtpEmail", rtp_get_sdes(sp->rtp_session[0], my_ssrc, RTCP_SDES_EMAIL));
644        setting_save_str("rtpPhone", rtp_get_sdes(sp->rtp_session[0], my_ssrc, RTCP_SDES_PHONE));
645        setting_save_str("rtpLoc",   rtp_get_sdes(sp->rtp_session[0], my_ssrc, RTCP_SDES_LOC));
646
647        init_part_two();
648        setting_save_str("audioTool", rtp_get_sdes(sp->rtp_session[0], my_ssrc, RTCP_SDES_TOOL));
649        setting_save_str("audioDevice",     add->name);
650        setting_save_int("audioFrequency",  af->sample_rate);
651        setting_save_int("audioChannelsIn", af->channels);
652       
653        /* If we save a dynamically mapped codec we crash when we reload on startup */
654        if (pri_cf->default_pt != CODEC_PAYLOAD_DYNAMIC) {
655                setting_save_str("audioPrimary",           pri_cf->short_name);
656                /* If vanilla channel coder don't save audioChannelParameters - it's rubbish */
657                if (strcmp(ccd->name, "Vanilla") == 0) {
658                        setting_save_str("audioChannelParameters", cc_param);
659                } else {
660                        setting_save_str("audioChannelParameters", "None");
661                }
662        }
663
664        setting_save_int("audioUnits", channel_encoder_get_units_per_packet(sp->channel_coder));
665        /* Don't save the layered channel coder - you need to start it */
666        /* from the command line anyway                                */
667        if (strcmp(ccd->name, "Layering") == 0) {
668                setting_save_str("audioChannelCoding", "Vanilla");
669        } else {
670                setting_save_str("audioChannelCoding", ccd->name);
671        }
672        setting_save_str("audioChannelCoding",     ccd->name);
673        setting_save_str("audioRepair",            repair->name);
674        setting_save_str("audioAutoConvert",       converter->name);
675        setting_save_int("audioLimitPlayout",      sp->limit_playout);
676        setting_save_int("audioMinPlayout",        sp->min_playout);
677        setting_save_int("audioMaxPlayout",        sp->max_playout);
678        setting_save_int("audioLecture",           sp->lecture);
679        setting_save_int("audio3dRendering",       sp->render_3d);
680        setting_save_int("audioSilence",           sp->detect_silence);
681        setting_save_int("audioAGC",               sp->agc_on);
682        setting_save_int("audioLoopback",          sp->loopback_gain);
683        setting_save_int("audioEchoSuppress",      sp->echo_suppress);
684        setting_save_int("audioOutputGain",        audio_get_ogain(sp->audio_device));
685        setting_save_int("audioInputGain",         audio_get_igain(sp->audio_device));
686        setting_save_str("audioOutputPort",        oapd->name);
687        setting_save_str("audioInputPort",         iapd->name);
688        setting_save_int("audioPowermeters",       sp->meter);
689        setting_save_int("audioLipSync",           sp->sync_on);
690        /* We do not save audioOutputMute and audioInputMute by default, but should */
691        /* recognize them when reloading.                                           */
692        save_done();
693        xfree(cc_param);
694}
Note: See TracBrowser for help on using the browser.