Index: skin/Project Mayhem III/PAL/Home.xml =================================================================== --- skin/Project Mayhem III/PAL/Home.xml (revision 30630) +++ skin/Project Mayhem III/PAL/Home.xml (working copy) @@ -680,6 +680,7 @@ 96 9 5 + XBMC.System.PWMControl(#FF0000) home-focus.gif - 30 @@ -705,6 +706,7 @@ 96 2 3 + XBMC.System.PWMControl(#00FF00) home-focus.gif - 30 @@ -730,6 +732,7 @@ 96 5 4 + XBMC.System.PWMControl(#FFFFFF,#000000,#000000,#000000,firework,500) home-focus.gif - 30 @@ -755,6 +758,7 @@ 96 3 7 + XBMC.System.PWMControl(#FF00FF) home-focus.gif - 30 @@ -780,6 +784,7 @@ 96 4 9 + XBMC.System.PWMControl(#FFFF00) home-focus.gif - 30 @@ -820,6 +825,7 @@ 6 10 5 + XBMC.System.PWMControl(#00FFFF) home-focus.gif - 20 @@ -838,6 +844,7 @@ 96 10 5 + XBMC.System.PWMControl(#FFFFFF) home-focus.gif - 20 Index: userdata/AdvancedSettings.xml =================================================================== --- userdata/AdvancedSettings.xml (revision 0) +++ userdata/AdvancedSettings.xml (revision 0) @@ -0,0 +1,13 @@ + + + true + 30 + 1 + 1.2 + 1.0 + 0.8 + 2 + 50 + 20 + + Index: xbmc/AdvancedSettings.cpp =================================================================== --- xbmc/AdvancedSettings.cpp (revision 30630) +++ xbmc/AdvancedSettings.cpp (working copy) @@ -216,6 +216,22 @@ m_bPythonVerbose = false; m_bgInfoLoaderMaxThreads = 1; + + m_ambiLight = false; + m_ambiLightSpaceBetweenPixels = 30; + m_ambiLightFloatingAverageFrames = 1; + m_ambiLightMinRGB = 2; + m_ambiLightMaxRGB = 255; + m_ambiLightMode = "linear"; + m_ambiLightPosition = "all"; + m_ambiLightGammaR = 1.2f; + m_ambiLightGammaG = 1.0f; + m_ambiLightGammaB = 0.8f; + m_ambiLightIntensityR = 1.0f; + m_ambiLightIntensityG = 1.0f; + m_ambiLightIntensityB = 1.0f; + m_ambiLightFilterThreshold = 50; + m_ambiLightDarknessLimit = 20; } bool CAdvancedSettings::Load() @@ -431,6 +447,26 @@ XMLUtils::GetBoolean(pElement, "verbose", m_bPythonVerbose); } + pElement = pRootElement->FirstChildElement("ambilight"); + if (pElement) + { + XMLUtils::GetBoolean(pElement, "enabled", m_ambiLight); + XMLUtils::GetInt(pElement, "spaceBetweenPixels", m_ambiLightSpaceBetweenPixels); + XMLUtils::GetInt(pElement, "floatingAverageFrames", m_ambiLightFloatingAverageFrames); + XMLUtils::GetInt(pElement, "minRGB", m_ambiLightMinRGB); + XMLUtils::GetInt(pElement, "maxRGB", m_ambiLightMaxRGB); + XMLUtils::GetString(pElement, "mode", m_ambiLightMode); + XMLUtils::GetString(pElement, "position", m_ambiLightPosition); + XMLUtils::GetFloat(pElement, "gammaR", m_ambiLightGammaR); + XMLUtils::GetFloat(pElement, "gammaG", m_ambiLightGammaG); + XMLUtils::GetFloat(pElement, "gammaB", m_ambiLightGammaB); + XMLUtils::GetFloat(pElement, "intensityR", m_ambiLightIntensityR); + XMLUtils::GetFloat(pElement, "intensityG", m_ambiLightIntensityG); + XMLUtils::GetFloat(pElement, "intensityB", m_ambiLightIntensityB); + XMLUtils::GetInt(pElement, "filterThreshold", m_ambiLightFilterThreshold); + XMLUtils::GetInt(pElement, "darknessLimit", m_ambiLightDarknessLimit); + } + XMLUtils::GetString(pRootElement, "cddbaddress", m_cddbAddress); XMLUtils::GetBoolean(pRootElement, "usepcdvdrom", m_usePCDVDROM); Index: xbmc/AdvancedSettings.h =================================================================== --- xbmc/AdvancedSettings.h (revision 30630) +++ xbmc/AdvancedSettings.h (working copy) @@ -217,6 +217,21 @@ bool m_bPythonVerbose; int m_bgInfoLoaderMaxThreads; + bool m_ambiLight; + int m_ambiLightSpaceBetweenPixels; + int m_ambiLightFloatingAverageFrames; + int m_ambiLightMinRGB; + int m_ambiLightMaxRGB; + CStdString m_ambiLightMode; + CStdString m_ambiLightPosition; + float m_ambiLightGammaR; + float m_ambiLightGammaG; + float m_ambiLightGammaB; + float m_ambiLightIntensityR; + float m_ambiLightIntensityG; + float m_ambiLightIntensityB; + int m_ambiLightFilterThreshold; + int m_ambiLightDarknessLimit; }; extern CAdvancedSettings g_advancedSettings; Index: xbmc/Application.cpp =================================================================== --- xbmc/Application.cpp (revision 30630) +++ xbmc/Application.cpp (working copy) @@ -2794,6 +2794,9 @@ } UpdateLCD(); +#ifdef AMBILIGHT_SUPPORT + g_iledSmartxxrgb.AmbiLightUpdateFrameNumber(); +#endif // read raw input from controller, remote control, mouse and keyboard ReadInput(); Index: xbmc/cores/dvdplayer/DVDPlayerVideo.cpp =================================================================== --- xbmc/cores/dvdplayer/DVDPlayerVideo.cpp (revision 30630) +++ xbmc/cores/dvdplayer/DVDPlayerVideo.cpp (working copy) @@ -38,6 +38,7 @@ #include "DVDCodecs/Overlay/DVDOverlaySSA.h" #include #include +#include "utils/LED.h" using namespace std; @@ -440,6 +441,23 @@ memset(&picture, 0, sizeof(DVDVideoPicture)); if (m_pVideoCodec->GetPicture(&picture)) { +#ifdef AMBILIGHT_SUPPORT + // AMBI-LIGHT STUFF + if(g_advancedSettings.m_ambiLight) + { + g_iledSmartxxrgb.AmbiLightUpdate(); + g_iledSmartxxrgb.AmbiLightParamsUpdate(0,picture.iWidth,2); + + for(unsigned int i=0;iiGroupId; Index: xbmc/cores/VideoRenderers/XBoxRenderer.cpp =================================================================== --- xbmc/cores/VideoRenderers/XBoxRenderer.cpp (revision 30630) +++ xbmc/cores/VideoRenderers/XBoxRenderer.cpp (working copy) @@ -24,6 +24,7 @@ #include "Application.h" #include "XBVideoConfig.h" #include "Settings.h" +#include "utils/LED.h" // http://www.martinreddy.net/gfx/faqs/colorconv.faq @@ -759,6 +760,10 @@ m_yuvcoef = yuv_coef_bt601; break; } +#ifdef AMBILIGHT_SUPPORT + // init Params for AmbiLight + g_iledSmartxxrgb.AmbiLightParamsInit(width,height,m_yuvcoef,m_yuvrange); +#endif // calculate the input frame aspect ratio CalculateFrameAspectRatio(d_width, d_height); ChooseBestResolution(m_fps); @@ -918,6 +923,66 @@ unsigned int CXBoxRenderer::DrawSlice(unsigned char *src[], int stride[], int w, int h, int x, int y) { +#ifdef AMBILIGHT_SUPPORT + BYTE *s0,*s1,*s2; + BYTE *d0,*d1,*d2; + + int w2,h2,x2,y2; + + // check if we've finished the recent Frame and push it to the SmartXX... + if(g_advancedSettings.m_ambiLight) + g_iledSmartxxrgb.AmbiLightUpdate(); + + int index = NextYV12Texture(); + if( index < 0 ) + return -1; + + if( WaitForSingleObject(m_eventTexturesDone[index], 500) == WAIT_TIMEOUT ) + CLog::Log(LOGWARNING, CStdString(__FUNCTION__) + " - Timeout waiting for texture %d", index); + + YV12Image &im = m_image[index]; + // copy Y + d0 = (BYTE*)im.plane[0] + im.stride[0] * y + x; + s0 = src[0]; + + w2 = w >> im.cshift_x; h2 = h >> im.cshift_y; + x2 = x >> im.cshift_x; y2 = y >> im.cshift_y; + + // copy U + d1 = (BYTE*)im.plane[1] + im.stride[1] * y2 + x2; + s1 = src[1]; + + // copy V + d2 = (BYTE*)im.plane[2] + im.stride[2] * y2 + x2; + s2 = src[2]; + + int widthRatio = (int)(w/w2); + int heightRatio = (int)(h/h2); + + //Update AmbiLights params... + if(g_advancedSettings.m_ambiLight) + g_iledSmartxxrgb.AmbiLightParamsUpdate(x,w,widthRatio); + + for (int i = 0;i < h;i++) + { + memcpy(d0, s0, w); + s0 += stride[0]; + d0 += im.stride[0]; + + if(i % heightRatio == 0) + { + memcpy(d1, s1, w2); + s1 += stride[1]; + d1 += im.stride[1]; + + memcpy(d2, s2, w2); + s2 += stride[2]; + d2 += im.stride[2]; + } + // calculate RGB-values for the recent row of the frame... + if(g_advancedSettings.m_ambiLight) + g_iledSmartxxrgb.AmbiLightRGBCalculate(s0,s1,s2); +#else BYTE *s; BYTE *d; int i, p; @@ -964,6 +1029,7 @@ memcpy(d, s, w); s += stride[p]; d += im.stride[p]; +#endif } SetEvent(m_eventTexturesDone[index]); @@ -1045,6 +1111,9 @@ m_hLowMemShader = 0; } +#ifdef AMBILIGHT_SUPPORT + g_iledSmartxxrgb.AmbiLightStop(); +#endif m_bConfigured = false; } Index: xbmc/Util.cpp =================================================================== --- xbmc/Util.cpp (revision 30630) +++ xbmc/Util.cpp (working copy) @@ -4397,14 +4397,30 @@ strWhiteB= arSplit[3].c_str(); strTran = arSplit[4].c_str(); iTrTime = atoi(arSplit[5].c_str()); +#ifdef AMBILIGHT_SUPPORT + CUtil::PWMControl(strRgbA,strRgbB,strWhiteA,strWhiteB,strTran, iTrTime); +#endif AMBILIGHT_SUPPORT } else if(parameter.size() > 6) { strRgbA = strRgbB = parameter; strWhiteA = strWhiteB = "#000000"; +#ifdef AMBILIGHT_SUPPORT + strTran = "fade2"; + tRGBColor rgb; + sscanf(parameter,"#%2X%2X%2X",&rgb.r,&rgb.g,&rgb.b); + + //CUtil::PWMControl(strRgbA,strRgbB,strWhiteA,strWhiteB,strTran, iTrTime); + g_iledSmartxxrgb.SetRGBState(rgb); + } + else + CLog::Log(LOGDEBUG,"System.PWMControl(): Invalid parameters - use: System.PWMControl(#rgb1,#rgb2,#white1,#white2,mode,time)"); +#else strTran = "none"; } CUtil::PWMControl(strRgbA,strRgbB,strWhiteA,strWhiteB,strTran, iTrTime); +#endif + } else if (execute.Equals("backupsysteminfo")) { Index: xbmc/utils/LED.cpp =================================================================== --- xbmc/utils/LED.cpp (revision 30630) +++ xbmc/utils/LED.cpp (working copy) @@ -50,6 +50,9 @@ #include "xbox/XKUtils.h" #include "LCD.h" #include "GUISettings.h" +#ifdef AMBILIGHT_SUPPORT +#include "AdvancedSettings.h" +#endif #include @@ -99,6 +102,11 @@ dwLastTime = 0; bRepeat = false; +#ifdef AMBILIGHT_SUPPORT + isPaused = false; + + AmbiLightParams.AmbiLightIsPlaying=false; +#endif } ILEDSmartxxRGB::~ILEDSmartxxRGB() @@ -108,7 +116,11 @@ { if (g_sysinfo.SmartXXModCHIP().Equals("SmartXX V3") || g_sysinfo.SmartXXModCHIP().Equals("SmartXX OPX")) { +#ifndef AMBILIGHT_SUPPORT SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_LOWEST); +#else + SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_HIGHEST); +#endif CLog::Log(LOGDEBUG,"Starting SmartXX RGB LED thread"); SetRGBStatus("general"); } @@ -117,6 +129,140 @@ { while(!m_bStop) { +#ifdef AMBILIGHT_SUPPORT + if(!isPaused) + { + dwFrameTime = timeGetTime() - dwLastTime; + + if( (s_RGBs.strTransition.IsEmpty() || s_RGBs.strTransition.Equals("none")) && !strLastTransition.Equals("none") ) + { + s_CurRGB.red = s_RGBs.red1; + s_CurRGB.green = s_RGBs.green1; + s_CurRGB.blue = s_RGBs.blue1; + s_CurRGB.white = s_RGBs.white1; + + SetRGBLed(s_CurRGB.red,s_CurRGB.green,s_CurRGB.blue, s_CurRGB.white); + } + else if(s_RGBs.strTransition.Equals("switch") && !strLastTransition.Equals("switch")) + { + if(dwFrameTime >= s_RGBs.iTime ) + { + s_CurRGB.red = s_RGBs.red2; + s_CurRGB.green = s_RGBs.green2; + s_CurRGB.blue = s_RGBs.blue2; + s_CurRGB.white = s_RGBs.white2; + SetRGBLed(s_CurRGB.red,s_CurRGB.green,s_CurRGB.blue,s_CurRGB.white); + } + else + { + s_CurRGB.red = s_RGBs.red1; + s_CurRGB.green = s_RGBs.green1; + s_CurRGB.blue = s_RGBs.blue1; + s_CurRGB.white = s_RGBs.white1; + SetRGBLed(s_CurRGB.red,s_CurRGB.green,s_CurRGB.blue,s_CurRGB.white); + } + + } + else if(s_RGBs.strTransition.Equals("blink")) + { + strLastTransition = "blink"; + if(dwFrameTime >= s_RGBs.iTime ) + { + s_CurRGB.red = (s_CurRGB.red != s_RGBs.red1) ? s_RGBs.red1 : s_RGBs.red2; + s_CurRGB.green = (s_CurRGB.green != s_RGBs.green1) ? s_RGBs.green1 : s_RGBs.green2; + s_CurRGB.blue = (s_CurRGB.blue != s_RGBs.blue1) ? s_RGBs.blue1 : s_RGBs.blue2; + s_CurRGB.white= (s_CurRGB.white != s_RGBs.white1) ? s_RGBs.white1 : s_RGBs.white2; + dwLastTime = timeGetTime(); + SetRGBLed(s_CurRGB.red,s_CurRGB.green,s_CurRGB.blue,s_CurRGB.white); + } + } + else if(s_RGBs.strTransition.Equals("fade") || s_RGBs.strTransition.Equals("fadeloop") || s_RGBs.strTransition.Equals("faderepeat")) + { + static double distanceR,distanceG,distanceB,distanceW; + + if(!strLastTransition.Equals("fade")) + { + distanceR = bRepeat ? s_RGBs.red1-s_RGBs.red2 : s_RGBs.red2-s_RGBs.red1; + distanceG = bRepeat ? s_RGBs.green1-s_RGBs.green2 : s_RGBs.green2-s_RGBs.green1; + distanceB = bRepeat ? s_RGBs.blue1-s_RGBs.blue2 : s_RGBs.blue2-s_RGBs.blue1; + distanceW = bRepeat ? s_RGBs.white1-s_RGBs.white2 : s_RGBs.white2-s_RGBs.white1; + + strLastTransition = "fade"; + + if(s_RGBs.strTransition.Equals("faderepeat"))bRepeat=!bRepeat; + } + + if(dwFrameTime <= s_RGBs.iTime ) + { + double stepR=distanceR/s_RGBs.iTime*dwFrameTime; + double stepG=distanceG/s_RGBs.iTime*dwFrameTime; + double stepB=distanceB/s_RGBs.iTime*dwFrameTime; + double stepW=distanceW/s_RGBs.iTime*dwFrameTime; + + s_CurRGB.red=(bRepeat ? s_RGBs.red1 : s_RGBs.red2) +(int)stepR; + s_CurRGB.green=(bRepeat ? s_RGBs.green1 : s_RGBs.green2)+(int)stepG; + s_CurRGB.blue=(bRepeat ? s_RGBs.blue1 : s_RGBs.blue2)+(int)stepB; + s_CurRGB.white=(bRepeat ? s_RGBs.white1 : s_RGBs.white2)+(int)stepW; + + SetRGBLed(s_CurRGB.red,s_CurRGB.green,s_CurRGB.blue,s_CurRGB.white); + } + else if(s_RGBs.strTransition.Equals("fadeloop") || s_RGBs.strTransition.Equals("faderepeat")) + { + strLastTransition="none"; + dwLastTime = timeGetTime(); + } + } + else if(s_RGBs.strTransition.Equals("fade2")) + { + static int distanceR,distanceG,distanceB,distanceW; + static int maxDiffR,maxDiffG,maxDiffB; + + if(!strLastTransition.Equals("fade2")) + { + strLastTransition = "fade2"; + dwFrameTime = 0; + dwLastTime = timeGetTime(); + + s_RGBs.red1 =s_CurRGB.red; + s_RGBs.green1 =s_CurRGB.green; + s_RGBs.blue1 =s_CurRGB.blue ; + + distanceR = s_RGBs.red2-s_RGBs.red1; + distanceG = s_RGBs.green2-s_RGBs.green1; + distanceB = s_RGBs.blue2-s_RGBs.blue1; + distanceW = s_RGBs.white2-s_RGBs.white1; + + int max=abs(MAX(distanceR,distanceG,distanceB)); + + //max one second transition time + s_RGBs.iTime = int((max/255.0f)*FADE2_MAX_TIME); + } + + if(dwFrameTime <= s_RGBs.iTime && (s_CurRGB.red!=s_RGBs.red2 || s_CurRGB.blue!=s_RGBs.blue2 || s_CurRGB.green!=s_RGBs.green2)) + { + // how far do we need to be according to current time... + int stepR=(int)((double)distanceR/s_RGBs.iTime*dwFrameTime); + int stepG=(int)((double)distanceG/s_RGBs.iTime*dwFrameTime); + int stepB=(int)((double)distanceB/s_RGBs.iTime*dwFrameTime); + int stepW=(int)((double)distanceW/s_RGBs.iTime*dwFrameTime); + + s_CurRGB.red=s_RGBs.red1+stepR; + s_CurRGB.green=s_RGBs.green1+stepG; + s_CurRGB.blue=s_RGBs.blue1+stepB; + s_CurRGB.white=s_RGBs.white1+stepW; + + SetRGBLed(s_CurRGB.red,s_CurRGB.green,s_CurRGB.blue,s_CurRGB.white); + } + else + { + strLastTransition = "reset"; + } + } + } + for(int i=0;i<10;i++) + Sleep(1); + } +#else dwFrameTime = timeGetTime() - dwLastTime; if( (s_RGBs.strTransition.IsEmpty() || s_RGBs.strTransition.Equals("none")) && !strLastTransition.Equals("none") ) @@ -203,10 +349,28 @@ Sleep(10); } +#endif } void ILEDSmartxxRGB::OnExit() { +#ifdef AMBILIGHT_SUPPORT + // this can also be called when navigationg in the XBMC while a movie runs in background. + // when in menu it can happen that parallel to AmbiLight PWMControl will be started. In that + // case we want AmbiLight to stay on and so the ambilight will stop the rgb-thread. To avoid + // flashing of LEDs in this case we don't turn them off. + if(!AmbiLightParams.AmbiLightIsPlaying) + { + SetRGBLed(0,0,0,0xb); //r=0,g=0,b=0 w=0xb (Status LED ON) + + // SmartXX OPX port for RGB-Red is the same port for display brightness control + // Restoring brightness value from the settings + if ( g_sysinfo.SmartXXModCHIP().Equals("SmartXX OPX") ) + g_lcd->SetBackLight(g_guiSettings.GetInt("lcd.backlight")); + + CLog::Log(LOGDEBUG,"Stopping SmartXX RGB LED thread"); + } +#else SetRGBLed(0,0,0,0xb); //r=0,g=0,b=0 w=0xb (Status LED ON) // SmartXX OPX port for RGB-Red is the same port for display brightness control @@ -215,11 +379,12 @@ g_lcd->SetBackLight(g_guiSettings.GetInt("lcd.backlight")); CLog::Log(LOGDEBUG,"Stopping SmartXX RGB LED thread"); +#endif } bool ILEDSmartxxRGB::Start() { - if (g_sysinfo.SmartXXModCHIP().Equals("SmartXX V3") || g_sysinfo.SmartXXModCHIP().Equals("SmartXX OPX")) + if (g_sysinfo.SmartXXModCHIP().Equals("SmartXX V3") || g_sysinfo.SmartXXModCHIP().Equals("SmartXX OPX")) // && !AmbiLightParams.AmbiLightIsPlaying && !isPaused) { Create(); return true; @@ -236,6 +401,23 @@ return (m_ThreadHandle != NULL); } +#ifdef AMBILIGHT_SUPPORT +void ILEDSmartxxRGB::Pause() +{ + //isPaused=true; +} +void ILEDSmartxxRGB::Continue() +{ + if(isPaused) + dwLastTime=timeGetTime(); + isPaused=false; +} + +bool ILEDSmartxxRGB::IsPaused() +{ + return isPaused; +} +#endif void ILEDSmartxxRGB::getRGBValues(const CStdString &strRGBa, const CStdString &strRGBb, const CStdString &strWhiteA, const CStdString &strWhiteB, RGBVALUES* s_rgb) { DWORD red=0,green=0,blue=0,white=0; @@ -298,6 +480,22 @@ bool ILEDSmartxxRGB::SetRGBLed(int red, int green, int blue, int white) { +#ifdef AMBILIGHT_SUPPORT + static oldR=0,oldG=0,oldB=0; + /*if(abs(oldR-red)>1) + CLog::DebugLog("RSkipAmbilight"); + if(abs(oldG-green)>1) + CLog::DebugLog("GSkipAmbilight"); + if(abs(oldB-blue)>1) + CLog::DebugLog("BSkipAmbilight");*/ + oldR=red; + oldG=green; + oldB=blue; + + red=CLAMP(red/2,0,127); + green=CLAMP(green/2,0,127); + blue=CLAMP(blue/2,0,127); +#endif _outp( g_sysinfo.SmartXXModCHIP().Equals("SmartXX V3") ? SMARTXX_PWD_RED:SMARTXX_OPX_PWD_RED, red); _outp( g_sysinfo.SmartXXModCHIP().Equals("SmartXX V3") ? SMARTXX_PWD_GREEN:SMARTXX_OPX_PWD_GREEN, green); _outp( g_sysinfo.SmartXXModCHIP().Equals("SmartXX V3") ? SMARTXX_PWD_BLUE:SMARTXX_OPX_PWD_BLUE, blue); @@ -344,3 +542,577 @@ return SetRGBStatus(strTransition); } + +#ifdef AMBILIGHT_SUPPORT +bool ILEDSmartxxRGB::SetRGBState(tRGBColor rgb) +{ + // we have a new request: start reset + strCurrentStatus = "NULL"; + strLastStatus = "NULL"; + strLastTransition = "NULL"; + s_RGBs.strTransition = "NULL"; + // is used to identify first frame in blink-mode, 0 is not usable to do this check as zero is + // a valid value for a color + s_RGBs.strTransition = "fade2"; + s_RGBs.red2 =rgb.r; + s_RGBs.green2 =rgb.g; + s_RGBs.blue2 =rgb.b; + + /*s_RGBs.red1 =s_CurRGB.red; + s_RGBs.green1 =s_CurRGB.green; + s_RGBs.blue1 =s_CurRGB.blue ;*/ + + //dwFrameTime = 0; + //dwLastTime = timeGetTime(); + bRepeat = false; + // end reset + + + + SetRGBStatus(s_RGBs.strTransition); + + if(!IsRunning()) + Start(); + return true; +} + + +void ILEDSmartxxRGB::AmbiLightUpdateFrameNumber() +{ + // only do this, if ambilight is enabled... + if(g_advancedSettings.m_ambiLight && AmbiLightParams.AmbiLightIsPlaying) + { + AmbiLightParams.recentFrame=(AmbiLightParams.recentFrame+1) % g_advancedSettings.m_ambiLightFloatingAverageFrames; + AmbiLightParams.frameHasChanged=true; + } +} + +void ILEDSmartxxRGB::AmbiLightSetAverageColor(unsigned int H,unsigned int S,unsigned int V) +{ + // this function is called after we've gotten the average color of the recent frame. It then shifts + // the array containing the values of our FloatingAverageArray (if enabled) to push out the oldest value and + // insert these new values at the beginning (FIFO). + + // first copy AmbiLightParams to local var for better handling... + AMBILIGHT ¶ms=AmbiLightParams; + // also copy our FloatingAverageFrames-var to local var... + unsigned int FloatingAverageFrames=g_advancedSettings.m_ambiLightFloatingAverageFrames; + + // when mode is linear, then the oldest frame in our floating average counts once, the frame after that counts twice, + // the frame after that thrice and so on... + /*if(g_advancedSettings.m_ambiLightMode=="linear") + { + // in this case the sums have to be recalculated from zero... + params.sumR=0; + params.sumG=0; + params.sumB=0; + // recalculate RGB arrays, starting with the now oldest frame... + // if we have a framenumber of 5 at the moment and FloatingAverageFrames of 10 + // then the first index is 5 which is the 6th field in the array, which takes now + // the oldest value... + for(unsigned int i=1;ioldSumH) + { + oldSumH=sumH; + hsv.h=i+1; + } + } + //now find the max inside our window... + int maxH=hsv.h; + for(int i=hsv.h+1;ihsvHisto.h[maxH]?i:maxH; + hsv.h=maxH; + + //window limits for saturation histogram... + int iMin=CLAMP(hsv.h-WINDOW_SIZE,0,360); + int iMax=CLAMP(hsv.h+WINDOW_SIZE,0,360); + + //now we count all pixels around the + int relevantPixelCount=0; + for(int i=iMin;i<=iMax;i++) + relevantPixelCount+=hsvHisto.h[i]; + + // nur wenn die Anzahl der für das Maximum relevanten Pixel eine Fläche einnimmt die größer als die Vorgegebene + // Minimalgröße ist wird die Saturation und der Value berechnet (ohne die bleibt die Farbe schwarz)... + if(relevantPixelCount*(g_advancedSettings.m_ambiLightSpaceBetweenPixels+1)>=params.SourceWidth*params.SourceHeight*MIN_PERCENTAGE) + { + //Saturation Histogramm + for(int i=0;i<(int)params.PixelCount;i++) + { + if(params.hsvMatrix[i].h>=iMin && params.hsvMatrix[i].h<=iMax) + hsvHisto.s[params.hsvMatrix[i].s]++; + } + + //Max Saturation + for(int i=0;i<=100;i++) + hsv.s=hsvHisto.s[i]>hsvHisto.s[hsv.s] ? i : hsv.s; + + //Value Average + int sumV=0; + for(int i=0;i<(int)params.PixelCount;i++) + sumV+=params.hsvMatrix[i].v; + + if(params.PixelCount>0) + hsv.v=sumV/params.PixelCount; + } + + rgb=HSV2RGB(hsv); + + // now we take the power of gamma for each RGB-value and use our scale-factor to form an valid RGB-value... + // this gamma factor is an simple way to control how fast a LED turns bright especially in lower values. if in dark + // passages you always have blue light try an gamme value below 1 so blue will have a lower gamma curve. On the other + // hand you could pass Red and green a gamma above 1, common value would be e.g. gammaR=2.2, gammeG=2.0, gammaB=0.8 + // (as blue often seems to be the brightest LED). + rgb.r=(int)(pow((float)rgb.r,params.gammaR)*params.gammaScaleR); + rgb.g=(int)(pow((float)rgb.g,params.gammaG)*params.gammaScaleG); + rgb.b=(int)(pow((float)rgb.b,params.gammaB)*params.gammaScaleB); + + rgb.r=CLAMP(rgb.r,g_advancedSettings.m_ambiLightMinRGB,g_advancedSettings.m_ambiLightMaxRGB); + rgb.g=CLAMP(rgb.g,g_advancedSettings.m_ambiLightMinRGB,g_advancedSettings.m_ambiLightMaxRGB); + rgb.b=CLAMP(rgb.b,g_advancedSettings.m_ambiLightMinRGB,g_advancedSettings.m_ambiLightMaxRGB); + + + // our values get passed to the SmartXX... + SetRGBState(rgb); + //SetRGBLed(rgb.r,rgb.g,rgb.b,0); +} + +void ILEDSmartxxRGB::AmbiLightParamsInit(unsigned int SourceWidth,unsigned int SourceHeight,YUVCOEF yuvcoef,YUVRANGE yuvrange) +{ + AMBILIGHT ¶ms=AmbiLightParams; + + if(g_advancedSettings.m_ambiLight==true) + { + CLog::DebugLog ("Params x %i w %i",params.x,params.w); + + CLog::Log(LOGDEBUG,"AmbiLight is initializing..."); + + s_CurRGB.red=2; + s_CurRGB.green=2; + s_CurRGB.blue=2; + params.AmbiLightIsPlaying=true; + // here we initialize our Params, it's always called, when a new movie starts... + params.frameHasChanged = false; + params.recentFrame = 0; + + params.SourceWidth=SourceWidth; + params.SourceHeight=SourceHeight; + + params.yuvcoef=yuvcoef; + params.yuvrange=yuvrange; + + params.PixelCount=0; + params.RowCount=0; + params.PixelOverflow=0; + + params.R=0; + params.G=0; + params.B=0; + + params.hsvMatrix=new tHSVColor[92161]; + //memset(¶ms.hsvMatrix,0,sizeof(params.hsvMatrix)*92161); + + if(params.arrH==NULL) + { + params.hsvh=new tHSVHistogramm[g_advancedSettings.m_ambiLightFloatingAverageFrames]; + params.arrH=new unsigned int[g_advancedSettings.m_ambiLightFloatingAverageFrames]; + params.arrS=new unsigned int[g_advancedSettings.m_ambiLightFloatingAverageFrames]; + params.arrV=new unsigned int[g_advancedSettings.m_ambiLightFloatingAverageFrames]; + } + + //initialize Array... + for(int i=0;i0) + { + //static unsigned int oldR=0,oldG=0,oldB=0; + + // then we calculate the average of the last frame... + + //unsigned int R=(unsigned int)(params.R/params.PixelCount); + //unsigned int G=(unsigned int)(params.G/params.PixelCount); + //unsigned int B=(unsigned int)(params.B/params.PixelCount); + + // check, whether we have a skip (e.g. a new scene or st.) + + //AmbiLightCheckSkip(oldR,oldG,oldB,R,G,B); + //oldR=R;oldG=G;oldB=B; + + // pass it to SetAverageColor... + + //AmbiLightSetAverageColor(R,G,B); + + // and push it onto the SmartXX... + AmbiLightRGBSet(); + } + + // then the RGB-values get zeroed for a fresh start in this new frame... + + params.R=0; + params.G=0; + params.B=0; + + //memset(¶ms.hsvMatrix,0,sizeof(params.hsvMatrix)*92161); + // the counter is reset... + + params.PixelCount=0; + + // a new startposition for Pixeloverflow is calculated... + + //params.PixelOverflow=g_advancedSettings.m_ambiLightSpaceBetweenPixels*params.recentFrame; + + // and FrameChange Property... + params.frameHasChanged = false; + // and RowCount is reset... + params.RowCount=0; + } +} +void ILEDSmartxxRGB::AmbiLightCheckSkip(unsigned int oldR,unsigned int oldG,unsigned int oldB,unsigned int newR,unsigned int newG,unsigned int newB) +{ + /*AMBILIGHT ¶ms=AmbiLightParams; + int skipR=oldR-newR; + int skipG=oldG-newG; + int skipB=oldB-newB; + + if(skipR<0) skipR*=-1; + if(skipG<0) skipG*=-1; + if(skipB<0) skipB*=-1; + + if((skipR+skipG+skipB)>g_advancedSettings.m_ambiLightFilterThreshold*3) + { + for(int i=0;i=params.SourceHeight/2) || position=="left" || position=="right" || position=="all") + { + // these vars take the x-value from where to start getting pixels and the width. e.g. if you only want to interpret + // the left area of the screen, then StartX equals zero and EndX equals StartX+resolutionwidth/2 + // these are always recalulated as x and w might change during different YUV-chunks... + unsigned int StartX; + unsigned int EndX; + // here we set StartX and EndW for different modes... + if(position=="left") + { + StartX=params.x; + EndX=StartX+params.w/2; + } + else if(position=="right") + { + StartX=params.x+params.SourceWidth/2; + EndX=StartX+params.w/2; + } + else + { + StartX=params.x; + EndX=StartX+params.w; + } + // now we go on the first pixel in a line is always Startx+PixelOverflow. PixelOverflow is calculated like this: + // when you have a resolution width of 1280px and SpaceBetweenPixels is 499 (means each 500th pixel in a row is + // interpreted) then the first Pixel taken is StartX+PixelCount which euals normally 0, then we add SpaceBetweenPixels+1 + // so our next Pixel in this row is number 500 (between 0 and 500 there's 499px). the next one is, as you guessed + // 1000 and the next one should be 1500 but, as our resolution width is only 1280, here's the overflow + // (also you have to take notice of FloatingAverageFrames which decreases SpaceBetweenPixels, so if we have + // FloatingAverageFrames of 10 then in each Frame we would interpret only each 500*10=5000th pixel) + for(unsigned int i=10;i=g_advancedSettings.m_ambiLightDarknessLimit) + { + params.hsvMatrix[params.PixelCount]=hsv; + // this counter is necessary, so we know, how many pixels we've counted in this frame... + params.PixelCount++; + } + } + // now our OverFlow-Parameter gets set as described above... + params.PixelOverflow=params.PixelOverflow % g_advancedSettings.m_ambiLightSpaceBetweenPixels*g_advancedSettings.m_ambiLightFloatingAverageFrames; + } + // and the RowCounter gets increased, it's necessary for us to know where we are when mode is top or bottom... + params.RowCount++; +} + +tRGBColor ILEDSmartxxRGB::AmbiLightYUV2RGB(BYTE y, BYTE u, BYTE v) +{ + YUVCOEF &coef = AmbiLightParams.yuvcoef; + YUVRANGE &range = AmbiLightParams.yuvrange; + + // normalize + float Yp = (y - range.y_min) * 255.0f / (range.y_max - range.y_min); + float Up = (u - range.u_min) * 255.0f / (range.u_max - range.u_min) - 127.5f; + float Vp = (v - range.v_min) * 255.0f / (range.v_max - range.v_min) - 127.5f; + + // recalculate + tRGBColor rgb; + rgb.r = (int)(Yp + coef.r_up * Up + coef.r_vp * Vp); + rgb.g = (int)(Yp + coef.g_up * Up + coef.g_vp * Vp); + rgb.b = (int)(Yp + coef.b_up * Up + coef.b_vp * Vp); + + // clamp + rgb.r = CLAMP(rgb.r, 0, 255); + rgb.g = CLAMP(rgb.g, 0, 255); + rgb.b = CLAMP(rgb.b, 0, 255); + + return rgb; +} + +tHSVColor ILEDSmartxxRGB::RGB2HSV(tRGBColor rgb) +{ + float min,max,delta; + float h,s,v; + float r,g,b; + + //skalieren der RGB-Werte auf das Intervall [0;1] + r=(float)rgb.r/255.0f; + g=(float)rgb.g/255.0f; + b=(float)rgb.b/255.0f; + + min=MIN(r,g,b); + max=MAX(r,g,b); + + v=max; // v + + delta=max-min; + + if(max!=0) + { + s=delta/max; // s + + if(r==g && g==b) + h=0; + else if(r==max) // h + h=(g-b)/delta; + else if(g==max) + h=2+(b-r)/delta; + else + h=4+(r-g)/delta; + + h*=60.0f; + if(h<0.0f) + h+=360.0f; + } + else + { + // r = g = b = 0 // s = 0, v is undefined + s=0.0f; + h=0.0f; + } + + //skalieren: h=[0;360]; s,v=[0;100] + tHSVColor hsv; + hsv.h=(int)h; + hsv.s=(int)(s*100); + hsv.v=(int)(v*100); + + return hsv; +} + +tRGBColor ILEDSmartxxRGB::HSV2RGB(tHSVColor hsv) +{ + int i; + float f,p,q,t; + float h,s,v; + float r,g,b; + + h=(float)hsv.h; + s=(float)hsv.s/100.0f; + v=(float)hsv.v/100.0f; + + if(s==0) + { + r=g=b=v; + } + else + { + h/=60; + i=(int)h; + f=h-i; + p=v*(1-s); + q=v*(1-s*f); + t=v*(1-s*(1-f)); + + switch(i) + { + case 0: + case 6: + r=v; + g=t; + b=p; + break; + case 1: + r=q; + g=v; + b=p; + break; + case 2: + r=p; + g=v; + b=t; + break; + case 3: + r=p; + g=q; + b=v; + break; + case 4: + r=t; + g=p; + b=v; + break; + default: + r=v; + g=p; + b=q; + break; + } + } + + tRGBColor rgb; + rgb.r=(int)(r*255); + rgb.g=(int)(g*255); + rgb.b=(int)(b*255); + return rgb; +} +#endif \ No newline at end of file Index: xbmc/utils/LED.h =================================================================== --- xbmc/utils/LED.h (revision 30630) +++ xbmc/utils/LED.h (working copy) @@ -35,6 +35,27 @@ //#define SMARTXX_PWM_LIGHT 0xF701 //PWM5: Display Port brightness control //#define SMARTXX_PWM_CONTRAST 0xF703 //PWM6: Display Port contrast control +#define AMBILIGHT_SUPPORT + +#ifdef AMBILIGHT_SUPPORT +#include "cores/VideoRenderers/XBoxRenderer.h" +#endif + +#ifdef AMBILIGHT_SUPPORT + +#define MAX(a, b, c) (((a) > (b) && (a) > (c)) ? (a) : ((b) > (c) ? (b) : (c))) +#define MIN(a, b, c) (((a) < (b) && (a) < (c)) ? (a) : ((b) < (c) ? (b) : (c))) + +#define FRAMES 10 + +// Ambilight consts +#define WINDOW_SIZE 5 +#define HUE_WINDOW 5 +#define MIN_PERCENTAGE 0.01 +#define FADE2_MAX_TIME 1000 + +#endif + struct RGBVALUE { unsigned short red; @@ -64,6 +85,65 @@ public: static void CLEDControl(int ixLED); }; + +#ifdef AMBILIGHT_SUPPORT +// --- tRGBColor -------------------------------------------------------------- +typedef struct { + int r, g, b; +} tRGBColor; + +// --- tHSVColor -------------------------------------------------------------- +typedef struct { + int h, s, v; +} tHSVColor; + +// --- tHSVHistogram ---------------------------------------------------------- +typedef struct { + int h[361]; + int s[101]; + int v[101]; +} tHSVHistogramm; + +struct AMBILIGHT { + bool frameHasChanged; + bool AmbiLightIsPlaying; + + unsigned int SourceWidth; + unsigned int SourceHeight; + unsigned int RowCount; + unsigned int PixelCount; + unsigned int PixelOverflow; + int WidthRatio; + + int x; + int w; + + unsigned int R; + unsigned int G; + unsigned int B; + + unsigned int *arrH; + unsigned int *arrS; + unsigned int *arrV; + + // new calculation + tHSVHistogramm *hsvh; + tHSVColor *hsvMatrix; + // end new calculation + + YUVCOEF yuvcoef; + YUVRANGE yuvrange; + + unsigned int recentFrame; + + float gammaR; + float gammaG; + float gammaB; + float gammaScaleR; + float gammaScaleG; + float gammaScaleB; }; +#endif + class ILEDSmartxxRGB : public CThread { protected: @@ -78,6 +158,10 @@ DWORD dwFrameTime; bool bRepeat; +#ifdef AMBILIGHT_SUPPORT + bool isPaused; +#endif + void getRGBValues(const CStdString &strRGBa, const CStdString &strRGBb, const CStdString &strWhiteA, const CStdString &strWhiteB, RGBVALUES* s_rgb); bool SetRGBStatus(const CStdString &strStatus); @@ -85,17 +169,44 @@ ILEDSmartxxRGB(); ~ILEDSmartxxRGB(); +#ifdef AMBILIGHT_SUPPORT + AMBILIGHT AmbiLightParams; + void AmbiLightSetAverageColor(unsigned int R,unsigned int B,unsigned int G); + void AmbiLightRGBSet(); + tRGBColor AmbiLightYUV2RGB(BYTE y, BYTE u, BYTE v); + void AmbiLightCheckSkip(unsigned int oldR,unsigned int oldG,unsigned int oldB,unsigned int newR,unsigned int newG,unsigned int newB); + tHSVColor RGB2HSV(tRGBColor rgb); + tRGBColor HSV2RGB(tHSVColor color); +#endif + virtual void OnStartup(); virtual void OnExit(); virtual void Process(); virtual bool IsRunning(); virtual bool Start(); virtual void Stop(); +#ifdef AMBILIGHT_SUPPORT + virtual void Pause(); + virtual void Continue(); + virtual bool IsPaused(); +#endif + bool SetRGBState(const CStdString &strRGB1, const CStdString &strRGB2, const CStdString &strWhiteA, const CStdString &strWhiteB, const CStdString &strTransition, int iTranTime); +#ifdef AMBILIGHT_SUPPORT + bool SetRGBState(tRGBColor rgb); +#endif //can used outsite to pass the values directly to the RGB port! //Don't forget to check if there is a SmartXX V3/OPX! -> CSysInfo::SmartXXModCHIP() bool SetRGBLed(int red, int green, int blue, int white); +#ifdef AMBILIGHT_SUPPORT + void AmbiLightUpdateFrameNumber(); + void AmbiLightRGBCalculate(BYTE *s0,BYTE *s1,BYTE *s2); + void AmbiLightParamsInit(unsigned int SourceWidth,unsigned int SourceHeight,YUVCOEF yuvcoef,YUVRANGE yuvrange); + void AmbiLightParamsUpdate(int x,int w,int widthRatio); + void AmbiLightUpdate(); + void AmbiLightStop(); +#endif }; extern ILEDSmartxxRGB g_iledSmartxxrgb;