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