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