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