Example #1
0
        private static LED_Set MergeSnapshotsDown(List<LED_Set> LightSnapshots)
        {
            if (LightSnapshots.Count <= 0) {
                return null;
            }
            LED_Set MergedLayers = new LED_Set();
            for (int index = 0; index < LightSnapshots[0].LightCount; index++) {
                MergedLayers.LED_Values.Add (new LED (0, 0, 0, 0));
            }

            List<int> snapshots_requesting_replacement = new List<int> ();

            for (int i = 0; i < LightSnapshots.Count; i++) {
                if (LightSnapshots [i].BlendMode == LED.BlendingStyle.Favor || LightSnapshots [i].BlendMode == LED.BlendingStyle.Mask || LightSnapshots [i].BlendMode == LED.BlendingStyle.Replace) {
                    snapshots_requesting_replacement.Add (i);
                } else {
                    // Blend the layers together
                    LightProcessing.MergeLayerDown (LightSnapshots [i].LED_Values, MergedLayers.LED_Values, LightSnapshots[i].BlendMode);
                }
            }

            // These must be done after standard blending modes due to the possibility of overriding the above
            foreach (int snapshot_index in snapshots_requesting_replacement) {
                LightProcessing.MergeLayerDown (LightSnapshots [snapshot_index].LED_Values, MergedLayers.LED_Values, LightSnapshots[snapshot_index].BlendMode);
            }
            return MergedLayers;
        }
Example #2
0
 public LED_Set Clone()
 {
     LED_Set cloned_set = new LED_Set (0);
     cloned_set.BlendMode = BlendMode;
     foreach (LED LED_to_clone in LED_Values) {
         cloned_set.LED_Values.Add (LED_to_clone.Clone ());
     }
     return cloned_set;
 }
