1 /*
2  * Copyright (C) 2002 - David W. Durham
3  *
4  * This file is part of ReZound, an audio editing application.
5  *
6  * ReZound is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published
8  * by the Free Software Foundation; either version 2 of the License,
9  * or (at your option) any later version.
10  *
11  * ReZound is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
19  */
20 
21 #include "settings.h"
22 
23 #include <stddef.h>
24 #include <stdlib.h> // for getenv
25 
26 #include <istring>
27 
28 CNestedDataFile *gSettingsRegistry=NULL;
29 
30 CNestedDataFile *gUserMacroStore=NULL;
31 
32 CNestedDataFile *gKeyBindingsStore=NULL;
33 const CNestedDataFile *gDefaultKeyBindingsStore=NULL;
34 
35 
36 string gPromptDialogDirectory="";
37 
38 
39 string gUserDataDirectory="";
40 string gSysDataDirectory="";
41 
encodeFilenamePresetParameter(const string _filename)42 const string encodeFilenamePresetParameter(const string _filename)
43 {
44 	string filename=_filename;
45 	if(gSysDataDirectory!="" && filename.find(gSysDataDirectory+"/")==0)
46 		filename.replace(0,gSysDataDirectory.size(),"$share");
47 	return filename;
48 }
49 
decodeFilenamePresetParameter(const string _filename)50 const string decodeFilenamePresetParameter(const string _filename)
51 {
52 	string filename=_filename;
53 	if(gSysDataDirectory!="" && filename.find("$share/")==0)
54 		filename.replace(0,6,gSysDataDirectory);
55 	return filename;
56 }
57 
58 
59 string gUserPresetsFilename="";
60 CNestedDataFile *gUserPresetsFile=NULL;
61 
62 string gSysPresetsFilename="";
63 CNestedDataFile *gSysPresetsFile=NULL;
64 
65 
66 string gDefaultAudioMethod="";
67 
68 unsigned gDesiredOutputSampleRate=44100;
69 unsigned gDesiredOutputChannelCount=2;
70 int gDesiredOutputBufferCount=2;
71 unsigned gDesiredOutputBufferSize=2048; // in frames (must be a power of 2)
72 
73 
74 #ifdef ENABLE_OSS
75 string gOSSOutputDevice="/dev/dsp";
76 string gOSSInputDevice="/dev/dsp";
77 #endif
78 #ifdef ENABLE_ALSA
79 string gALSAOutputDevice="hw:0";
80 string gALSAInputDevice="hw:0";
81 #endif
82 #ifdef ENABLE_PORTAUDIO
83 int gPortAudioOutputDevice=0;
84 int gPortAudioInputDevice=0;
85 #endif
86 #ifdef ENABLE_JACK
87 string gJACKOutputPortNames[64];
88 string gJACKInputPortNames[64];
89 #endif
90 
91 
92 string gLADSPAPath="";
93 
94 
95 string gFallbackWorkDir="/tmp"; // ??? would be something else on non-unix platforms
96 string gPrimaryWorkDir="";
97 
98 
99 string gClipboardDir="/tmp"; // ??? would be something else on non-unix platforms
100 string gClipboardFilenamePrefix="rezclip";
101 size_t gWhichClipboard=0;
102 
103 
104 size_t gMaxReopenHistory=16;
105 
106 
107 float gSkipMiddleMarginSeconds=2.0;
108 float gLoopGapLengthSeconds=0.75;
109 
110 float gPlayPositionShift=-0.08; // a guess at latency between hearing and where it would place the cue
111 
112 string gAddCueWhilePlaying_CueName="";
113 bool gAddCueWhilePlaying_Anchored=false;
114 
115 string gAddCueAtClick_CueName="";
116 bool gAddCueAtClick_Anchored=false;
117 
118 
119 unsigned gMeterUpdateTime=50;
120 
121 bool gLevelMetersEnabled=true;
122 unsigned gMeterRMSWindowTime=200;
123 unsigned gMaxPeakFallDelayTime=500;
124 double gMaxPeakFallRate=0.02;
125 
126 bool gStereoPhaseMetersEnabled=true;
127 unsigned gStereoPhaseMeterPointCount=100;
128 bool gStereoPhaseMeterUnrotate=true;
129 
130 bool gFrequencyAnalyzerEnabled=true;
131 unsigned gAnalyzerPeakFallDelayTime=400;
132 double gAnalyzerPeakFallRate=0.025;
133 
134 
135 CrossfadeEdgesTypes gCrossfadeEdges=cetInner;
136 float gCrossfadeStartTime=10.0;	 // default 20ms crossfade time
137 float gCrossfadeStopTime=10.0;
138 CrossfadeFadeMethods gCrossfadeFadeMethod=cfmLinear;
139 
140 
141 
142 map<string,AActionFactory *> gRegisteredActionFactories;
143 
144 // ----------------------------------------------------------------------------
145 
146 #include "AStatusComm.h"
147 #include "AFrontendHooks.h"
148 #include "CSound_defs.h"
149 
150 #include <stdio.h>
151 
152 #include <stdexcept>
153 
154 #include <CPath.h>
155 #include <CNestedDataFile/CNestedDataFile.h>
156 
157 /* read backend setting variables with the exception of gUserDataDir */
readBackendSettings()158 void readBackendSettings()
159 {
160 	// determine where the share data is located
161 	{
162 		// first try env var
163 		const char *rezShareEnvVar=getenv("REZ_SHARE_DIR");
164 		if(rezShareEnvVar!=NULL && CPath(rezShareEnvVar).exists())
165 		{
166 			printf("using environment variable $REZ_SHARE_DIR='%s' to override normal setting for share data direcory\n",rezShareEnvVar);
167 			gSysDataDirectory=rezShareEnvVar;
168 		}
169 		// next try the source directory where the code was built
170 		else if(CPath(SOURCE_DIR"/share").exists())
171 			gSysDataDirectory=SOURCE_DIR"/share";
172 		// next try the registry setting
173 		else if(gSettingsRegistry->keyExists("shareDirectory") && CPath(gSettingsRegistry->getValue<string>("shareDirectory")).exists())
174 			gSysDataDirectory=gSettingsRegistry->getValue<string>("shareDirectory");
175 		// finally fall back on the #define set by configure saying where ReZound will be installed
176 		else
177 			gSysDataDirectory=DATA_DIR"/rezound";
178 
179 		recheckShareDataDir:
180 
181 		string checkFile=gSysDataDirectory+istring(CPath::dirDelim)+"presets.dat";
182 
183 		// now, if the presets.dat file doesn't exist in the share data dir, ask for a setting
184 		if(!CPath(checkFile).exists())
185 		{
186 			if(Question("presets.dat not found in share data dir, '"+gSysDataDirectory+"'.\n  Would you like to manually select the share data directory directory?",yesnoQues)==yesAns)
187 			{
188 				gFrontendHooks->promptForDirectory(gSysDataDirectory,"Select Share Data Directory");
189 				goto recheckShareDataDir;
190 			}
191 		}
192 
193 		// fully qualify the share data directory
194 		gSysDataDirectory=CPath(gSysDataDirectory).realPath();
195 
196 		printf("using path '%s' for share data directory\n",gSysDataDirectory.c_str());
197 	}
198 
199 
200 	// parse the system presets
201 	gSysPresetsFilename=gSysDataDirectory+istring(CPath::dirDelim)+"presets.dat";
202 	gSysPresetsFile=new CNestedDataFile("",false);
203 	try
204 	{
205 		gSysPresetsFile->parseFile(gSysPresetsFilename);
206 	}
207 	catch(exception &e)
208 	{
209 		Error(e.what());
210 	}
211 
212 
213 	// parse the user presets
214 	gUserPresetsFilename=gUserDataDirectory+istring(CPath::dirDelim)+"presets.dat";
215 	CPath(gUserPresetsFilename).touch();
216 	gUserPresetsFile=new CNestedDataFile("",false);
217 	try
218 	{
219 		gUserPresetsFile->parseFile(gUserPresetsFilename);
220 	}
221 	catch(exception &e)
222 	{
223 		Error(e.what());
224 	}
225 
226 	GET_SETTING("promptDialogDirectory",gPromptDialogDirectory,string)
227 
228 
229 	GET_SETTING("DesiredOutputSampleRate",gDesiredOutputSampleRate,unsigned)
230 	GET_SETTING("DesiredOutputChannelCount",gDesiredOutputChannelCount,unsigned)
231 	GET_SETTING("DesiredOutputBufferCount",gDesiredOutputBufferCount,int)
232 		gDesiredOutputBufferCount=max(2,gDesiredOutputBufferCount);
233 	GET_SETTING("DesiredOutputBufferSize",gDesiredOutputBufferSize,unsigned)
234 		if(gDesiredOutputBufferSize<256 || (gDesiredOutputBufferSize & (gDesiredOutputBufferSize-1)))
235 			throw runtime_error(string(__func__)+" -- DesiredOutputBufferSize in "+gSettingsRegistry->getFilename()+" must be a power of 2 and >= than 256");
236 
237 
238 #ifdef ENABLE_OSS
239 	GET_SETTING("OSSOutputDevice",gOSSOutputDevice,string)
240 	GET_SETTING("OSSInputDevice",gOSSInputDevice,string)
241 #endif
242 
243 #ifdef ENABLE_ALSA
244 	GET_SETTING("ALSAOutputDevice",gALSAOutputDevice,string)
245 	GET_SETTING("ALSAInputDevice",gALSAInputDevice,string)
246 #endif
247 
248 #ifdef ENABLE_PORTAUDIO
249 	GET_SETTING("PortAudioOutputDevice",gPortAudioOutputDevice,int)
250 	GET_SETTING("PortAudioInputDevice",gPortAudioInputDevice,int)
251 #endif
252 
253 #ifdef ENABLE_JACK
254 	// ??? could do these with vector values I suppose
255 	for(unsigned t=0;t<MAX_CHANNELS;t++)
256 	{
257 		GET_SETTING("JACKOutputPortName"+istring(t+1),gJACKOutputPortNames[t],string)
258 		else
259 			break;
260 	}
261 
262 	for(unsigned t=0;t<MAX_CHANNELS;t++)
263 	{
264 		GET_SETTING("JACKInputPortName"+istring(t+1),gJACKInputPortNames[t],string)
265 		else
266 			break;
267 	}
268 #endif
269 
270 	GET_SETTING("LADSPA_PATH",gLADSPAPath,string)
271 	if(gLADSPAPath=="")
272 	{
273 		if(getenv("LADSPA_PATH")==NULL)
274 		{
275     			fprintf(stderr,"Notice: You do not have a LADSPA_PATH environment variable set.  Defaulting to \"/usr/local/lib/ladspa:/usr/lib/ladspa\"\n");
276 			gLADSPAPath="/usr/local/lib/ladspa:/usr/lib/ladspa";
277 		}
278 		else
279 			gLADSPAPath=mnn(getenv("LADSPA_PATH"));
280 	}
281 
282 	// where ReZound should fallback to put working files if it can't write to where it loaded a file from
283 		// ??? This could be a vector where it would try multiple locations finding one that isn't full or close to full relative to the loaded file size
284 	GET_SETTING("primaryWorkDir",gPrimaryWorkDir,string)
285 	GET_SETTING("fallbackWorkDir",gFallbackWorkDir,string)
286 
287 	GET_SETTING("clipboardDir",gClipboardDir,string)
288 
289 	GET_SETTING("clipboardFilenamePrefix",gClipboardFilenamePrefix,string)
290 
291 	GET_SETTING("whichClipboard",gWhichClipboard,size_t)
292 
293 	GET_SETTING("ReopenHistory" DOT "maxReopenHistory",gMaxReopenHistory,size_t)
294 
295 	GET_SETTING("skipMiddleMarginSeconds",gSkipMiddleMarginSeconds,float)
296 
297 	GET_SETTING("loopGapLengthSeconds",gLoopGapLengthSeconds,float)
298 
299 	GET_SETTING("playPositionShift",gPlayPositionShift,float)
300 
301 	GET_SETTING("addCueWhilePlaying_CueName",gAddCueWhilePlaying_CueName,string)
302 	GET_SETTING("addCueWhilePlaying_Anchored",gAddCueWhilePlaying_Anchored,bool)
303 
304 	GET_SETTING("addCueAtClick_CueName",gAddCueAtClick_CueName,string)
305 	GET_SETTING("addCueAtClick_Anchored",gAddCueAtClick_Anchored,bool)
306 
307 	GET_SETTING("Meters" DOT "meterUpdateTime",gMeterUpdateTime,unsigned)
308 
309 	GET_SETTING("Meters" DOT "Level" DOT "enabled",gLevelMetersEnabled,bool)
310 	GET_SETTING("Meters" DOT "Level" DOT "RMSWindowTime",gMeterRMSWindowTime,unsigned)
311 	GET_SETTING("Meters" DOT "Level" DOT "maxPeakFallDelayTime",gMaxPeakFallDelayTime,unsigned)
312 	GET_SETTING("Meters" DOT "Level" DOT "maxPeakFallRate",gMaxPeakFallRate,double)
313 
314 	GET_SETTING("Meters" DOT "StereoPhase" DOT "enabled",gStereoPhaseMetersEnabled,bool)
315 	GET_SETTING("Meters" DOT "StereoPhase" DOT "pointCount",gStereoPhaseMeterPointCount,unsigned)
316 	GET_SETTING("Meters" DOT "StereoPhase" DOT "unrotate",gStereoPhaseMeterUnrotate,bool)
317 
318 	GET_SETTING("Meters" DOT "Analyzer" DOT "enabled",gFrequencyAnalyzerEnabled,bool)
319 	GET_SETTING("Meters" DOT "Analyzer" DOT "peakFallDelayTime",gAnalyzerPeakFallDelayTime,unsigned)
320 	GET_SETTING("Meters" DOT "Analyzer" DOT "peakFallRate",gAnalyzerPeakFallRate,double)
321 
322 
323 	if(gSettingsRegistry->keyExists("crossfadeEdges"))
324 	{
325 		gCrossfadeEdges= (CrossfadeEdgesTypes)gSettingsRegistry->getValue<int>("crossfadeEdges");
326 		gCrossfadeStartTime= gSettingsRegistry->getValue<float>("crossfadeStartTime");
327 		gCrossfadeStopTime= gSettingsRegistry->getValue<float>("crossfadeStopTime");
328 		gCrossfadeFadeMethod= (CrossfadeFadeMethods)gSettingsRegistry->getValue<int>("crossfadeFadeMethod");
329 	}
330 }
331 
writeBackendSettings()332 void writeBackendSettings()
333 {
334 	gSettingsRegistry->setValue<string>("shareDirectory",gSysDataDirectory);
335 	gSettingsRegistry->setValue<string>("promptDialogDirectory",gPromptDialogDirectory);
336 
337 
338 	gSettingsRegistry->setValue<unsigned>("DesiredOutputSampleRate",gDesiredOutputSampleRate);
339 	gSettingsRegistry->setValue<unsigned>("DesiredOutputChannelCount",gDesiredOutputChannelCount);
340 	gSettingsRegistry->setValue<int>("DesiredOutputBufferCount",gDesiredOutputBufferCount);
341 	gSettingsRegistry->setValue<unsigned>("DesiredOutputBufferSize",gDesiredOutputBufferSize);
342 
343 
344 #ifdef ENABLE_OSS
345 	gSettingsRegistry->setValue<string>("OSSOutputDevice",gOSSOutputDevice);
346 	gSettingsRegistry->setValue<string>("OSSInputDevice",gOSSInputDevice);
347 #endif
348 
349 #ifdef ENABLE_PORTAUDIO
350 	gSettingsRegistry->setValue<int>("PortAudioOutputDevice",gPortAudioOutputDevice);
351 	gSettingsRegistry->setValue<int>("PortAudioInputDevice",gPortAudioInputDevice);
352 #endif
353 
354 #ifdef ENABLE_JACK
355 	// ??? could do these with vector<string> values I suppose
356 	for(unsigned t=0;t<MAX_CHANNELS;t++)
357 	{
358 		if(gJACKOutputPortNames[t]!="")
359 			gSettingsRegistry->setValue<string>("JACKOutputPortName"+istring(t+1),gJACKOutputPortNames[t]);
360 		else
361 		{
362 			gSettingsRegistry->removeKey("JACKOutputPortName"+istring(t+2));
363 			break;
364 		}
365 	}
366 	for(unsigned t=0;t<MAX_CHANNELS;t++)
367 	{
368 		if(gJACKInputPortNames[t]!="")
369 			gSettingsRegistry->setValue<string>("JACKInputPortName"+istring(t+1),gJACKInputPortNames[t]);
370 		else
371 		{
372 			gSettingsRegistry->removeKey("JACKInputPortName"+istring(t+2));
373 			break;
374 		}
375 	}
376 #endif
377 
378 	gSettingsRegistry->setValue<string>("LADSPA_PATH",gLADSPAPath);
379 
380 	gSettingsRegistry->setValue<string>("primaryWorkDir",gPrimaryWorkDir);
381 	gSettingsRegistry->setValue<string>("fallbackWorkDir",gFallbackWorkDir);
382 
383 	gSettingsRegistry->setValue<string>("clipboardDir",gClipboardDir);
384 	gSettingsRegistry->setValue<string>("clipboardFilenamePrefix",gClipboardFilenamePrefix);
385 	gSettingsRegistry->setValue<size_t>("whichClipboard",gWhichClipboard);
386 
387 	gSettingsRegistry->setValue<size_t>("ReopenHistory" DOT "maxReopenHistory",gMaxReopenHistory);
388 
389 	gSettingsRegistry->setValue<float>("skipMiddleMarginSeconds",gSkipMiddleMarginSeconds);
390 	gSettingsRegistry->setValue<float>("loopGapLengthSeconds",gLoopGapLengthSeconds);
391 
392 	gSettingsRegistry->setValue<float>("playPositionShift",gPlayPositionShift);
393 
394 	gSettingsRegistry->setValue<string>("addCueWhilePlaying_CueName",gAddCueWhilePlaying_CueName);
395 	gSettingsRegistry->setValue<bool>("addCueWhilePlaying_Anchored",gAddCueWhilePlaying_Anchored);
396 
397 	gSettingsRegistry->setValue<string>("addCueAtClick_CueName",gAddCueAtClick_CueName);
398 	gSettingsRegistry->setValue<bool>("addCueAtClick_Anchored",gAddCueAtClick_Anchored);
399 
400 	gSettingsRegistry->setValue<unsigned>("Meters" DOT "meterUpdateTime",gMeterUpdateTime);
401 
402 	gSettingsRegistry->setValue<bool>("Meters" DOT "Level" DOT "enabled",gLevelMetersEnabled);
403 	gSettingsRegistry->setValue<unsigned>("Meters" DOT "Level" DOT "RMSWindowTime",gMeterRMSWindowTime);
404 	gSettingsRegistry->setValue<unsigned>("Meters" DOT "Level" DOT "maxPeakFallDelayTime",gMaxPeakFallDelayTime);
405 	gSettingsRegistry->setValue<double>("Meters" DOT "Level" DOT "maxPeakFallRate",gMaxPeakFallRate);
406 
407 	gSettingsRegistry->setValue<bool>("Meters" DOT "StereoPhase" DOT "enabled",gStereoPhaseMetersEnabled);
408 	gSettingsRegistry->setValue<unsigned>("Meters" DOT "StereoPhase" DOT "pointCount",gStereoPhaseMeterPointCount);
409 	gSettingsRegistry->setValue<bool>("Meters" DOT "StereoPhase" DOT "unrotate",gStereoPhaseMeterUnrotate);
410 
411 	gSettingsRegistry->setValue<bool>("Meters" DOT "Analyzer" DOT "enabled",gFrequencyAnalyzerEnabled);
412 	gSettingsRegistry->setValue<unsigned>("Meters" DOT "Analyzer" DOT "peakFallDelayTime",gAnalyzerPeakFallDelayTime);
413 	gSettingsRegistry->setValue<double>("Meters" DOT "Analyzer" DOT "peakFallRate",gAnalyzerPeakFallRate);
414 
415 	gSettingsRegistry->setValue<int>("crossfadeEdges",(int)gCrossfadeEdges);
416 	gSettingsRegistry->setValue<float>("crossfadeStartTime",gCrossfadeStartTime);
417 	gSettingsRegistry->setValue<float>("crossfadeStopTime",gCrossfadeStopTime);
418 	gSettingsRegistry->setValue<int>("crossfadeFadeMethod",(int)gCrossfadeFadeMethod);
419 }
420 
421