void BlendAnimations(Scene scene,
                         int[] colorsChromaLink, int[] tempColorsChromaLink,
                         int[] colorsHeadset, int[] tempColorsHeadset,
                         int[] colorsKeyboard, int[] tempColorsKeyboard,
                         int[] colorsKeypad, int[] tempColorsKeypad,
                         int[] colorsMouse, int[] tempColorsMouse,
                         int[] colorsMousepad, int[] tempColorsMousepad)
    {
        // blend active animations
        List <Effect> effects = scene._mEffects;

        foreach (Effect effect in effects)
        {
            if (effect._mState)
            {
                DeviceFrameIndex deviceFrameIndex = effect._mFrameIndex;

                //iterate all device types
                for (int d = (int)Device.ChromaLink; d < (int)Device.MAX; ++d)
                {
                    string animationName = effect._mAnimation;

                    switch ((Device)d)
                    {
                    case Device.ChromaLink:
                        animationName += "_ChromaLink.chroma";
                        BlendAnimation1D(effect, deviceFrameIndex, d, Device1D.ChromaLink, animationName, colorsChromaLink, tempColorsChromaLink);
                        break;

                    case Device.Headset:
                        animationName += "_Headset.chroma";
                        BlendAnimation1D(effect, deviceFrameIndex, d, Device1D.Headset, animationName, colorsHeadset, tempColorsHeadset);
                        break;

                    case Device.Keyboard:
                        animationName += "_Keyboard.chroma";
                        BlendAnimation2D(effect, deviceFrameIndex, d, Device2D.Keyboard, animationName, colorsKeyboard, tempColorsKeyboard);
                        break;

                    case Device.Keypad:
                        animationName += "_Keypad.chroma";
                        BlendAnimation2D(effect, deviceFrameIndex, d, Device2D.Keypad, animationName, colorsKeypad, tempColorsKeypad);
                        break;

                    case Device.Mouse:
                        animationName += "_Mouse.chroma";
                        BlendAnimation2D(effect, deviceFrameIndex, d, Device2D.Mouse, animationName, colorsMouse, tempColorsMouse);
                        break;

                    case Device.Mousepad:
                        animationName += "_Mousepad.chroma";
                        BlendAnimation1D(effect, deviceFrameIndex, d, Device1D.Mousepad, animationName, colorsMousepad, tempColorsMousepad);
                        break;
                    }
                }
            }
        }
    }
    void BlendAnimation2D(Effect effect, DeviceFrameIndex deviceFrameIndex, int device, Device2D device2D, string animationName,
                          int[] colors, int[] tempColors)
    {
        int size       = GetColorArraySize2D(device2D);
        int frameId    = deviceFrameIndex._mFrameIndex[device];
        int frameCount = ChromaAnimationAPI.GetFrameCountName(animationName);

        if (frameId < frameCount)
        {
            //cout << animationName << ": " << (1 + frameId) << " of " << frameCount << endl;
            float duration;
            int   animationId = ChromaAnimationAPI.GetAnimation(animationName);
            ChromaAnimationAPI.GetFrame(animationId, frameId, out duration, tempColors, size);
            for (int i = 0; i < size; ++i)
            {
                int color1    = colors[i];     //target
                int tempColor = tempColors[i]; //source

                // BLEND
                int color2;
                if (effect._mBlend == "none")
                {
                    color2 = tempColor; //source
                }
                else if (effect._mBlend == "invert")
                {
                    if (tempColor != 0)                  //source
                    {
                        color2 = InvertColor(tempColor); //source inverted
                    }
                    else
                    {
                        color2 = 0;
                    }
                }
                else if (effect._mBlend == "thresh")
                {
                    color2 = Thresh(effect._mPrimaryColor, effect._mSecondaryColor, tempColor); //source
                }
                else // if (effect._mBlend == "lerp") //default
                {
                    color2 = MultiplyNonZeroTargetColorLerp(effect._mPrimaryColor, effect._mSecondaryColor, tempColor); //source
                }

                // MODE
                if (effect._mMode == "max")
                {
                    colors[i] = MaxColor(color1, color2);
                }
                else if (effect._mMode == "min")
                {
                    colors[i] = MinColor(color1, color2);
                }
                else if (effect._mMode == "average")
                {
                    colors[i] = AverageColor(color1, color2);
                }
                else if (effect._mMode == "multiply")
                {
                    colors[i] = MultiplyColor(color1, color2);
                }
                else if (effect._mMode == "add")
                {
                    colors[i] = AddColor(color1, color2);
                }
                else if (effect._mMode == "subtract")
                {
                    colors[i] = SubtractColor(color1, color2);
                }
                else // if (effect._mMode == "replace") //default
                {
                    if (color2 != 0)
                    {
                        colors[i] = color2;
                    }
                }
            }
            deviceFrameIndex._mFrameIndex[device] = (frameId + frameCount + effect._mSpeed) % frameCount;
        }
    }