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
MixerSettings()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
~MixerSettings()46 MixerSettings::~MixerSettings()
47 {
48 StopDeferredSave();
49 if (fSettingsDirty)
50 Save();
51 delete fLocker;
52 delete fSettingsFile;
53 }
54
55
56 void
SetSettingsFile(const char * file)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
AttenuateOutput()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
SetAttenuateOutput(bool yesno)79 MixerSettings::SetAttenuateOutput(bool yesno)
80 {
81 fLocker->Lock();
82 fSettings.AttenuateOutput = yesno;
83 fLocker->Unlock();
84 StartDeferredSave();
85 }
86
87
88 bool
UseBalanceControl()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
SetUseBalanceControl(bool yesno)100 MixerSettings::SetUseBalanceControl(bool yesno)
101 {
102 fLocker->Lock();
103 fSettings.UseBalanceControl = yesno;
104 fLocker->Unlock();
105 StartDeferredSave();
106 }
107
108
109 bool
AllowOutputChannelRemapping()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
SetAllowOutputChannelRemapping(bool yesno)121 MixerSettings::SetAllowOutputChannelRemapping(bool yesno)
122 {
123 fLocker->Lock();
124 fSettings.AllowOutputChannelRemapping = yesno;
125 fLocker->Unlock();
126 StartDeferredSave();
127 }
128
129
130 bool
AllowInputChannelRemapping()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
SetAllowInputChannelRemapping(bool yesno)142 MixerSettings::SetAllowInputChannelRemapping(bool yesno)
143 {
144 fLocker->Lock();
145 fSettings.AllowInputChannelRemapping = yesno;
146 fLocker->Unlock();
147 StartDeferredSave();
148 }
149
150
151 int
InputGainControls()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
SetInputGainControls(int value)163 MixerSettings::SetInputGainControls(int value)
164 {
165 fLocker->Lock();
166 fSettings.InputGainControls = value;
167 fLocker->Unlock();
168 StartDeferredSave();
169 }
170
171
172 int
ResamplingAlgorithm()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
SetResamplingAlgorithm(int value)184 MixerSettings::SetResamplingAlgorithm(int value)
185 {
186 fLocker->Lock();
187 fSettings.ResamplingAlgorithm = value;
188 fLocker->Unlock();
189 StartDeferredSave();
190 }
191
192
193 bool
RefuseOutputFormatChange()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
SetRefuseOutputFormatChange(bool yesno)205 MixerSettings::SetRefuseOutputFormatChange(bool yesno)
206 {
207 fLocker->Lock();
208 fSettings.RefuseOutputFormatChange = yesno;
209 fLocker->Unlock();
210 StartDeferredSave();
211 }
212
213
214 bool
RefuseInputFormatChange()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
SetRefuseInputFormatChange(bool yesno)226 MixerSettings::SetRefuseInputFormatChange(bool yesno)
227 {
228 fLocker->Lock();
229 fSettings.RefuseInputFormatChange = yesno;
230 fLocker->Unlock();
231 StartDeferredSave();
232 }
233
234
235 void
SaveConnectionSettings(MixerInput * input)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
LoadConnectionSettings(MixerInput * input)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
SaveConnectionSettings(MixerOutput * output)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
LoadConnectionSettings(MixerOutput * output)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
Save()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
Load()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
StartDeferredSave()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
StopDeferredSave()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
SaveThread()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
_save_thread_(void * arg)576 MixerSettings::_save_thread_(void *arg)
577 {
578 static_cast<MixerSettings *>(arg)->SaveThread();
579 return 0;
580 }
581