#include "MixerSettings.h"
#include <string.h>
#include <File.h>
#include <Locker.h>
#include <MediaDefs.h>
#include <OS.h>
#include <Path.h>
#include "MixerCore.h"
#include "MixerDebug.h"
#include "MixerInput.h"
#include "MixerOutput.h"
#define SAVE_DELAY 5000000
#define SAVE_RUNTIME 30000000
#define SETTINGS_VERSION ((int32)0x94251601)
MixerSettings::MixerSettings()
:
fLocker(new BLocker("mixer settings lock")),
fSettingsFile(0),
fSettingsDirty(false),
fSettingsLastChange(0),
fSaveThread(-1),
fSaveThreadWaitSem(-1),
fSaveThreadRunning(false)
{
Load();
}
MixerSettings::~MixerSettings()
{
StopDeferredSave();
if (fSettingsDirty)
Save();
delete fLocker;
delete fSettingsFile;
}
void
MixerSettings::SetSettingsFile(const char *file)
{
fLocker->Lock();
delete fSettingsFile;
fSettingsFile = new BPath(file);
fLocker->Unlock();
Load();
}
bool
MixerSettings::AttenuateOutput()
{
bool temp;
fLocker->Lock();
temp = fSettings.AttenuateOutput;
fLocker->Unlock();
return temp;
}
void
MixerSettings::SetAttenuateOutput(bool yesno)
{
fLocker->Lock();
fSettings.AttenuateOutput = yesno;
fLocker->Unlock();
StartDeferredSave();
}
bool
MixerSettings::UseBalanceControl()
{
bool temp;
fLocker->Lock();
temp = fSettings.UseBalanceControl;
fLocker->Unlock();
return temp;
}
void
MixerSettings::SetUseBalanceControl(bool yesno)
{
fLocker->Lock();
fSettings.UseBalanceControl = yesno;
fLocker->Unlock();
StartDeferredSave();
}
bool
MixerSettings::AllowOutputChannelRemapping()
{
bool temp;
fLocker->Lock();
temp = fSettings.AllowOutputChannelRemapping;
fLocker->Unlock();
return temp;
}
void
MixerSettings::SetAllowOutputChannelRemapping(bool yesno)
{
fLocker->Lock();
fSettings.AllowOutputChannelRemapping = yesno;
fLocker->Unlock();
StartDeferredSave();
}
bool
MixerSettings::AllowInputChannelRemapping()
{
bool temp;
fLocker->Lock();
temp = fSettings.AllowInputChannelRemapping;
fLocker->Unlock();
return temp;
}
void
MixerSettings::SetAllowInputChannelRemapping(bool yesno)
{
fLocker->Lock();
fSettings.AllowInputChannelRemapping = yesno;
fLocker->Unlock();
StartDeferredSave();
}
int
MixerSettings::InputGainControls()
{
int temp;
fLocker->Lock();
temp = fSettings.InputGainControls;
fLocker->Unlock();
return temp;
}
void
MixerSettings::SetInputGainControls(int value)
{
fLocker->Lock();
fSettings.InputGainControls = value;
fLocker->Unlock();
StartDeferredSave();
}
int
MixerSettings::ResamplingAlgorithm()
{
int temp;
fLocker->Lock();
temp = fSettings.ResamplingAlgorithm;
fLocker->Unlock();
return temp;
}
void
MixerSettings::SetResamplingAlgorithm(int value)
{
fLocker->Lock();
fSettings.ResamplingAlgorithm = value;
fLocker->Unlock();
StartDeferredSave();
}
bool
MixerSettings::RefuseOutputFormatChange()
{
bool temp;
fLocker->Lock();
temp = fSettings.RefuseOutputFormatChange;
fLocker->Unlock();
return temp;
}
void
MixerSettings::SetRefuseOutputFormatChange(bool yesno)
{
fLocker->Lock();
fSettings.RefuseOutputFormatChange = yesno;
fLocker->Unlock();
StartDeferredSave();
}
bool
MixerSettings::RefuseInputFormatChange()
{
bool temp;
fLocker->Lock();
temp = fSettings.RefuseInputFormatChange;
fLocker->Unlock();
return temp;
}
void
MixerSettings::SetRefuseInputFormatChange(bool yesno)
{
fLocker->Lock();
fSettings.RefuseInputFormatChange = yesno;
fLocker->Unlock();
StartDeferredSave();
}
void
MixerSettings::SaveConnectionSettings(MixerInput *input)
{
fLocker->Lock();
int index = -1;
for (int i = 0; i < MAX_INPUT_SETTINGS; i++) {
if (fInputSetting[i].IsEmpty())
continue;
if (!strcmp(fInputSetting[i].FindString("name"), input->MediaInput().name)) {
index = i;
break;
}
}
if (index == -1) {
for (int i = 0; i < MAX_INPUT_SETTINGS; i++) {
if (fInputSetting[i].IsEmpty()) {
index = i;
break;
}
}
}
if (index == -1) {
index = 0;
for (int i = 0; i < MAX_INPUT_SETTINGS; i++) {
if (fInputSetting[i].FindInt64("lru") < fInputSetting[index].FindInt64("lru"))
index = i;
}
}
TRACE("SaveConnectionSettings: using entry %d\n", index);
fInputSetting[index].MakeEmpty();
fInputSetting[index].AddInt64("lru", system_time());
fInputSetting[index].AddString("name", input->MediaInput().name);
int count = input->GetInputChannelCount();
fInputSetting[index].AddInt32("InputChannelCount", count);
fInputSetting[index].AddBool("InputIsEnabled", input->IsEnabled());
for (int i = 0; i < count; i++)
fInputSetting[index].AddFloat("InputChannelGain", input->GetInputChannelGain(i));
fLocker->Unlock();
StartDeferredSave();
}
void
MixerSettings::LoadConnectionSettings(MixerInput *input)
{
fLocker->Lock();
int index;
for (index = 0; index < MAX_INPUT_SETTINGS; index++) {
if (fInputSetting[index].IsEmpty())
continue;
if (!strcmp(fInputSetting[index].FindString("name"), input->MediaInput().name))
break;
}
if (index == MAX_INPUT_SETTINGS) {
TRACE("LoadConnectionSettings: entry not found\n");
fLocker->Unlock();
return;
}
TRACE("LoadConnectionSettings: found entry %d\n", index);
int count = input->GetInputChannelCount();
if (fInputSetting[index].FindInt32("InputChannelCount") == count) {
for (int i = 0; i < count; i++)
input->SetInputChannelGain(i, fInputSetting[index].FindFloat("InputChannelGain", i));
input->SetEnabled(fInputSetting[index].FindBool("InputIsEnabled"));
}
fInputSetting[index].ReplaceInt64("lru", system_time());
fLocker->Unlock();
StartDeferredSave();
}
void
MixerSettings::SaveConnectionSettings(MixerOutput *output)
{
fLocker->Lock();
fOutputSetting.MakeEmpty();
int count = output->GetOutputChannelCount();
fOutputSetting.AddInt32("OutputChannelCount", count);
for (int i = 0; i < count; i++)
fOutputSetting.AddFloat("OutputChannelGain", output->GetOutputChannelGain(i));
fOutputSetting.AddBool("OutputIsMuted", output->IsMuted());
fLocker->Unlock();
StartDeferredSave();
}
void
MixerSettings::LoadConnectionSettings(MixerOutput *output)
{
fLocker->Lock();
int count = output->GetOutputChannelCount();
if (fOutputSetting.FindInt32("OutputChannelCount") == count) {
for (int i = 0; i < count; i++)
output->SetOutputChannelGain(i, fOutputSetting.FindFloat("OutputChannelGain", i));
output->SetMuted(fOutputSetting.FindBool("OutputIsMuted"));
}
fLocker->Unlock();
}
void
MixerSettings::Save()
{
fLocker->Lock();
if (!fSettingsFile) {
fLocker->Unlock();
return;
}
TRACE("MixerSettings: SAVE!\n");
BMessage msg;
msg.AddInt32("version", SETTINGS_VERSION);
msg.AddData("settings", B_RAW_TYPE, (void *)&fSettings, sizeof(fSettings));
msg.AddMessage("output", &fOutputSetting);
for (int i = 0; i < MAX_INPUT_SETTINGS; i++)
msg.AddMessage("input", &fInputSetting[i]);
char *buffer;
size_t length;
length = msg.FlattenedSize();
buffer = new char [length];
msg.Flatten(buffer, length);
BFile file(fSettingsFile->Path(), B_READ_WRITE | B_CREATE_FILE);
file.Write(buffer, length);
delete [] buffer;
fSettingsDirty = false;
fLocker->Unlock();
}
void
MixerSettings::Load()
{
fLocker->Lock();
fSettings.AttenuateOutput = true;
fSettings.UseBalanceControl = false;
fSettings.AllowOutputChannelRemapping = false;
fSettings.AllowInputChannelRemapping = false;
fSettings.InputGainControls = 0;
fSettings.ResamplingAlgorithm = 2;
fSettings.RefuseOutputFormatChange = false;
fSettings.RefuseInputFormatChange = true;
if (!fSettingsFile) {
fLocker->Unlock();
return;
}
BFile file(fSettingsFile->Path(), B_READ_WRITE);
off_t size = 0;
file.GetSize(&size);
if (size == 0) {
fLocker->Unlock();
TRACE("MixerSettings: no settings file\n");
return;
}
char * buffer = new char[size];
if (size != file.Read(buffer, size)) {
delete [] buffer;
fLocker->Unlock();
TRACE("MixerSettings: can't read settings file\n");
return;
}
BMessage msg;
if (B_OK != msg.Unflatten(buffer)) {
delete [] buffer;
fLocker->Unlock();
TRACE("MixerSettings: can't unflatten settings\n");
return;
}
delete [] buffer;
if (msg.FindInt32("version") != SETTINGS_VERSION) {
fLocker->Unlock();
TRACE("MixerSettings: settings have wrong version\n");
return;
}
const void *data;
ssize_t datasize = 0;
msg.FindData("settings", B_RAW_TYPE, &data, &datasize);
if (datasize != sizeof(fSettings)) {
fLocker->Unlock();
TRACE("MixerSettings: settings have wrong size\n");
return;
}
memcpy((void *)&fSettings, data, sizeof(fSettings));
msg.FindMessage("output", &fOutputSetting);
for (int i = 0; i < MAX_INPUT_SETTINGS; i++)
msg.FindMessage("input", i, &fInputSetting[i]);
fLocker->Unlock();
}
void
MixerSettings::StartDeferredSave()
{
fLocker->Lock();
if (!fSettingsFile) {
fLocker->Unlock();
return;
}
fSettingsDirty = true;
fSettingsLastChange = system_time();
if (fSaveThreadRunning) {
fLocker->Unlock();
return;
}
StopDeferredSave();
ASSERT(fSaveThreadWaitSem < 0);
fSaveThreadWaitSem = create_sem(0, "save thread wait");
if (fSaveThreadWaitSem < B_OK) {
ERROR("MixerSettings: can't create semaphore\n");
Save();
fLocker->Unlock();
return;
}
ASSERT(fSaveThread < 0);
fSaveThread = spawn_thread(_save_thread_, "Attack of the Killer Tomatoes", 7, this);
if (fSaveThread < B_OK) {
ERROR("MixerSettings: can't spawn thread\n");
delete_sem(fSaveThreadWaitSem);
fSaveThreadWaitSem = -1;
Save();
fLocker->Unlock();
return;
}
resume_thread(fSaveThread);
fSaveThreadRunning = true;
fLocker->Unlock();
}
void
MixerSettings::StopDeferredSave()
{
fLocker->Lock();
if (fSaveThread >= 0) {
ASSERT(fSaveThreadWaitSem > 0);
status_t unused;
delete_sem(fSaveThreadWaitSem);
wait_for_thread(fSaveThread, &unused);
fSaveThread = -1;
fSaveThreadWaitSem = -1;
}
fLocker->Unlock();
}
void
MixerSettings::SaveThread()
{
bigtime_t timeout;
status_t rv;
TRACE("MixerSettings: save thread started\n");
fLocker->Lock();
timeout = fSettingsLastChange + SAVE_DELAY;
fLocker->Unlock();
for (;;) {
rv = acquire_sem_etc(fSaveThreadWaitSem, 1, B_ABSOLUTE_TIMEOUT, timeout);
if (rv == B_INTERRUPTED)
continue;
if (rv != B_TIMED_OUT && rv < B_OK)
break;
if (B_OK != fLocker->LockWithTimeout(200000))
continue;
TRACE("MixerSettings: save thread running\n");
bigtime_t delta = system_time() - fSettingsLastChange;
if (fSettingsDirty && delta > SAVE_DELAY) {
Save();
}
if (delta > SAVE_RUNTIME) {
fSaveThreadRunning = false;
fLocker->Unlock();
break;
}
timeout = system_time() + SAVE_DELAY;
fLocker->Unlock();
}
TRACE("MixerSettings: save thread ended\n");
}
int32
MixerSettings::_save_thread_(void *arg)
{
static_cast<MixerSettings *>(arg)->SaveThread();
return 0;
}