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