Example #3
0
        private static void Actinic_Light_Run_Queue()
        {
            Actinic_Lights_Queue.ClearQueue ();

            // Current LED frame to send to output
            LED_Set Light_Snapshot = null;
            // All individual frames to send to output
            List<LED_Set> Light_Snapshots = new List<LED_Set> ();
            // Current VU volumes as collected from the VU input system; only used for AbstractReactiveAnimation
            List<double> Audio_Volumes_Snapshot = new List<double> ();

            // Keeps track of queue performance to maintain consistent FPS
            System.Diagnostics.Stopwatch Queue_PerfStopwatch = new System.Diagnostics.Stopwatch ();
            #if DEBUG_PERFORMANCE
            bool wasIdle = false;
            #endif
            while (true) {
                // Keep track of how long these steps take to maintain a consistent FPS
                Queue_PerfStopwatch.Restart ();

                // -- ReactiveAnimation-specific queue management --
                // Note: For now, only the base layer can be a ReactiveAnimation; overlays will not get updated
                AbstractReactiveAnimation selected_reactive_animation = Actinic_Lights_Queue.SelectedAnimation as AbstractReactiveAnimation;
                if (Actinic_Lights_Queue.AnimationActive && selected_reactive_animation != null) {
            #if DEBUG_VU_PERFORMANCE
                    VU_Processing_PerfStopwatch.Restart ();
            #endif
            #if DEBUG_PERFORMANCE
                    Console.WriteLine ("{0} ms - updating audio snapshot", Queue_PerfStopwatch.ElapsedMilliseconds);
            #endif
                    // Grab a snapshot of the current audio volumes
                    if (ActiveAudioInputSystem.Running) {
                        double[] Audio_Volumes = ActiveAudioInputSystem.GetSnapshot ();
                        while (Audio_Volumes_Snapshot.Count < Audio_Volumes.Length) {
                            Audio_Volumes_Snapshot.Add (0);
                        }
                        while (Audio_Volumes_Snapshot.Count > Audio_Volumes.Length) {
                            Audio_Volumes_Snapshot.RemoveAt (Audio_Volumes_Snapshot.Count - 1);
                        }
                        for (int i = 0; i < Audio_Volumes.Length; i++) {
                            Audio_Volumes_Snapshot [i] = Audio_Volumes [i];
                        }
                    }

                    // Update the current reactive animation with this volume snapshot
                    selected_reactive_animation.UpdateAudioSnapshot (Audio_Volumes_Snapshot);
                    ReactiveSystem.PrintAudioInformationToConsole (selected_reactive_animation);
            #if DEBUG_VU_PERFORMANCE
                    Console.WriteLine ("# Time until acknowledged: {0}", VU_Processing_PerfStopwatch.ElapsedMilliseconds);
            #endif
                }

                // -- Animation-specific queue management --
                foreach (KeyValuePair <string, LED_Queue> queue in GetAllQueues ()) {
                    if (queue.Value.AnimationActive) {
                        UpdateAnimationStackForQueue (queue.Value, Queue_PerfStopwatch.ElapsedMilliseconds, queue.Key);
                    }
                }

                // -- Generic Light Queue output --
                // Fixed: with multi-threaded timing, the light-queue could become empty between checking the count and pulling a snapshot

                if (ActiveOutputSystemReady ()) {
                    LED_Set QueueLightSnapshot = null;
                    bool update_needed = false;
                    foreach (KeyValuePair <string, LED_Queue> queue in GetAllQueues ()) {
                        if (queue.Value.QueueEmpty == false) {
                            update_needed = true;
                            break;
                        }
                    }
                    foreach (KeyValuePair <string, LED_Queue> queue in GetAllQueues ()) {
                        QueueLightSnapshot = queue.Value.PopFromQueue ();
                        if (QueueLightSnapshot != null) {
            #if DEBUG_PERFORMANCE
                            Console.WriteLine ("{0} ms - grabbing snapshot from light queue ({1})", Queue_PerfStopwatch.ElapsedMilliseconds, queue.Key);
            #endif
                            Light_Snapshots.Add (QueueLightSnapshot);
                            queue.Value.QueueIdleTime = 0;
                            queue.Value.Lights = QueueLightSnapshot.LED_Values;
            #if DEBUG_PERFORMANCE
                            Console.WriteLine ("{0} ms - updating last processed ({1})", Queue_PerfStopwatch.ElapsedMilliseconds, queue.Key);
            #endif
                            queue.Value.MarkAsProcessed ();
                        } else if (update_needed) {
                            // Nothing new in the queue, but it must be added to the snapshot for it to be blended down again
                            LED_Set last_processed_set = new LED_Set (queue.Value.LightsLastProcessed);
                            last_processed_set.BlendMode = queue.Value.BlendMode;
                            Light_Snapshots.Add (last_processed_set);
                        }
                    }
                }

                // Do this after the above to ensure any remaining queue entries will be pushed out
                bool update_after_deletion_needed = false;
                lock (Actinic_Lights_Overlay_Queues) {
                    List<string> EmptyQueues = new List<string> ();
                    IAnimationOneshot selected_oneshot_animation = null;
                    bool deletionRequested = false;
                    bool queueWithMaskBlendingActive = false;
                    foreach (KeyValuePair <string, LED_Queue> queue in Actinic_Lights_Overlay_Queues) {
                        selected_oneshot_animation = queue.Value.SelectedAnimation as IAnimationOneshot;
                        if (queue.Value.BlendMode == LED.BlendingStyle.Mask || queue.Value.BlendMode == LED.BlendingStyle.Replace)
                            queueWithMaskBlendingActive = true;
                        if ((queue.Value.LightsHaveNoEffect) || (selected_oneshot_animation != null && selected_oneshot_animation.AnimationFinished)) {
                            HaltActivity (queue.Value, true);
                            EmptyQueues.Add (queue.Key);
                            deletionRequested = true;
                        }
                    }

                    if (deletionRequested && queueWithMaskBlendingActive)
                        update_after_deletion_needed = true;
                    // Test case:
                    //  color all 0 255 0
                    //  brightness all 100
                    //  overlay test identify && overlay test blending replace
                    //  overlay voice-notif color all 255 0 0 keep && overlay voice-notif blending favor && overlay voice-notif brightness all 255
                    // [wait a little while]
                    //  overlay voice-notif brightness all 0 && overlay voice-notif color all 0 0 0 keep
                    // [test layer should reappear -and- have the correct color/brightness values]
                    // Continuing the test case:
                    //  overlay clear_all
                    // [only green as set above should show]

                    foreach (string key in EmptyQueues) {
            #if DEBUG_OVERLAY_MANAGEMENT
                        Console.WriteLine (" {0} ms - removing overlay '{1}' as it is empty", Queue_PerfStopwatch.ElapsedMilliseconds, key);
            #endif
                        Actinic_Lights_Overlay_Queues.Remove (key);
                    }
                }
                if (update_after_deletion_needed)
                {
                    UpdateAllQueues ();
                }

                // Merge all layers together...
                Light_Snapshot = MergeSnapshotsDown (Light_Snapshots);
                // And clear the now out-of-date values for next run
                Light_Snapshots.Clear ();

                if (Light_Snapshot != null && ActiveOutputSystemReady ()) {
            #if DEBUG_PERFORMANCE
                    wasIdle = false;
            #endif
                    // There's a frame to play and the system is ready
                    Actinic_Light_Queue_CurrentIdleWait = 1;
                    // Reset the idle counter, used for implementing interval animations

            #if DEBUG_PERFORMANCE
                    Console.WriteLine ("{0} ms - frame generated", Queue_PerfStopwatch.ElapsedMilliseconds);
            #endif
                    int retriesSinceLastSuccess = 0;
                    while (true) {
                        if (retriesSinceLastSuccess >= 5) {
                            throw new System.IO.IOException ("Could not reconnect to output system in background after 5 tries, giving up");
                        }
                        try {
                            if (UpdateLights_All (Light_Snapshot.LED_Values) == false) {
                                Console.WriteLine ("(Error while updating lights in the animation queue!)");
                            }
                            // It at least didn't throw an exception...
                            retriesSinceLastSuccess = 0;
                            // Exit this loop to allow further processing
                            break;
                        } catch (System.IO.IOException ex) {
                            Console.WriteLine (DateTime.Now);
                            Console.WriteLine ("\n ! Unexpected connection loss, attempting to reconnect...\n\n{0}\n", ex);
                            ShutdownOutputSystem ();
                            retriesSinceLastSuccess ++;
                            Console.Write ("Waiting 5 seconds...");
                            System.Threading.Thread.Sleep (5000);
                            Console.WriteLine ("  Retrying.");

                            // RunWithoutInteraction:  Usually someone won't be providing input when this happens
                            // SkipPreparingSystem:  Queue and all that is already running
                            if (PrepareLightingOutput (true, true) == false)
                            {
                                throw new System.IO.IOException ("Could not reconnect to output system in background, giving up");
                            }
                            // Try again by nature of not calling break
                        }
                    }
            #if DEBUG_PERFORMANCE
                    Console.WriteLine ("{0} ms - frame sent", Queue_PerfStopwatch.ElapsedMilliseconds);
            #endif
            #if DEBUG_BRIEF_PERFORMANCE
                    if (Queue_PerfStopwatch.ElapsedMilliseconds > Light_Animation_Latency)
                        Console.WriteLine ("# {0} ms - frame finished ({1})", Queue_PerfStopwatch.ElapsedMilliseconds, DateTime.Now.ToLongTimeString ());
            #endif
                    // Attempt to keep each frame spaced 'Light_Animation_Delay' time apart, default 50ms
                    SleepForAnimation ((int)Queue_PerfStopwatch.ElapsedMilliseconds);
            #if DEBUG_PERFORMANCE
                    Console.WriteLine ("# {0} ms - frame finished", Queue_PerfStopwatch.ElapsedMilliseconds);
            #endif
            #if DEBUG_VU_PERFORMANCE
                    Console.WriteLine ("# {0} ms - frame finished", VU_Processing_PerfStopwatch.ElapsedMilliseconds);
                    VU_Processing_PerfStopwatch.Stop ();
            #endif
                } else {
            #if DEBUG_PERFORMANCE
                    if (wasIdle == false)
                        Console.WriteLine ("# Idle ({0} ms loop)", Actinic_Light_Queue_CurrentIdleWait);
                    if (Actinic_Light_Queue_CurrentIdleWait == Actinic_Light_Queue_MaxIdleWaitTime)
                        wasIdle = true;
            #endif
                    System.Threading.Thread.Sleep (Actinic_Light_Queue_CurrentIdleWait);
                    foreach (KeyValuePair <string, LED_Queue> queue in GetAllQueues ()) {
                        queue.Value.QueueIdleTime += Actinic_Light_Queue_CurrentIdleWait;
                    }
                    if (Actinic_Light_Queue_CurrentIdleWait < Actinic_Light_Queue_MaxIdleWaitTime) {
                        // Each loop spent in idle without events, increase the delay
                        Actinic_Light_Queue_CurrentIdleWait *= Actinic_Light_Queue_IdleWaitMultiplier;
                    } else if (Actinic_Light_Queue_CurrentIdleWait > Actinic_Light_Queue_MaxIdleWaitTime) {
                        // ...but don't go above the maximum idle wait time
                        Actinic_Light_Queue_CurrentIdleWait = Actinic_Light_Queue_MaxIdleWaitTime;
                    }
                }
                if (Actinic_Lights_Queue.QueueCount >= Actinic_Light_Queue_BufferFullWarning && Actinic_Light_Queue_BufferFullWarning_Shown == false) {
                    Console.WriteLine ("(Warning: the LED output queue holds over {0} frames, which will cause a delay in" +
                        " response after a command)", Actinic_Light_Queue_BufferFullWarning);
                    Actinic_Light_Queue_BufferFullWarning_Shown = true;
                } else if (Actinic_Lights_Queue.QueueCount < Actinic_Light_Queue_BufferFullWarning && Actinic_Light_Queue_BufferFullWarning_Shown == true) {
                    Console.WriteLine ("(LED output queue now holds less than {0} frames)", Actinic_Light_Queue_BufferFullWarning);
                    Actinic_Light_Queue_BufferFullWarning_Shown = false;
                }
                Queue_PerfStopwatch.Stop ();
            }
        }
Example #4
0
 /// <summary>
 /// Adds a frame to the end of the output queue
 /// </summary>
 /// <param name="NextFrame">An LED_Set representing the desired frame.</param>
 public void PushToQueue(LED_Set NextFrame)
 {
     if (NextFrame.LightCount != LightCount)
         throw new ArgumentOutOfRangeException (string.Format ("NextFrame must contain same number of LEDs (has {0}, expected {1})", NextFrame.LightCount, LightCount));
     lock (OutputQueue) {
         OutputQueue.Enqueue (NextFrame.Clone ());
     }
 }