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