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