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