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