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