#include #include #include #include #include #include "../include/cfgfmt.h" #include "../include/string.h" #include "../include/strexp.h" #include "../include/fio.h" #include "ymode.h" #include "soundpaths.h" #include "ysound.h" #include "ymixer.h" #include "rcfile.h" #include "options.h" int RCLoadFromFile(const char *filename); /* * Structure to contain a mixer device settings read from * file. */ typedef struct { char *name; Coefficient val[YTotalMixers][YMixerValues]; } mixer_device_fdata_struct; static void RCMixerParseMixerValueString( const char *s, Coefficient *value1, Coefficient *value2, Coefficient *value3, Coefficient *value4 ); static void MixerFreeFData( mixer_device_fdata_struct **ptr, int total ); static mixer_device_fdata_struct **MixerRCLoadAllFromFile( const char *filename, int *total ); int MixerRCLoadFromFile(const char *filename, Recorder *recorder); int MixerRCSaveToFile(const char *filename, Recorder *recorder); extern int YAntiShift(int in); /* In main.c */ #define ATOI(s) (((s) != NULL) ? atoi(s) : 0) #define ATOL(s) (((s) != NULL) ? atol(s) : 0) #define ATOF(s) (((s) != NULL) ? atof(s) : 0.0f) #define STRDUP(s) (((s) != NULL) ? strdup(s) : NULL) #define MAX(a,b) (((a) > (b)) ? (a) : (b)) #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #define CLIP(a,l,h) (MIN(MAX((a),(l)),(h))) #define STRLEN(s) (((s) != NULL) ? strlen(s) : 0) #define STRISEMPTY(s) (((s) != NULL) ? (*(s) == '\0') : 1) /* * Loads configuration. * * Any current Audio modes and sound paths will be deleted * and then loaded with the new ones from the file. */ int RCLoadFromFile(const char *filename) { int status; char *strptr, *strptr2; FILE *fp; struct stat stat_buf; char parm[CFG_PARAMETER_MAX]; char val[CFG_VALUE_MAX]; int lines_read = 0; YMode *ymode_ptr; SoundPath *sp_ptr; /* Check if file exists */ if(filename == NULL) return(-1); if(stat(filename, &stat_buf)) { fprintf(stderr, "%s: No such file.\n", filename); return(-1); } /* Open YIFF Sound Server configuration file */ fp = FOpen(filename, "rb"); if(fp == NULL) { fprintf(stderr, "%s: Cannot open.\n", filename); return(-1); } /* Free allocated resources that would be reloaded with the * contents of the configuration file to be loaded. */ /* Y Audio Modes */ YModeDeleteAll(); /* Y Sound Paths */ SoundPathDeleteAll(); /* Reads the next line, fetches parm and val. Will break the loop * if end of file is reached or continue as needed. Make sure variable * is initially reset to NULL before start of reading file. Uses * variables; strptr, strptr2, fp, parm, val, and lines_read. */ #define DO_READ_LINE \ { \ /* Free previous line and allocate/read next line */ \ free(strptr); \ strptr = FReadNextLineAlloc(fp, UNIXCFG_COMMENT_CHAR); \ if(strptr == NULL) \ break; \ lines_read++; \ \ /* Fetch parameter */ \ strptr2 = StringCfgParseParm(strptr); \ if(strptr2 == NULL) \ continue; \ strncpy(parm, strptr2, CFG_PARAMETER_MAX); \ parm[CFG_PARAMETER_MAX - 1] = '\0'; \ \ /* Fetch value */ \ strptr2 = StringCfgParseValue(strptr); \ if(strptr2 == NULL) \ strptr2 = "0"; /* Set it to "0" if NULL */ \ strncpy(val, strptr2, CFG_VALUE_MAX); \ val[CFG_VALUE_MAX - 1] = '\0'; \ } /* Begin reading file */ strptr = NULL; while(1) { DO_READ_LINE /* VersionMajor */ if(!strcasecmp(parm, "VersionMajor")) { } /* VersionMinor */ else if(!strcasecmp(parm, "VersionMinor")) { } /* Listening port number */ else if(!strcasecmp(parm, "Port")) { option.port = atoi(val); } /* Refresh interval */ else if(!strcasecmp(parm, "RefreshInterval")) { option.refresh_int.ms = atol(val) / 1000; option.refresh_int.us = atol(val) % 1000; } /* DSP device */ else if(!strcasecmp(parm, "DSPDevice") || !strcasecmp(parm, "Device") ) { strncpy( fname.device, val, PATH_MAX + NAME_MAX ); if(stat(fname.device, &stat_buf)) { fprintf(stderr, "%s: Line %i: Warning: %s: No such device.\n", filename, lines_read, fname.device ); } } /* Mixer device */ else if(!strcasecmp(parm, "MixerDevice") || !strcasecmp(parm, "Mixer") ) { strncpy( fname.mixer, val, PATH_MAX + NAME_MAX ); if(fname.mixer[0] != '\0') { if(stat(fname.mixer, &stat_buf)) fprintf(stderr, "%s: Line %i: Warning: %s: No such device.\n", filename, lines_read, fname.mixer ); } } /* MIDI player command */ else if(!strcasecmp(parm, "MIDIPlayCommand")) { free(option.midi_play_cmd); option.midi_play_cmd = strdup(val); } /* ALSA MIDI device port */ else if(!strcasecmp(parm, "MIDIDevicePort")) { #ifdef ALSA_RUN_CONFORM /* If -1 then implies it is not set */ if(option.midi_device_number < 0) { /* Was not previously set, so set it with the * specified value from configuration file. */ option.midi_device_number = atoi(val); } #endif /* ALSA_RUN_CONFORM */ } /* **************************************************** */ /* BeginYAudioMode */ else if(!strcasecmp(parm, "BeginYAudioMode") || !strcasecmp(parm, "BeginAudioMode") || !strcasecmp(parm, "BeginYMode") || !strcasecmp(parm, "BeginMode") ) { status = YModeAllocate(NULL); ymode_ptr = YModeGetPtr(status); if(ymode_ptr == NULL) continue; while(1) { DO_READ_LINE /* Name */ if(!strcasecmp(parm, "Name")) { free(ymode_ptr->name); ymode_ptr->name = STRDUP(val); } /* Cycle */ else if(!strcasecmp(parm, "Cycle")) { ymode_ptr->cycle.ms = atol(val) / 1000; ymode_ptr->cycle.us = atol(val) % 1000; } /* WriteAhead */ else if(!strcasecmp(parm, "WriteAhead")) { ymode_ptr->write_ahead.ms = atol(val) / 1000; ymode_ptr->write_ahead.us = atol(val) % 1000; } /* SampleSize */ else if(!strcasecmp(parm, "SampleSize")) { ymode_ptr->sample_size = atoi(val); if(ymode_ptr->sample_size == 16) ymode_ptr->sample_size = 16; else ymode_ptr->sample_size = 8; } /* Channels */ else if(!strcasecmp(parm, "Channels")) { ymode_ptr->channels = atoi(val); if(ymode_ptr->channels == 2) ymode_ptr->channels = 2; else ymode_ptr->channels = 1; } /* SampleRate */ else if(!strcasecmp(parm, "SampleRate")) { ymode_ptr->sample_rate = atoi(val); } #ifdef OSS_BUFFRAG /* AllowFragmenting */ else if(!strcasecmp(parm, "AllowFragmenting")) { ymode_ptr->allow_fragments = StringIsYes(val); } /* Fragments */ else if(!strcasecmp(parm, "Fragments")) { ymode_ptr->num_fragments = atoi(val); } /* FragmentSize */ else if(!strcasecmp(parm, "FragmentSize")) { /* Note: On file, it's bytes, in memory it's * shifts. */ ymode_ptr->fragment_size = YAntiShift(atoi(val) - 1); } #endif /* OSS_BUFFRAG */ /* FlipStereo */ else if(!strcasecmp(parm, "FlipStereo")) { ymode_ptr->flip_stereo = StringIsYes(val); } /* Direction */ else if(!strcasecmp(parm, "Direction")) { if(!strcasecmp(val, "Record")) ymode_ptr->direction = AUDIO_DIRECTION_RECORD; else ymode_ptr->direction = AUDIO_DIRECTION_PLAY; } /* EndYAudioMode */ else if(!strcasecmp(parm, "EndYAudioMode") || !strcasecmp(parm, "EndAudioMode") || !strcasecmp(parm, "EndYMode") || !strcasecmp(parm, "EndMode") ) { break; } /* Unknown parameter */ else { fprintf(stderr, "%s: Line %i: Unknown parameter: %s\n", filename, lines_read, parm ); continue; } } } /* **************************************************** */ /* BeginYSoundPath */ else if(!strcasecmp(parm, "BeginYSoundPath") || !strcasecmp(parm, "BeginSoundPath") ) { status = SoundPathAllocate(); sp_ptr = SoundPathGetPtr(status); if(sp_ptr == NULL) continue; while(1) { DO_READ_LINE /* Path */ if(!strcasecmp(parm, "Path")) { free(sp_ptr->path); sp_ptr->path = STRDUP(val); } /* EndYSoundPath */ else if(!strcasecmp(parm, "EndYSoundPath") || !strcasecmp(parm, "EndSoundPath") ) { break; } /* Unknown parameter */ else { fprintf(stderr, "%s: Line %i: Unknown parameter: %s\n", filename, lines_read, parm ); continue; } } } /* Unknown parameter */ else { fprintf(stderr, "%s: Line %i: Unknown parameter: %s\n", filename, lines_read, parm ); } } #undef DO_READ_LINE /* Close the file */ FClose(fp); return(0); } /* * Parses two values seperated by a white space from string * s and puts them into the Coefficient values. */ static void RCMixerParseMixerValueString( const char *s, Coefficient *value1, Coefficient *value2, Coefficient *value3, Coefficient *value4 ) { /* Seek past spaces and get value 1 */ while(ISBLANK(*s)) s++; if(value1 != NULL) *value1 = atof(s); /* Seek to and get value 2 */ while(!ISBLANK(*s) && (*s != '\0')) s++; while(ISBLANK(*s)) s++; if(value2 != NULL) *value2 = atof(s); /* Seek to and get value 3 */ while(!ISBLANK(*s) && (*s != '\0')) s++; while(ISBLANK(*s)) s++; if(value3 != NULL) *value3 = atof(s); /* Seek to and get value 4 */ while(!ISBLANK(*s) && (*s != '\0')) s++; while(ISBLANK(*s)) s++; if(value4 != NULL) *value4 = atof(s); } /* * Deallocates mixer device file data list. */ static void MixerFreeFData(mixer_device_fdata_struct **ptr, int total) { int i; mixer_device_fdata_struct *mdfd_ptr; for(i = 0; i < total; i++) { mdfd_ptr = ptr[i]; if(mdfd_ptr == NULL) continue; free(mdfd_ptr->name); free(mdfd_ptr); } free(ptr); } /* * Loads all mixer device channel values from the specified * file and returns an allocated array of listings for each * device. * * This array needs to be free'ed by the calling function. */ static mixer_device_fdata_struct **MixerRCLoadAllFromFile( const char *filename, int *total ) { int i, n; char *strptr, *strptr2; FILE *fp; int lines_read = 0; struct stat stat_buf; char parm[CFG_PARAMETER_MAX]; char val[CFG_VALUE_MAX]; char *mixer_name[] = YMixerConicalNames; mixer_device_fdata_struct **ptr = NULL; mixer_device_fdata_struct *mdfd_ptr; if((filename == NULL) || (total == NULL) ) return(NULL); if(stat(filename, &stat_buf)) return(NULL); /* Reset total */ (*total) = 0; /* Open mixer device file for reading */ fp = FOpen(filename, "rb"); if(fp == NULL) return(NULL); /* Reads the next line, fetches parm and val. Will break the loop * if end of file is reached or continue as needed. Make sure variable * is initially reset to NULL before start of reading file. Uses * variables; strptr, strptr2, fp, parm, val, and lines_read. */ #define DO_READ_LINE \ { \ /* Free previous line and allocate/read next line */ \ free(strptr); \ strptr = FReadNextLineAlloc(fp, UNIXCFG_COMMENT_CHAR); \ if(strptr == NULL) \ break; \ lines_read++; \ \ /* Fetch parameter */ \ strptr2 = StringCfgParseParm(strptr); \ if(strptr2 == NULL) \ continue; \ strncpy(parm, strptr2, CFG_PARAMETER_MAX); \ parm[CFG_PARAMETER_MAX - 1] = '\0'; \ \ /* Fetch value */ \ strptr2 = StringCfgParseValue(strptr); \ if(strptr2 == NULL) \ strptr2 = "0"; /* Set it to "0" if NULL */ \ strncpy(val, strptr2, CFG_VALUE_MAX); \ val[CFG_VALUE_MAX - 1] = '\0'; \ } /* Begin reading file */ strptr = NULL; while(1) { DO_READ_LINE /* BeginMixer */ if(!strcasecmp(parm, "BeginMixer")) { /* Allocate a new mixer device structure */ i = (*total); (*total) = i + 1; ptr = (mixer_device_fdata_struct **)realloc( ptr, (*total) * sizeof(mixer_device_fdata_struct *) ); if(ptr == NULL) { (*total) = 0; continue; } ptr[i] = (mixer_device_fdata_struct *)calloc( 1, sizeof(mixer_device_fdata_struct) ); if(ptr[i] == NULL) { (*total) = i; continue; } mdfd_ptr = ptr[i]; /* Record mixer device's name */ mdfd_ptr->name = STRDUP(val); while(1) { DO_READ_LINE /* Go through mixer device channel names */ for(n = 0; n < YTotalMixers; n++) { if(!strcasecmp(parm, mixer_name[n])) { RCMixerParseMixerValueString( val, (YMixerValues > 0) ? &mdfd_ptr->val[n][0] : NULL, (YMixerValues > 1) ? &mdfd_ptr->val[n][1] : NULL, (YMixerValues > 2) ? &mdfd_ptr->val[n][2] : NULL, (YMixerValues > 3) ? &mdfd_ptr->val[n][3] : NULL ); } } /* EndMixer */ if(!strcasecmp(parm, "EndMixer")) { break; } } } /* Unknown parameter */ else { fprintf( stderr, "%s: Line %i: Unknown parameter: %s\n", filename, lines_read, parm ); } } #undef DO_READ_LINE /* Close mixer device data file */ FClose(fp); return(ptr); } /* * Loads the mixer device channel values from the specified * file and updates the recorder's mixer device channel values. * * The recorder's mixer must already be opened for this * function to have any affect. * * If global fname.mixer is an empty string then no mixer data * will be loaded. */ int MixerRCLoadFromFile(const char *filename, Recorder *recorder) { int i, mixer_index, total; int set_our_mixer = 0; mixer_device_fdata_struct **ptr, *mdfd_ptr; const char *this_mixer_dev_name; if((filename == NULL) || (recorder == NULL)) return(-1); /* Get pointer to our mixer device's name, this will be * used to check through the listing of mixers to see which * one we want to load values for. */ this_mixer_dev_name = fname.mixer; if(this_mixer_dev_name[0] == '\0') { /* No mixer device to load */ return(0); } /* Load all mixer devices data from file */ ptr = MixerRCLoadAllFromFile(filename, &total); if(ptr == NULL) total = 0; /* No mixer devices loaded? */ if(total <= 0) return(-1); /* Go through mixer device listing */ for(i = 0; i < total; i++) { mdfd_ptr = ptr[i]; if(mdfd_ptr == NULL) continue; if(mdfd_ptr->name == NULL) continue; /* Match mixer device name */ if(!strcasecmp(mdfd_ptr->name, this_mixer_dev_name)) { /* This is the mixer device we're looking for, now go * through each mixer device channel setting. */ for(mixer_index = 0; mixer_index < YTotalMixers; mixer_index++) { /* Set mixer values for our mixer */ YMixerSet( recorder, mixer_index + YMixerCodeBaseOffset, mdfd_ptr->val[mixer_index] ); } set_our_mixer = 1; } } /* Deallocate the loaded mixer data listing */ MixerFreeFData(ptr, total); if(set_our_mixer) return(0); else return(-1); } /* * Saves the mixer device channel values specified in the * recorder to file. * * Other mixer device channel values in the file will also * be read and rewritten to file (their values will be * preserved). * * If the fname.mixer is an empty string then no operation * will be performed. * * The recorder's mixer must already be opened for this * function to have any affect. */ int MixerRCSaveToFile(const char *filename, Recorder *recorder) { int i, mixer_index, total; int got_our_mixer = 0; FILE *fp; mixer_device_fdata_struct **ptr, *mdfd_ptr; const char *this_mixer_dev_name; char *mixer_name[] = YMixerConicalNames; if((filename == NULL) || (recorder == NULL) ) return(-1); /* Get pointer to our mixer device's name, this will be * used to check through the listing of mixers to see which * one we want to save values for. */ this_mixer_dev_name = fname.mixer; if(this_mixer_dev_name[0] == '\0') { /* Mixer device name is empty string, nothing to save */ return(0); } /* Load all mixer devices data from file */ ptr = MixerRCLoadAllFromFile(filename, &total); if(ptr == NULL) total = 0; /* Go through mixer device listing, search for our mixer * device by matching name. If it is found, then update * its values with the current mixer device channel values. */ for(i = 0; i < total; i++) { mdfd_ptr = ptr[i]; if(mdfd_ptr == NULL) continue; if(mdfd_ptr->name == NULL) continue; /* Match mixer device name */ if(!strcasecmp(mdfd_ptr->name, this_mixer_dev_name)) { /* This is the mixer device we're looking for, now go * through each mixer device channel setting. */ for(mixer_index = 0; mixer_index < YTotalMixers; mixer_index++) { /* Get current mixer values for our mixer, store * the fetched values into the mixer device file * data structure which we will save later. */ YMixerGet( recorder, mixer_index + YMixerCodeBaseOffset, mdfd_ptr->val[mixer_index] ); } } /* Mark that we have set values for our mixer device * in the list. */ got_our_mixer = 1; } /* If our mixer device was not in the loaded mixer device listing * then append it to the list. */ if(!got_our_mixer) { /* Sanitize total */ if(total < 0) total = 0; /* Allocate one more pointer */ i = total; total++; ptr = (mixer_device_fdata_struct **)realloc( ptr, total * sizeof(mixer_device_fdata_struct *) ); if(ptr == NULL) { total = 0; } else { /* Allocate our mixer device structure in the list */ ptr[i] = (mixer_device_fdata_struct *)calloc( 1, sizeof(mixer_device_fdata_struct) ); mdfd_ptr = ptr[i]; if(mdfd_ptr != NULL) { mdfd_ptr->name = strdup(this_mixer_dev_name); /* Go through mixer device channel settings */ for(mixer_index = 0; mixer_index < YTotalMixers; mixer_index++) { /* Get current mixer values for our mixer, store * the fetched values into the mixer device file * data structure which we will save later. */ YMixerGet( recorder, mixer_index + YMixerCodeBaseOffset, mdfd_ptr->val[mixer_index] ); } } } } /* Do not deallocate mixer device listing just yet, we need * to write them back to file below. */ /* Open mixer device file for writing */ fp = FOpen(filename, "wb"); if(fp != NULL) { /* Write commented heading */ fprintf(fp, "# Mixer device channel settings.\n\ # This file is automatically generated by the YIFF Sound Server.\n\ # You may manually edit this file as needed.\n\ #\n" ); /* Write each mixer device channel values to file */ for(i = 0; i < total; i++) { mdfd_ptr = ptr[i]; if(mdfd_ptr == NULL) continue; if(mdfd_ptr->name == NULL) continue; fprintf(fp, "BeginMixer = %s\n", mdfd_ptr->name ); /* Write each mixer channel values */ for(mixer_index = 0; mixer_index < YTotalMixers; mixer_index++) { fprintf(fp, " %s = %f %f %f %f\n", mixer_name[mixer_index], (YMixerValues > 0) ? mdfd_ptr->val[mixer_index][0] : 0.0, (YMixerValues > 1) ? mdfd_ptr->val[mixer_index][1] : 0.0, (YMixerValues > 2) ? mdfd_ptr->val[mixer_index][2] : 0.0, (YMixerValues > 3) ? mdfd_ptr->val[mixer_index][3] : 0.0 ); } fprintf(fp, "EndMixer\n"); } /* Close the file */ FClose(fp); fp = NULL; } /* Deallocate loaded mixer device file data listing */ MixerFreeFData(ptr, total); return(0); }