xref: /haiku/src/add-ons/media/media-add-ons/mixer/MixerSettings.cpp (revision 5d9e40fe9252c8f9c5e5e41594545bfa4419fcc7)
1 #include <MediaDefs.h>
2 #include <Locker.h>
3 #include <Path.h>
4 #include <File.h>
5 #include <OS.h>
6 
7 #include "MixerCore.h"
8 #include "MixerInput.h"
9 #include "MixerOutput.h"
10 #include "MixerSettings.h"
11 #include "debug.h"
12 
13 #define SAVE_DELAY		5000000		// delay saving of settings for 5s
14 #define SAVE_RUNTIME	30000000	// stop save thread after 30s inactivity
15 
16 #define SETTINGS_VERSION ((int32)0x94251601)
17 
18 MixerSettings::MixerSettings()
19  :	fLocker(new BLocker),
20  	fSettingsFile(0),
21 	fSettingsDirty(false),
22 	fSettingsLastChange(0),
23 	fSaveThread(-1),
24 	fSaveThreadWaitSem(-1),
25 	fSaveThreadRunning(false)
26 {
27 	Load();
28 }
29 
30 MixerSettings::~MixerSettings()
31 {
32 	StopDeferredSave();
33 	if (fSettingsDirty)
34 		Save();
35 	delete fLocker;
36 	delete fSettingsFile;
37 }
38 
39 void
40 MixerSettings::SetSettingsFile(const char *file)
41 {
42 	fLocker->Lock();
43 	delete fSettingsFile;
44 	fSettingsFile = new BPath(file);
45 	fLocker->Unlock();
46 	Load();
47 }
48 
49 bool
50 MixerSettings::AttenuateOutput()
51 {
52 	bool temp;
53 	fLocker->Lock();
54 	temp = fSettings.AttenuateOutput;
55 	fLocker->Unlock();
56 	return temp;
57 }
58 
59 void
60 MixerSettings::SetAttenuateOutput(bool yesno)
61 {
62 	fLocker->Lock();
63 	fSettings.AttenuateOutput = yesno;
64 	fLocker->Unlock();
65 	StartDeferredSave();
66 }
67 
68 bool
69 MixerSettings::NonLinearGainSlider()
70 {
71 	bool temp;
72 	fLocker->Lock();
73 	temp = fSettings.NonLinearGainSlider;
74 	fLocker->Unlock();
75 	return temp;
76 }
77 
78 void
79 MixerSettings::SetNonLinearGainSlider(bool yesno)
80 {
81 	fLocker->Lock();
82 	fSettings.NonLinearGainSlider = yesno;
83 	fLocker->Unlock();
84 	StartDeferredSave();
85 }
86 
87 bool
88 MixerSettings::UseBalanceControl()
89 {
90 	bool temp;
91 	fLocker->Lock();
92 	temp = fSettings.UseBalanceControl;
93 	fLocker->Unlock();
94 	return temp;
95 }
96 
97 void
98 MixerSettings::SetUseBalanceControl(bool yesno)
99 {
100 	fLocker->Lock();
101 	fSettings.UseBalanceControl = yesno;
102 	fLocker->Unlock();
103 	StartDeferredSave();
104 }
105 
106 bool
107 MixerSettings::AllowOutputChannelRemapping()
108 {
109 	bool temp;
110 	fLocker->Lock();
111 	temp = fSettings.AllowOutputChannelRemapping;
112 	fLocker->Unlock();
113 	return temp;
114 }
115 
116 void
117 MixerSettings::SetAllowOutputChannelRemapping(bool yesno)
118 {
119 	fLocker->Lock();
120 	fSettings.AllowOutputChannelRemapping = yesno;
121 	fLocker->Unlock();
122 	StartDeferredSave();
123 }
124 
125 bool
126 MixerSettings::AllowInputChannelRemapping()
127 {
128 	bool temp;
129 	fLocker->Lock();
130 	temp = fSettings.AllowInputChannelRemapping;
131 	fLocker->Unlock();
132 	return temp;
133 }
134 
135 void
136 MixerSettings::SetAllowInputChannelRemapping(bool yesno)
137 {
138 	fLocker->Lock();
139 	fSettings.AllowInputChannelRemapping = yesno;
140 	fLocker->Unlock();
141 	StartDeferredSave();
142 }
143 
144 int
145 MixerSettings::InputGainControls()
146 {
147 	int temp;
148 	fLocker->Lock();
149 	temp = fSettings.InputGainControls;
150 	fLocker->Unlock();
151 	return temp;
152 }
153 
154 void
155 MixerSettings::SetInputGainControls(int value)
156 {
157 	fLocker->Lock();
158 	fSettings.InputGainControls = value;
159 	fLocker->Unlock();
160 	StartDeferredSave();
161 }
162 
163 int
164 MixerSettings::ResamplingAlgorithm()
165 {
166 	int temp;
167 	fLocker->Lock();
168 	temp = fSettings.ResamplingAlgorithm;
169 	fLocker->Unlock();
170 	return temp;
171 }
172 
173 void
174 MixerSettings::SetResamplingAlgorithm(int value)
175 {
176 	fLocker->Lock();
177 	fSettings.ResamplingAlgorithm = value;
178 	fLocker->Unlock();
179 	StartDeferredSave();
180 }
181 
182 bool
183 MixerSettings::RefuseOutputFormatChange()
184 {
185 	bool temp;
186 	fLocker->Lock();
187 	temp = fSettings.RefuseOutputFormatChange;
188 	fLocker->Unlock();
189 	return temp;
190 }
191 
192 void
193 MixerSettings::SetRefuseOutputFormatChange(bool yesno)
194 {
195 	fLocker->Lock();
196 	fSettings.RefuseOutputFormatChange = yesno;
197 	fLocker->Unlock();
198 	StartDeferredSave();
199 }
200 
201 bool
202 MixerSettings::RefuseInputFormatChange()
203 {
204 	bool temp;
205 	fLocker->Lock();
206 	temp = fSettings.RefuseInputFormatChange;
207 	fLocker->Unlock();
208 	return temp;
209 }
210 
211 void
212 MixerSettings::SetRefuseInputFormatChange(bool yesno)
213 {
214 	fLocker->Lock();
215 	fSettings.RefuseInputFormatChange = yesno;
216 	fLocker->Unlock();
217 	StartDeferredSave();
218 }
219 
220 void
221 MixerSettings::SaveConnectionSettings(MixerInput *input)
222 {
223 	fLocker->Lock();
224 	int index = -1;
225 
226 	// try to find matching name first
227 	for (int i = 0; i < MAX_INPUT_SETTINGS; i++) {
228 		if (fInputSetting[i].IsEmpty())
229 			continue;
230 		if (0 == strcmp(fInputSetting[i].FindString("name"), input->MediaInput().name)) {
231 			index = i;
232 			break;
233 		}
234 	}
235 	if (index == -1) {
236 		// try to find empty location
237 		for (int i = 0; i < MAX_INPUT_SETTINGS; i++) {
238 			if (fInputSetting[i].IsEmpty()) {
239 				index = i;
240 				break;
241 			}
242 		}
243 	}
244 	if (index == -1) {
245 		// find lru location
246 		index = 0;
247 		for (int i = 0; i < MAX_INPUT_SETTINGS; i++) {
248 			if (fInputSetting[i].FindInt64("lru") < fInputSetting[index].FindInt64("lru"))
249 				index = i;
250 		}
251 	}
252 
253 	TRACE("SaveConnectionSettings: using entry %d\n", index);
254 
255 	fInputSetting[index].MakeEmpty();
256 	fInputSetting[index].AddInt64("lru", system_time());
257 	fInputSetting[index].AddString("name", input->MediaInput().name);
258 
259 	int count = input->GetInputChannelCount();
260 	fInputSetting[index].AddInt32("InputChannelCount", count);
261 	fInputSetting[index].AddBool("InputIsEnabled", input->IsEnabled());
262 
263 	for (int i = 0; i < count; i++)
264 		fInputSetting[index].AddFloat("InputChannelGain", input->GetInputChannelGain(i));
265 
266 	// XXX should save channel destinations and mixer channels
267 
268 	fLocker->Unlock();
269 
270 	StartDeferredSave();
271 }
272 
273 void
274 MixerSettings::LoadConnectionSettings(MixerInput *input)
275 {
276 	fLocker->Lock();
277 	int index;
278 	for (index = 0; index < MAX_INPUT_SETTINGS; index++) {
279 		if (fInputSetting[index].IsEmpty())
280 			continue;
281 		if (0 == strcmp(fInputSetting[index].FindString("name"), input->MediaInput().name))
282 			break;
283 	}
284 	if (index == MAX_INPUT_SETTINGS) {
285 		TRACE("LoadConnectionSettings: entry not found\n");
286 		fLocker->Unlock();
287 		return;
288 	}
289 
290 	TRACE("LoadConnectionSettings: found entry %d\n", index);
291 
292 	int count = input->GetInputChannelCount();
293 	if (fInputSetting[index].FindInt32("InputChannelCount") == count) {
294 		for (int i = 0; i < count; i++)
295 			input->SetInputChannelGain(i, fInputSetting[index].FindFloat("InputChannelGain", i));
296 		input->SetEnabled(fInputSetting[index].FindBool("InputIsEnabled"));
297 	}
298 
299 	// XXX should load channel destinations and mixer channels
300 
301 	fInputSetting[index].ReplaceInt64("lru", system_time());
302 
303 	fLocker->Unlock();
304 
305 	StartDeferredSave();
306 }
307 
308 void
309 MixerSettings::SaveConnectionSettings(MixerOutput *output)
310 {
311 	fLocker->Lock();
312 
313 	fOutputSetting.MakeEmpty();
314 
315 	int count = output->GetOutputChannelCount();
316 	fOutputSetting.AddInt32("OutputChannelCount", count);
317 	for (int i = 0; i < count; i++)
318 		fOutputSetting.AddFloat("OutputChannelGain", output->GetOutputChannelGain(i));
319 	fOutputSetting.AddBool("OutputIsMuted", output->IsMuted());
320 
321 	// XXX should save channel sources and source gains
322 
323 	fLocker->Unlock();
324 
325 	StartDeferredSave();
326 }
327 
328 void
329 MixerSettings::LoadConnectionSettings(MixerOutput *output)
330 {
331 	fLocker->Lock();
332 
333 	int count = output->GetOutputChannelCount();
334 	if (fOutputSetting.FindInt32("OutputChannelCount") == count) {
335 		for (int i = 0; i < count; i++)
336 			output->SetOutputChannelGain(i, fOutputSetting.FindFloat("OutputChannelGain", i));
337 		output->SetMuted(fOutputSetting.FindBool("OutputIsMuted"));
338 	}
339 
340 	// XXX should load channel sources and source gains
341 
342 	fLocker->Unlock();
343 }
344 
345 void
346 MixerSettings::Save()
347 {
348 	fLocker->Lock();
349 	// if we don't have a settings file, don't continue
350 	if (!fSettingsFile) {
351 		fLocker->Unlock();
352 		return;
353 	}
354 	TRACE("MixerSettings: SAVE!\n");
355 
356 	BMessage msg;
357 	msg.AddInt32("version", SETTINGS_VERSION);
358 	msg.AddData("settings", B_RAW_TYPE, (void *)&fSettings, sizeof(fSettings));
359 	msg.AddMessage("output", &fOutputSetting);
360 	for (int i = 0; i < MAX_INPUT_SETTINGS; i++)
361 		msg.AddMessage("input", &fInputSetting[i]);
362 
363 	char *buffer;
364     size_t length;
365 
366     length = msg.FlattenedSize();
367     buffer = new char [length];
368     msg.Flatten(buffer, length);
369 
370 	BFile file(fSettingsFile->Path(), B_READ_WRITE | B_CREATE_FILE);
371 	file.Write(buffer, length);
372 
373    	delete [] buffer;
374 
375 	fSettingsDirty = false;
376 	fLocker->Unlock();
377 }
378 
379 void
380 MixerSettings::Load()
381 {
382 	fLocker->Lock();
383 	// setup defaults
384 	fSettings.AttenuateOutput = true;
385 	fSettings.NonLinearGainSlider = true;
386 	fSettings.UseBalanceControl = false;
387 	fSettings.AllowOutputChannelRemapping = false;
388 	fSettings.AllowInputChannelRemapping = false;
389 	fSettings.InputGainControls = 0;
390 	fSettings.ResamplingAlgorithm = 0;
391 	fSettings.RefuseOutputFormatChange = true;
392 	fSettings.RefuseInputFormatChange = true;
393 
394 	// if we don't have a settings file, don't continue
395 	if (!fSettingsFile) {
396 		fLocker->Unlock();
397 		return;
398 	}
399 
400 	BFile file(fSettingsFile->Path(), B_READ_WRITE);
401 	off_t size = 0;
402 	file.GetSize(&size);
403 	if (size == 0) {
404 		fLocker->Unlock();
405 		TRACE("MixerSettings: no settings file\n");
406 		return;
407 	}
408 
409 	char * buffer = new char[size];
410 	if (size != file.Read(buffer, size)) {
411 		delete [] buffer;
412 		fLocker->Unlock();
413 		TRACE("MixerSettings: can't read settings file\n");
414 		return;
415 	}
416 
417 	BMessage msg;
418 	if (B_OK != msg.Unflatten(buffer)) {
419 		delete [] buffer;
420 		fLocker->Unlock();
421 		TRACE("MixerSettings: can't unflatten settings\n");
422 		return;
423 	}
424 
425 	delete [] buffer;
426 
427 	if (msg.FindInt32("version") != SETTINGS_VERSION) {
428 		fLocker->Unlock();
429 		TRACE("MixerSettings: settings have wrong version\n");
430 		return;
431 	}
432 
433 	const void *data;
434 	ssize_t datasize = 0;
435 
436 	msg.FindData("settings", B_RAW_TYPE, &data, &datasize);
437 	if (datasize != sizeof(fSettings)) {
438 		fLocker->Unlock();
439 		TRACE("MixerSettings: settings have wrong size\n");
440 		return;
441 	}
442 	memcpy((void *)&fSettings, data, sizeof(fSettings));
443 
444 	msg.FindMessage("output", &fOutputSetting);
445 	for (int i = 0; i < MAX_INPUT_SETTINGS; i++)
446 		msg.FindMessage("input", i, &fInputSetting[i]);
447 
448 	fLocker->Unlock();
449 }
450 
451 void
452 MixerSettings::StartDeferredSave()
453 {
454 	fLocker->Lock();
455 
456 	// if we don't have a settings file, don't save the settings
457 	if (!fSettingsFile) {
458 		fLocker->Unlock();
459 		return;
460 	}
461 
462 	fSettingsDirty = true;
463 	fSettingsLastChange = system_time();
464 
465 	if (fSaveThreadRunning) {
466 		fLocker->Unlock();
467 		return;
468 	}
469 
470 	StopDeferredSave();
471 
472 	ASSERT(fSaveThreadWaitSem < 0);
473 	fSaveThreadWaitSem = create_sem(0, "save thread wait");
474 	if (fSaveThreadWaitSem < B_OK) {
475 		ERROR("MixerSettings: can't create semaphore\n");
476 		Save();
477 		fLocker->Unlock();
478 		return;
479 	}
480 	ASSERT(fSaveThread < 0);
481 	fSaveThread = spawn_thread(_save_thread_, "Attack of the Killer Tomatoes", 7, this);
482 	if (fSaveThread < B_OK) {
483 		ERROR("MixerSettings: can't spawn thread\n");
484 		delete_sem(fSaveThreadWaitSem);
485 		fSaveThreadWaitSem = -1;
486 		Save();
487 		fLocker->Unlock();
488 		return;
489 	}
490 	resume_thread(fSaveThread);
491 
492 	fSaveThreadRunning = true;
493 
494 	fLocker->Unlock();
495 }
496 
497 void
498 MixerSettings::StopDeferredSave()
499 {
500 	fLocker->Lock();
501 
502 	if (fSaveThread > 0) {
503 		ASSERT(fSaveThreadWaitSem > 0);
504 
505 		status_t unused;
506 		delete_sem(fSaveThreadWaitSem);
507 		wait_for_thread(fSaveThread, &unused);
508 
509 		fSaveThread = -1;
510 		fSaveThreadWaitSem = -1;
511 	}
512 
513 	fLocker->Unlock();
514 }
515 
516 void
517 MixerSettings::SaveThread()
518 {
519 	bigtime_t timeout;
520 	status_t rv;
521 
522 	TRACE("MixerSettings: save thread started\n");
523 
524 	fLocker->Lock();
525 	timeout = fSettingsLastChange + SAVE_DELAY;
526 	fLocker->Unlock();
527 
528 	for (;;) {
529 		rv = acquire_sem_etc(fSaveThreadWaitSem, 1, B_ABSOLUTE_TIMEOUT, timeout);
530 		if (rv == B_INTERRUPTED)
531 			continue;
532 		if (rv != B_TIMED_OUT && rv < B_OK)
533 			break;
534 		if (B_OK != fLocker->LockWithTimeout(200000))
535 			continue;
536 
537 		TRACE("MixerSettings: save thread running\n");
538 
539 		bigtime_t delta = system_time() - fSettingsLastChange;
540 
541 		if (fSettingsDirty && delta > SAVE_DELAY) {
542 			Save();
543 		}
544 
545 		if (delta > SAVE_RUNTIME) {
546 			fSaveThreadRunning = false;
547 			fLocker->Unlock();
548 			break;
549 		}
550 
551 		timeout = system_time() + SAVE_DELAY;
552 		fLocker->Unlock();
553 	}
554 
555 	TRACE("MixerSettings: save thread ended\n");
556 }
557 
558 int32
559 MixerSettings::_save_thread_(void *arg)
560 {
561 	static_cast<MixerSettings *>(arg)->SaveThread();
562 	return 0;
563 }
564