Skip to content
This repository has been archived by the owner on May 30, 2018. It is now read-only.

Commit

Permalink
Merge pull request #15 from Archomeda/update-timer
Browse files Browse the repository at this point in the history
Implement timer based light updates
  • Loading branch information
Archomeda committed Mar 14, 2015
2 parents dcd6101 + 19cce74 commit faebf5e
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 71 deletions.
115 changes: 54 additions & 61 deletions src/Devices/Device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ namespace lightfx {
namespace devices {

LFXE_API Device::~Device() {
this->StopLightColorUpdateWorker();
this->StopUpdateWorker();
}


Expand All @@ -38,7 +38,7 @@ namespace lightfx {
LFXE_API bool Device::Enable() {
if (!this->isEnabled) {
LOG(LogLevel::Debug, L"Enabling");
this->StartLightColorUpdateWorker();
this->StartUpdateWorker();
this->isEnabled = true;
}
return true;
Expand All @@ -47,7 +47,7 @@ namespace lightfx {
LFXE_API bool Device::Disable() {
if (this->isEnabled) {
LOG(LogLevel::Debug, L"Disabling");
this->StopLightColorUpdateWorker();
this->StopUpdateWorker();
this->isEnabled = false;
}
return true;
Expand Down Expand Up @@ -76,28 +76,24 @@ namespace lightfx {
if (this->isEnabled) {
// Place the pre-queued item into the queue
{
lock_guard<mutex> lock1(this->TimelineQueueMutex);
lock_guard<mutex> lock2(this->lightColorUpdateThreadMutex);
lock_guard<mutex> lock(this->TimelineQueueMutex);
if (flushQueue) {
this->TimelineQueue = {};
this->TimelineQueueFlush = true;
}
this->TimelineQueue.push(this->QueuedTimeline);
this->QueuedTimeline = {};
}

// Signal the update thread
this->lightColorUpdateThreadConditionVariable.notify_one();
return true;
}
return false;
}

LFXE_API bool Device::Reset() {
bool running = this->lightColorUpdateThreadRunning;
bool running = this->updateWorkerActive;

if (running) {
this->StopLightColorUpdateWorker();
this->StopUpdateWorker();
}

this->ActiveTimeline = {};
Expand All @@ -106,7 +102,7 @@ namespace lightfx {
this->TimelineQueueFlush = false;

if (running) {
this->StartLightColorUpdateWorker();
this->StartUpdateWorker();
}

return true;
Expand All @@ -132,6 +128,11 @@ namespace lightfx {
this->QueuedTimeline = timeline;
}

LFXE_API void Device::NotifyUpdate() {
this->notifyUpdateWorker = true;
this->updateWorkerCv.notify_all();
}


LFXE_API const size_t Device::GetNumberOfLights() {
return this->numberOfLights;
Expand Down Expand Up @@ -167,83 +168,75 @@ namespace lightfx {
}


LFXE_API void Device::StartLightColorUpdateWorker() {
if (!this->lightColorUpdateThreadRunning) {
// Start the update thread
this->lightColorUpdateThreadRunning = true;
this->lightColorUpdateThread = thread(&Device::LightColorUpdateWorkerThread, this);
LFXE_API void Device::StartUpdateWorker() {
if (!this->updateWorkerActive) {
this->updateWorkerActive = true;
this->stopUpdateWorker = false;
this->updateWorkerThread = thread(&Device::UpdateWorker, this);
}
}

LFXE_API void Device::StopLightColorUpdateWorker() {
if (this->lightColorUpdateThreadRunning) {
// Notify the update thread to exit
{
lock_guard<mutex> lock(this->lightColorUpdateThreadMutex);
this->lightColorUpdateThreadRunning = false;
}

this->lightColorUpdateThreadConditionVariable.notify_one();
if (this->lightColorUpdateThread.joinable()) {
this->lightColorUpdateThread.join();
LFXE_API void Device::StopUpdateWorker() {
if (this->updateWorkerActive) {
this->stopUpdateWorker = true;
this->updateWorkerCv.notify_all();
if (this->updateWorkerThread.joinable()) {
this->updateWorkerThread.join();
}
this->updateWorkerActive = false;
}
}

LFXE_API void Device::LightColorUpdateWorkerThread() {
LFXE_API void Device::UpdateWorker() {
bool isUpdating = false;
unsigned long long timelineStart = 0;

while (this->lightColorUpdateThreadRunning) {
if (!isUpdating || this->TimelineQueueFlush) {
if (this->TimelineQueue.empty()) {
// Only wait for an update if we are not currently busy updating the colors and there's nothing in the queue
unique_lock<mutex> lock(this->lightColorUpdateThreadMutex);
this->lightColorUpdateThreadConditionVariable.wait(lock, [&] { return !this->TimelineQueue.empty() || !this->lightColorUpdateThreadRunning; });
while (!this->stopUpdateWorker) {
unique_lock<mutex> lock(this->updateWorkerCvMutex);
this->updateWorkerCv.wait(lock, [&] { return this->notifyUpdateWorker && (isUpdating || !this->TimelineQueue.empty()) || this->stopUpdateWorker; });
this->notifyUpdateWorker = false;

if (!this->lightColorUpdateThreadRunning) {
// Make sure to exit the loop before trying to update the colors if the thread should be exited
break;
}
}
if (this->stopUpdateWorker) {
break;
}

{
lock_guard<mutex> lock(this->TimelineQueueMutex);
if (!this->TimelineQueue.empty()) {
if (!isUpdating || this->TimelineQueueFlush) {
// Not currently updating the color or the queue needs to be flushed
this->TimelineQueueFlush = false;
this->ActiveTimeline = this->TimelineQueue.front();
this->TimelineQueue.pop();
isUpdating = true;
{
lock_guard<mutex> lock(this->TimelineQueueMutex);
this->ActiveTimeline = this->TimelineQueue.front();
this->TimelineQueue.pop();
}
LOG(LogLevel::Debug, L"Performing timeline " + this->ActiveTimeline.ToString());
timelineStart = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
}
}

LOG(LogLevel::Debug, L"Performing timeline " + this->ActiveTimeline.ToString());
this->lightColor = this->ActiveTimeline.GetColorAtTime(0);
this->PushColorToDevice(this->lightColor);

isUpdating = true;
timelineStart = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
} else if (isUpdating) {
if (isUpdating) {
// Get the color at the current time
unsigned long timelinePos = unsigned long(chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count() - timelineStart);
vector<LightColor> newColor = this->ActiveTimeline.GetColorAtTime(timelinePos);
bool needsUpdate = false;
for (size_t i = 0; i < newColor.size(); ++i) {
if (this->lightColor[i] != newColor[i]) {
needsUpdate = true;
break;
bool needsUpdate = this->lightColor.size() != newColor.size();
if (!needsUpdate) {
for (size_t i = 0; i < newColor.size(); ++i) {
if (this->lightColor[i] != newColor[i]) {
needsUpdate = true;
break;
}
}
}

// Update color if needed
if (needsUpdate) {
this->lightColor = newColor;
this->PushColorToDevice(this->lightColor);
}

isUpdating = timelinePos <= this->ActiveTimeline.GetTotalDuration();
}

try {
this_thread::sleep_for(chrono::milliseconds(5));
} catch (...) {
return;
}
}
}

Expand Down
24 changes: 14 additions & 10 deletions src/Devices/Device.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#pragma once

// Standard includes
#include <string>
#include <mutex>
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <string>
#include <thread>
#include <queue>

Expand Down Expand Up @@ -72,6 +73,7 @@ namespace lightfx {
timelines::Timeline GetQueuedTimeline();
timelines::Timeline GetRecentTimeline();
void QueueTimeline(const timelines::Timeline& timeline);
void NotifyUpdate();

virtual const std::wstring GetDeviceName() = 0;
virtual const DeviceType GetDeviceType() = 0;
Expand All @@ -91,11 +93,11 @@ namespace lightfx {
timelines::Timeline QueuedTimeline;
std::queue<timelines::Timeline> TimelineQueue;
std::mutex TimelineQueueMutex;
bool TimelineQueueFlush = false;
std::atomic<bool> TimelineQueueFlush = false;

void StartLightColorUpdateWorker();
void StopLightColorUpdateWorker();
void LightColorUpdateWorkerThread();
void StartUpdateWorker();
void StopUpdateWorker();
void UpdateWorker();
virtual bool PushColorToDevice(const std::vector<timelines::LightColor>& colors) = 0;

private:
Expand All @@ -105,10 +107,12 @@ namespace lightfx {
std::vector<timelines::LightColor> lightColor = {};
std::vector<LightData> lightData = {};

std::thread lightColorUpdateThread;
bool lightColorUpdateThreadRunning = false;
std::mutex lightColorUpdateThreadMutex;
std::condition_variable lightColorUpdateThreadConditionVariable;
bool updateWorkerActive = false;
std::atomic<bool> notifyUpdateWorker = false;
std::atomic<bool> stopUpdateWorker = false;
std::thread updateWorkerThread;
std::condition_variable updateWorkerCv;
std::mutex updateWorkerCvMutex;

};

Expand Down
1 change: 1 addition & 0 deletions src/LightFXExtender.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@
<ClInclude Include="Timelines\Timeline.h" />
<ClInclude Include="Utils\FileIO.h" />
<ClInclude Include="Utils\String.h" />
<ClInclude Include="Utils\Timer.h" />
<ClInclude Include="Utils\Windows.h" />
<ClInclude Include="VersionInfo.h" />
</ItemGroup>
Expand Down
3 changes: 3 additions & 0 deletions src/LightFXExtender.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@
<ClInclude Include="Devices\DeviceLightFX.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Utils\Timer.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="LightFXExtender.rc">
Expand Down
30 changes: 30 additions & 0 deletions src/Managers/DeviceManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ namespace lightfx {
}

auto logitech = make_shared<DeviceLogitech>();
logitech->SetRange(config->LogitechColorRangeOutMin, config->LogitechColorRangeOutMax, config->LogitechColorRangeInMin, config->LogitechColorRangeInMax);
this->AddChild(L"Logitech", logitech);
logitech->SetG110WorkaroundEnabled(config->LogitechG110WorkaroundEnabled);
if (logitech->Initialize()) {
Expand Down Expand Up @@ -98,13 +99,20 @@ namespace lightfx {
}
}

// Start update timer
this->StartUpdateTimer();

return i;
}

LFXE_API size_t DeviceManager::UninitializeDevices() {
LOG(LogLevel::Debug, L"Uninitializing devices");
size_t i = 0;

// Stop update timer
this->StopUpdateTimer();

// Unload devices
for (size_t j = 0; j < this->GetChildrenCount(); ++j) {
auto device = this->GetChildByIndex(j);
if (device->IsEnabled() && device->Release()) {
Expand All @@ -130,5 +138,27 @@ namespace lightfx {
return i;
}


LFXE_API void DeviceManager::StartUpdateTimer() {
if (this->DeviceUpdateTimer == nullptr || !this->DeviceUpdateTimer->IsActive()) {
this->DeviceUpdateTimer = make_unique<Timer>(10, &DeviceManager::DeviceUpdateTask, this);
}
}

LFXE_API void DeviceManager::StopUpdateTimer() {
if (this->DeviceUpdateTimer != nullptr && this->DeviceUpdateTimer->IsActive()) {
this->DeviceUpdateTimer->Stop();
}
}

LFXE_API void DeviceManager::DeviceUpdateTask() {
for (size_t i = 0; i < this->GetChildrenCount(); ++i) {
auto device = this->GetChildByIndex(i);
if (device->IsEnabled()) {
device->NotifyUpdate();
}
}
}

}
}
13 changes: 13 additions & 0 deletions src/Managers/DeviceManager.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
#pragma once

// Standard includes
#include <memory>

// Project includes
#include "ManagerWithChildren.h"
#include "../Devices/Device.h"
#include "../Utils/Timer.h"

// API exports
#include "../Common/ApiExports.h"
Expand All @@ -26,6 +30,15 @@ namespace lightfx {
size_t InitializeDevices();
size_t UninitializeDevices();

void StartUpdateTimer();
void StopUpdateTimer();

protected:
void DeviceUpdateTask();

private:
std::unique_ptr<utils::Timer> DeviceUpdateTimer;

};

}
Expand Down
Loading

0 comments on commit faebf5e

Please sign in to comment.