/addons/visualization.waveform/Waveform.vis
/addons/visualization.waveform/Waveform_win32.vis
/addons/visualization.itunes/iTunes.mvis
+/addons/visualization.fishbmc/fishbmc.vis
/addons/script.module.pil/
/addons/pvr.*
/xbmc/visualizations/XBMCProjectM/libprojectM/CMakeFiles/
/xbmc/visualizations/XBMCProjectM/libprojectM/cmake_install.cmake
/xbmc/visualizations/XBMCProjectM/libprojectM/config.inp
+/xbmc/visualizations/fishBMC/Makefile
/xbmc/win32/git_rev.h
endif
VIS_DIRS=xbmc/visualizations/OpenGLSpectrum \
- xbmc/visualizations/WaveForm
+ xbmc/visualizations/WaveForm \
+ xbmc/visualizations/fishBMC
ifneq (@DISABLE_PROJECTM@,1)
VIS_DIRS+= xbmc/visualizations/XBMCProjectM
<item>
<label>$LOCALIZE[557]:</label>
<label2>$INFO[ListItem.Artist]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Artist)</visible>
</item>
<item>
<label>$LOCALIZE[515]:</label>
<label2>$INFO[ListItem.Genre]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Genre)</visible>
</item>
<item>
<label>$LOCALIZE[563]:</label>
<label2>$INFO[ListItem.Rating]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Rating)</visible>
</item>
<item>
<label>$LOCALIZE[175]:</label>
<label2>$INFO[ListItem.Property(Album_Mood)]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Property(Album_Mood))</visible>
</item>
<item>
<label>$LOCALIZE[176]:</label>
<label2>$INFO[ListItem.Property(Album_Style)]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Property(Album_Style))</visible>
</item>
<item>
<label>$LOCALIZE[21895]:</label>
<label2>$INFO[ListItem.Property(Album_Theme)]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Property(Album_Theme))</visible>
</item>
<item>
<label>146</label>
<label2>$INFO[ListItem.Property(Album_Type)]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Property(Album_Type))</visible>
</item>
<item>
<label>$LOCALIZE[21899]:</label>
<label2>$INFO[ListItem.Property(Album_Label)]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Property(Album_Label))</visible>
</item>
<item>
<label>$LOCALIZE[562]:</label>
<label2>$INFO[ListItem.Year]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Year)</visible>
</item>
</content>
<item>
<label>$LOCALIZE[21893]:</label>
<label2>$INFO[ListItem.Property(Artist_Born)]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Property(Artist_Born))</visible>
</item>
<item>
<label>$LOCALIZE[21894]:</label>
<label2>$INFO[ListItem.Property(Artist_Formed)]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Property(Artist_Formed))</visible>
</item>
<item>
<label>$LOCALIZE[515]:</label>
<label2>$INFO[ListItem.Genre]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Genre)</visible>
</item>
<item>
<label>$LOCALIZE[175]:</label>
<label2>$INFO[ListItem.Property(Artist_Mood)]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Property(Artist_Mood))</visible>
</item>
<item>
<label>$LOCALIZE[176]:</label>
<label2>$INFO[ListItem.Property(Artist_Style)]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Property(Artist_Style))</visible>
</item>
<item>
<label>$LOCALIZE[21892]:</label>
<label2>$INFO[ListItem.Property(Artist_Instrument)]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Property(Artist_Instrument))</visible>
</item>
<item>
<label>$LOCALIZE[21897]:</label>
<label2>$INFO[ListItem.Property(Artist_Died)]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Property(Artist_Died))</visible>
</item>
<item>
<label>$LOCALIZE[21896]:</label>
<label2>$INFO[ListItem.Property(Artist_Disbanded)]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Property(Artist_Disbanded))</visible>
</item>
<item>
<label>$LOCALIZE[21898]:</label>
<label2>$INFO[ListItem.Property(Artist_YearsActive)]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Property(Artist_YearsActive))</visible>
</item>
</content>
</control>
<include>Clock</include>
</controls>
-</window>
\ No newline at end of file
+</window>
<item>
<label>$LOCALIZE[20376]:</label>
<label2>$INFO[ListItem.OriginalTitle]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.OriginalTitle) + !stringcompare(ListItem.OriginalTitle,Listitem.Title)</visible>
</item>
<item>
<item>
<label>$LOCALIZE[20417]:</label>
<label2>$INFO[ListItem.Writer]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Writer)</visible>
</item>
<item>
<label>$LOCALIZE[572]:</label>
<label2>$INFO[ListItem.Studio]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Studio)</visible>
</item>
<item>
<label>$LOCALIZE[515]:</label>
<label2>$INFO[ListItem.Genre]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Genre)</visible>
</item>
<item>
<label>$LOCALIZE[562]:</label>
<label2>$INFO[ListItem.Year]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Year)</visible>
</item>
<item>
<label>$LOCALIZE[2050]:</label>
<label2>$INFO[ListItem.Duration] $LOCALIZE[12391]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Duration)</visible>
</item>
<item>
<label>$LOCALIZE[563]:</label>
<label2>$INFO[ListItem.RatingAndVotes]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.RatingAndVotes)</visible>
</item>
<item>
<label>$LOCALIZE[202]:</label>
<label2>$INFO[ListItem.TagLine]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.TagLine)</visible>
</item>
<item>
<label>$LOCALIZE[203]:</label>
<label2>$INFO[ListItem.PlotOutline]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.PlotOutline)</visible>
</item>
<item>
<label>$LOCALIZE[20074]:</label>
<label2>$INFO[ListItem.mpaa]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.mpaa)</visible>
</item>
<item>
<label>$LOCALIZE[15311]</label>
<label2>$INFO[ListItem.FilenameAndPath]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.FilenameAndPath)</visible>
</item>
</content>
<item>
<label>$LOCALIZE[20360]:</label>
<label2>$INFO[listitem.episode] [COLOR=grey] ($INFO[ListItem.Property(WatchedEpisodes),, $LOCALIZE[16102]] - $INFO[ListItem.Property(UnWatchedEpisodes), , $LOCALIZE[16101]])[/COLOR]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Episode)</visible>
</item>
<item>
<label>$LOCALIZE[31322]:</label>
<label2>$INFO[ListItem.Premiered]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Premiered)</visible>
</item>
<item>
<label>$LOCALIZE[515]:</label>
<label2>$INFO[ListItem.Genre]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Genre)</visible>
</item>
<item>
<label>$LOCALIZE[562]:</label>
<label2>$INFO[ListItem.Year]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Year)</visible>
</item>
<item>
<label>$LOCALIZE[563]:</label>
<label2>$INFO[ListItem.Rating]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Rating)</visible>
</item>
<item>
<label>$LOCALIZE[15311]</label>
<label2>$INFO[ListItem.FilenameAndPath]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.FilenameAndPath)</visible>
</item>
</content>
<item>
<label>$LOCALIZE[20364]:</label>
<label2>$INFO[ListItem.TVShowTitle]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.TVShowTitle)</visible>
</item>
<item>
<label>$LOCALIZE[20373]:</label>
<label2>$INFO[ListItem.Season]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Season)</visible>
</item>
<item>
<label>$LOCALIZE[20359]:</label>
<label2>$INFO[ListItem.Episode]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Episode)</visible>
</item>
<item>
<label>$LOCALIZE[31322]:</label>
<label2>$INFO[ListItem.Premiered]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Premiered)</visible>
</item>
<item>
<label>$LOCALIZE[515]:</label>
<label2>$INFO[ListItem.Genre]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Genre)</visible>
</item>
<item>
<item>
<label>$LOCALIZE[20417]:</label>
<label2>$INFO[ListItem.Writer]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Writer)</visible>
</item>
<item>
<label>$LOCALIZE[562]:</label>
<label2>$INFO[ListItem.Year]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Year)</visible>
</item>
<item>
<label>$LOCALIZE[2050]:</label>
<label2>$INFO[ListItem.Duration] $LOCALIZE[12391]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Duration)</visible>
</item>
<item>
<label>$LOCALIZE[563]:</label>
<label2>$INFO[ListItem.Rating]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Rating)</visible>
</item>
<item>
<label>$LOCALIZE[15311]</label>
<label2>$INFO[ListItem.FilenameAndPath]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.FilenameAndPath)</visible>
</item>
</content>
<item>
<label>$LOCALIZE[557]:</label>
<label2>$INFO[ListItem.Artist]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Artist)</visible>
</item>
<item>
<label>$LOCALIZE[558]:</label>
<label2>$INFO[ListItem.Album]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Album)</visible>
</item>
<item>
<label>$LOCALIZE[515]:</label>
<label2>$INFO[ListItem.Genre]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Genre)</visible>
</item>
<item>
<label>$LOCALIZE[572]:</label>
<label2>$INFO[ListItem.Studio]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Studio)</visible>
</item>
<item>
<label>$LOCALIZE[20417]:</label>
<label2>$INFO[ListItem.Writer]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Writer)</visible>
</item>
<item>
<label>$LOCALIZE[20339]:</label>
<label2>$INFO[ListItem.Director]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Director)</visible>
</item>
<item>
<label>$LOCALIZE[562]:</label>
<label2>$INFO[ListItem.Year]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Year)</visible>
</item>
<item>
<label>$LOCALIZE[2050]:</label>
<label2>$INFO[ListItem.Duration] $LOCALIZE[12391]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.Duration)</visible>
</item>
<item>
<label>$LOCALIZE[15311]</label>
<label2>$INFO[ListItem.FilenameAndPath]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(ListItem.FilenameAndPath)</visible>
</item>
</content>
<item>
<label>$LOCALIZE[31909]</label>
<icon>-</icon>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!Weather.IsFetched</visible>
</item>
<item>
<property name="LowTemp">$INFO[Window.Property(Day0.LowTemp)]</property>
<property name="Outlook">$INFO[Window.Property(Day0.Outlook)]</property>
<property name="TempUnits">$INFO[System.TemperatureUnits]</property>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>Weather.IsFetched + !IsEmpty(Window.Property(Day0.Title))</visible>
</item>
<item>
<property name="LowTemp">$INFO[Window.Property(Day1.LowTemp)]</property>
<property name="Outlook">$INFO[Window.Property(Day1.Outlook)]</property>
<property name="TempUnits">$INFO[System.TemperatureUnits]</property>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>Weather.IsFetched + !IsEmpty(Window.Property(Day1.Title))</visible>
</item>
<item>
<property name="LowTemp">$INFO[Window.Property(Day2.LowTemp)]</property>
<property name="Outlook">$INFO[Window.Property(Day2.Outlook)]</property>
<property name="TempUnits">$INFO[System.TemperatureUnits]</property>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>Weather.IsFetched + !IsEmpty(Window.Property(Day2.Title))</visible>
</item>
<item>
<property name="LowTemp">$INFO[Window.Property(Day3.LowTemp)]</property>
<property name="Outlook">$INFO[Window.Property(Day3.Outlook)]</property>
<property name="TempUnits">$INFO[System.TemperatureUnits]</property>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>Weather.IsFetched + !IsEmpty(Window.Property(Day3.Title))</visible>
</item>
<item>
<property name="LowTemp">$INFO[Window.Property(Day4.LowTemp)]</property>
<property name="Outlook">$INFO[Window.Property(Day4.Outlook)]</property>
<property name="TempUnits">$INFO[System.TemperatureUnits]</property>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>Weather.IsFetched + !IsEmpty(Window.Property(Day4.Title))</visible>
</item>
<item>
<property name="LowTemp">$INFO[Window.Property(Day5.LowTemp)]</property>
<property name="Outlook">$INFO[Window.Property(Day5.Outlook)]</property>
<property name="TempUnits">$INFO[System.TemperatureUnits]</property>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>Weather.IsFetched + !IsEmpty(Window.Property(Day5.Title))</visible>
</item>
<item>
<property name="LowTemp">$INFO[Window.Property(Day6.LowTemp)]</property>
<property name="Outlook">$INFO[Window.Property(Day6.Outlook)]</property>
<property name="TempUnits">$INFO[System.TemperatureUnits]</property>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>Weather.IsFetched + !IsEmpty(Window.Property(Day6.Title))</visible>
</item>
<item>
<property name="LowTemp">$INFO[Window.Property(Day7.LowTemp)]</property>
<property name="Outlook">$INFO[Window.Property(Day7.Outlook)]</property>
<property name="TempUnits">$INFO[System.TemperatureUnits]</property>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>Weather.IsFetched + !IsEmpty(Window.Property(Day7.Title))</visible>
</item>
<item>
<property name="LowTemp">$INFO[Window.Property(Day8.LowTemp)]</property>
<property name="Outlook">$INFO[Window.Property(Day8.Outlook)]</property>
<property name="TempUnits">$INFO[System.TemperatureUnits]</property>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>Weather.IsFetched + !IsEmpty(Window.Property(Day8.Title))</visible>
</item>
<item>
<property name="LowTemp">$INFO[Window.Property(Day9.LowTemp)]</property>
<property name="Outlook">$INFO[Window.Property(Day9.Outlook)]</property>
<property name="TempUnits">$INFO[System.TemperatureUnits]</property>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>Weather.IsFetched + !IsEmpty(Window.Property(Day9.Title))</visible>
</item>
</content>
<item id="1">
<label>128</label>
<label2>5</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
</item>
<item id="2">
<label>513</label>
<label2>31112</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
</item>
<item id="3">
<label>31102</label>
<label2>31112</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
</item>
<item id="4">
<label>24000</label>
<label2>31200</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
</item>
</content>
</control>
<onclick>Close</onclick>
<onclick>XBMC.RunScript($INFO[Skin.String(SubtitleScript_Path)])</onclick>
<altclick>Skin.SetAddon(SubtitleScript_Path,xbmc.python.subtitles)</altclick>
- <altclick>Close</altclick>
- <altclick>XBMC.RunScript($INFO[Skin.String(SubtitleScript_Path)])</altclick>
+ <altclick condition="Window.IsVisible(VideoOSD)">Dialog.Close(VideoOSD)</altclick>
+ <altclick condition="!IsEmpty(Skin.String(SubtitleScript_Path))">XBMC.RunScript($INFO[Skin.String(SubtitleScript_Path)])</altclick>
<usealttexture>IsEmpty(Skin.String(SubtitleScript_Path))</usealttexture>
</control>
<control type="button" id="251">
<item id="1">
<label>$LOCALIZE[342]</label>
<label2>$LOCALIZE[31206] $INFO[Control.GetLabel(110)] $LOCALIZE[31025]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>Control.IsVisible(119)</visible>
</item>
<item id="2">
<label>$LOCALIZE[20343]</label>
<label2>$LOCALIZE[31206] $INFO[Control.GetLabel(120)] $LOCALIZE[31025]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>Control.IsVisible(129)</visible>
</item>
<item id="3">
<label>$LOCALIZE[33054]</label>
<label2>$LOCALIZE[31206] $INFO[Control.GetLabel(130)] $LOCALIZE[31025]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>Control.IsVisible(139)</visible>
</item>
<item id="4">
<label>$LOCALIZE[20360]</label>
<label2>$LOCALIZE[31206] $INFO[Control.GetLabel(140)] $LOCALIZE[31025]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>Control.IsVisible(149)</visible>
</item>
<item id="5">
<label>$LOCALIZE[20389]</label>
<label2>$LOCALIZE[31206] $INFO[Control.GetLabel(150)] $LOCALIZE[31025]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>Control.IsVisible(159)</visible>
</item>
<item id="6">
<label>$LOCALIZE[133]</label>
<label2>$LOCALIZE[31206] $INFO[Control.GetLabel(160)] $LOCALIZE[31025]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>Control.IsVisible(169)</visible>
</item>
<item id="7">
<label>$LOCALIZE[132]</label>
<label2>$LOCALIZE[31206] $INFO[Control.GetLabel(170)] $LOCALIZE[31025]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>Control.IsVisible(179)</visible>
</item>
<item id="8">
<label>$LOCALIZE[134]</label>
<label2>$LOCALIZE[31206] $INFO[Control.GetLabel(180)] $LOCALIZE[31025]</label2>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>Control.IsVisible(189)</visible>
</item>
<item id="10">
<content>
<item>
<icon>$LOCALIZE[31909]</icon>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>IsEmpty(Window.Property(Daily.IsFetched))</visible>
</item>
<item>
<label2>$INFO[Window.Property(Daily.1.HighTemperature),[COLOR=grey2]$LOCALIZE[419] :[/COLOR][B] ,[/B]] $INFO[Window.Property(Daily.1.LowTemperature),[COLOR=grey2]$LOCALIZE[418] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Daily.1.Precipitation),[COLOR=grey2]$LOCALIZE[33022] :[/COLOR][B] ,[/B]]</label2>
<thumb>$INFO[Window.Property(Daily.1.OutlookIcon)]</thumb>
<icon>$INFO[Window.Property(Daily.1.Outlook),[COLOR=grey2]$LOCALIZE[33030]: [/COLOR]][CR][COLOR=grey2]$LOCALIZE[383]: [/COLOR]$INFO[Window.Property(Daily.1.WindSpeed)] $INFO[Window.Property(Daily.1.WindDirection)]</icon>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(Window.Property(Daily.IsFetched))</visible>
</item>
<item>
<label2>$INFO[Window.Property(Daily.2.HighTemperature),[COLOR=grey2]$LOCALIZE[419] :[/COLOR][B] ,[/B]] $INFO[Window.Property(Daily.2.LowTemperature),[COLOR=grey2]$LOCALIZE[418] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Daily.2.Precipitation),[COLOR=grey2]$LOCALIZE[33022] :[/COLOR][B] ,[/B]]</label2>
<thumb>$INFO[Window.Property(Daily.2.OutlookIcon)]</thumb>
<icon>$INFO[Window.Property(Daily.2.Outlook),[COLOR=grey2]$LOCALIZE[33030]: [/COLOR]][CR][COLOR=grey2]$LOCALIZE[383]: [/COLOR]$INFO[Window.Property(Daily.2.WindSpeed)] $INFO[Window.Property(Daily.2.WindDirection)]</icon>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(Window.Property(Daily.IsFetched))</visible>
</item>
<item>
<label2>$INFO[Window.Property(Daily.3.HighTemperature),[COLOR=grey2]$LOCALIZE[419] :[/COLOR][B] ,[/B]] $INFO[Window.Property(Daily.3.LowTemperature),[COLOR=grey2]$LOCALIZE[418] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Daily.3.Precipitation),[COLOR=grey2]$LOCALIZE[33022] :[/COLOR][B] ,[/B]]</label2>
<thumb>$INFO[Window.Property(Daily.3.OutlookIcon)]</thumb>
<icon>$INFO[Window.Property(Daily.3.Outlook),[COLOR=grey2]$LOCALIZE[33030]: [/COLOR]][CR][COLOR=grey2]$LOCALIZE[383]: [/COLOR]$INFO[Window.Property(Daily.3.WindSpeed)] $INFO[Window.Property(Daily.3.WindDirection)]</icon>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(Window.Property(Daily.IsFetched))</visible>
</item>
<item>
<label2>$INFO[Window.Property(Daily.4.HighTemperature),[COLOR=grey2]$LOCALIZE[419] :[/COLOR][B] ,[/B]] $INFO[Window.Property(Daily.4.LowTemperature),[COLOR=grey2]$LOCALIZE[418] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Daily.4.Precipitation),[COLOR=grey2]$LOCALIZE[33022] :[/COLOR][B] ,[/B]]</label2>
<thumb>$INFO[Window.Property(Daily.4.OutlookIcon)]</thumb>
<icon>$INFO[Window.Property(Daily.4.Outlook),[COLOR=grey2]$LOCALIZE[33030]: [/COLOR]][CR][COLOR=grey2]$LOCALIZE[383]: [/COLOR]$INFO[Window.Property(Daily.4.WindSpeed)] $INFO[Window.Property(Daily.4.WindDirection)]</icon>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(Window.Property(Daily.IsFetched))</visible>
</item>
<item>
<label2>$INFO[Window.Property(Daily.5.HighTemperature),[COLOR=grey2]$LOCALIZE[419] :[/COLOR][B] ,[/B]] $INFO[Window.Property(Daily.5.LowTemperature),[COLOR=grey2]$LOCALIZE[418] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Daily.5.Precipitation),[COLOR=grey2]$LOCALIZE[33022] :[/COLOR][B] ,[/B]]</label2>
<thumb>$INFO[Window.Property(Daily.5.OutlookIcon)]</thumb>
<icon>$INFO[Window.Property(Daily.5.Outlook),[COLOR=grey2]$LOCALIZE[33030]: [/COLOR]][CR][COLOR=grey2]$LOCALIZE[383]: [/COLOR]$INFO[Window.Property(Daily.5.WindSpeed)] $INFO[Window.Property(Daily.5.WindDirection)]</icon>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(Window.Property(Daily.IsFetched))</visible>
</item>
<item>
<label2>$INFO[Window.Property(Daily.6.HighTemperature),[COLOR=grey2]$LOCALIZE[419] :[/COLOR][B] ,[/B]] $INFO[Window.Property(Daily.6.LowTemperature),[COLOR=grey2]$LOCALIZE[418] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Daily.6.Precipitation),[COLOR=grey2]$LOCALIZE[33022] :[/COLOR][B] ,[/B]]</label2>
<thumb>$INFO[Window.Property(Daily.6.OutlookIcon)]</thumb>
<icon>$INFO[Window.Property(Daily.6.Outlook),[COLOR=grey2]$LOCALIZE[33030]: [/COLOR]][CR][COLOR=grey2]$LOCALIZE[383]: [/COLOR]$INFO[Window.Property(Daily.6.WindSpeed)] $INFO[Window.Property(Daily.6.WindDirection)]</icon>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(Window.Property(Daily.IsFetched))</visible>
</item>
<item>
<label2>$INFO[Window.Property(Daily.7.HighTemperature),[COLOR=grey2]$LOCALIZE[419] :[/COLOR][B] ,[/B]] $INFO[Window.Property(Daily.7.LowTemperature),[COLOR=grey2]$LOCALIZE[418] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Daily.7.Precipitation),[COLOR=grey2]$LOCALIZE[33022] :[/COLOR][B] ,[/B]]</label2>
<thumb>$INFO[Window.Property(Daily.7.OutlookIcon)]</thumb>
<icon>$INFO[Window.Property(Daily.7.Outlook),[COLOR=grey2]$LOCALIZE[33030]: [/COLOR]][CR][COLOR=grey2]$LOCALIZE[383]: [/COLOR]$INFO[Window.Property(Daily.7.WindSpeed)] $INFO[Window.Property(Daily.7.WindDirection)]</icon>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(Window.Property(Daily.IsFetched))</visible>
</item>
<item>
<label2>$INFO[Window.Property(Daily.8.HighTemperature),[COLOR=grey2]$LOCALIZE[419] :[/COLOR][B] ,[/B]] $INFO[Window.Property(Daily.8.LowTemperature),[COLOR=grey2]$LOCALIZE[418] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Daily.8.Precipitation),[COLOR=grey2]$LOCALIZE[33022] :[/COLOR][B] ,[/B]]</label2>
<thumb>$INFO[Window.Property(Daily.8.OutlookIcon)]</thumb>
<icon>$INFO[Window.Property(Daily.8.Outlook),[COLOR=grey2]$LOCALIZE[33030]: [/COLOR]][CR][COLOR=grey2]$LOCALIZE[383]: [/COLOR]$INFO[Window.Property(Daily.8.WindSpeed)] $INFO[Window.Property(Daily.8.WindDirection)]</icon>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(Window.Property(Daily.IsFetched))</visible>
</item>
<item>
<label2>$INFO[Window.Property(Daily.9.HighTemperature),[COLOR=grey2]$LOCALIZE[419] :[/COLOR][B] ,[/B]] $INFO[Window.Property(Daily.9.LowTemperature),[COLOR=grey2]$LOCALIZE[418] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Daily.9.Precipitation),[COLOR=grey2]$LOCALIZE[33022] :[/COLOR][B] ,[/B]]</label2>
<thumb>$INFO[Window.Property(Daily.9.OutlookIcon)]</thumb>
<icon>$INFO[Window.Property(Daily.9.Outlook),[COLOR=grey2]$LOCALIZE[33030]: [/COLOR]][CR][COLOR=grey2]$LOCALIZE[383]: [/COLOR]$INFO[Window.Property(Daily.9.WindSpeed)] $INFO[Window.Property(Daily.9.WindDirection)]</icon>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(Window.Property(Daily.IsFetched))</visible>
</item>
<item>
<label2>$INFO[Window.Property(Daily.10.HighTemperature),[COLOR=grey2]$LOCALIZE[419] :[/COLOR][B] ,[/B]] $INFO[Window.Property(Daily.10.LowTemperature),[COLOR=grey2]$LOCALIZE[418] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Daily.10.Precipitation),[COLOR=grey2]$LOCALIZE[33022] :[/COLOR][B] ,[/B]]</label2>
<thumb>$INFO[Window.Property(Daily.10.OutlookIcon)]</thumb>
<icon>$INFO[Window.Property(Daily.10.Outlook),[COLOR=grey2]$LOCALIZE[33030]: [/COLOR]][CR][COLOR=grey2]$LOCALIZE[383]: [/COLOR]$INFO[Window.Property(Daily.10.WindSpeed)] $INFO[Window.Property(Daily.10.WindDirection)]</icon>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(Window.Property(Daily.IsFetched))</visible>
</item>
</content>
<content>
<item>
<icon>$LOCALIZE[31909]</icon>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>IsEmpty(Window.Property(Hourly.IsFetched))</visible>
</item>
<item>
<label2>$INFO[Window.Property(Hourly.1.Temperature),[COLOR=grey2]$LOCALIZE[419] :[/COLOR][B] ,[/B]] $INFO[Window.Property(Hourly.1.FeelsLike),[COLOR=grey2]$LOCALIZE[402] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Hourly.1.Humidity),[COLOR=grey2]$LOCALIZE[406] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Hourly.1.ChancePrecipitation),[COLOR=grey2]$LOCALIZE[31908] :[/COLOR][B] ,[/B]]</label2>
<thumb>$INFO[Window.Property(Hourly.1.OutlookIcon)]</thumb>
<icon>$INFO[Window.Property(Hourly.1.Outlook),[COLOR=grey2]$LOCALIZE[33030]: [/COLOR]][CR][COLOR=grey2]$LOCALIZE[383]: [/COLOR]$INFO[Window.Property(Hourly.1.WindSpeed)] $INFO[Window.Property(Hourly.1.WindDirection)]</icon>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(Window.Property(Hourly.IsFetched))</visible>
</item>
<item>
<label2>$INFO[Window.Property(Hourly.2.Temperature),[COLOR=grey2]$LOCALIZE[419] :[/COLOR][B] ,[/B]] $INFO[Window.Property(Hourly.2.FeelsLike),[COLOR=grey2]$LOCALIZE[402] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Hourly.2.Humidity),[COLOR=grey2]$LOCALIZE[406] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Hourly.2.ChancePrecipitation),[COLOR=grey2]$LOCALIZE[31908] :[/COLOR][B] ,[/B]]</label2>
<thumb>$INFO[Window.Property(Hourly.2.OutlookIcon)]</thumb>
<icon>$INFO[Window.Property(Hourly.2.Outlook),[COLOR=grey2]$LOCALIZE[33030]: [/COLOR]][CR][COLOR=grey2]$LOCALIZE[383]: [/COLOR]$INFO[Window.Property(Hourly.2.WindSpeed)] $INFO[Window.Property(Hourly.2.WindDirection)]</icon>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(Window.Property(Hourly.IsFetched))</visible>
</item>
<item>
<label2>$INFO[Window.Property(Hourly.3.Temperature),[COLOR=grey2]$LOCALIZE[419] :[/COLOR][B] ,[/B]] $INFO[Window.Property(Hourly.3.FeelsLike),[COLOR=grey2]$LOCALIZE[402] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Hourly.3.Humidity),[COLOR=grey2]$LOCALIZE[406] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Hourly.3.ChancePrecipitation),[COLOR=grey2]$LOCALIZE[31908] :[/COLOR][B] ,[/B]]</label2>
<thumb>$INFO[Window.Property(Hourly.3.OutlookIcon)]</thumb>
<icon>$INFO[Window.Property(Hourly.3.Outlook),[COLOR=grey2]$LOCALIZE[33030]: [/COLOR]][CR][COLOR=grey2]$LOCALIZE[383]: [/COLOR]$INFO[Window.Property(Hourly.3.WindSpeed)] $INFO[Window.Property(Hourly.3.WindDirection)]</icon>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(Window.Property(Hourly.IsFetched))</visible>
</item>
<item>
<label2>$INFO[Window.Property(Hourly.4.Temperature),[COLOR=grey2]$LOCALIZE[419] :[/COLOR][B] ,[/B]] $INFO[Window.Property(Hourly.4.FeelsLike),[COLOR=grey2]$LOCALIZE[402] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Hourly.4.Humidity),[COLOR=grey2]$LOCALIZE[406] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Hourly.4.ChancePrecipitation),[COLOR=grey2]$LOCALIZE[31908] :[/COLOR][B] ,[/B]]</label2>
<thumb>$INFO[Window.Property(Hourly.4.OutlookIcon)]</thumb>
<icon>$INFO[Window.Property(Hourly.4.Outlook),[COLOR=grey2]$LOCALIZE[33030]: [/COLOR]][CR][COLOR=grey2]$LOCALIZE[383]: [/COLOR]$INFO[Window.Property(Hourly.4.WindSpeed)] $INFO[Window.Property(Hourly.4.WindDirection)]</icon>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(Window.Property(Hourly.IsFetched))</visible>
</item>
<item>
<label2>$INFO[Window.Property(Hourly.5.Temperature),[COLOR=grey2]$LOCALIZE[419] :[/COLOR][B] ,[/B]] $INFO[Window.Property(Hourly.5.FeelsLike),[COLOR=grey2]$LOCALIZE[402] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Hourly.5.Humidity),[COLOR=grey2]$LOCALIZE[406] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Hourly.5.ChancePrecipitation),[COLOR=grey2]$LOCALIZE[31908] :[/COLOR][B] ,[/B]]</label2>
<thumb>$INFO[Window.Property(Hourly.5.OutlookIcon)]</thumb>
<icon>$INFO[Window.Property(Hourly.5.Outlook),[COLOR=grey2]$LOCALIZE[33030]: [/COLOR]][CR][COLOR=grey2]$LOCALIZE[383]: [/COLOR]$INFO[Window.Property(Hourly.5.WindSpeed)] $INFO[Window.Property(Hourly.5.WindDirection)]</icon>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(Window.Property(Hourly.IsFetched))</visible>
</item>
<item>
<label2>$INFO[Window.Property(Hourly.6.Temperature),[COLOR=grey2]$LOCALIZE[419] :[/COLOR][B] ,[/B]] $INFO[Window.Property(Hourly.6.FeelsLike),[COLOR=grey2]$LOCALIZE[402] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Hourly.6.Humidity),[COLOR=grey2]$LOCALIZE[406] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Hourly.6.ChancePrecipitation),[COLOR=grey2]$LOCALIZE[31908] :[/COLOR][B] ,[/B]]</label2>
<thumb>$INFO[Window.Property(Hourly.6.OutlookIcon)]</thumb>
<icon>$INFO[Window.Property(Hourly.6.Outlook),[COLOR=grey2]$LOCALIZE[33030]: [/COLOR]][CR][COLOR=grey2]$LOCALIZE[383]: [/COLOR]$INFO[Window.Property(Hourly.6.WindSpeed)] $INFO[Window.Property(Hourly.6.WindDirection)]</icon>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(Window.Property(Hourly.IsFetched))</visible>
</item>
<item>
<label2>$INFO[Window.Property(Hourly.7.Temperature),[COLOR=grey2]$LOCALIZE[419] :[/COLOR][B] ,[/B]] $INFO[Window.Property(Hourly.7.FeelsLike),[COLOR=grey2]$LOCALIZE[402] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Hourly.7.Humidity),[COLOR=grey2]$LOCALIZE[406] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Hourly.7.ChancePrecipitation),[COLOR=grey2]$LOCALIZE[31908] :[/COLOR][B] ,[/B]]</label2>
<thumb>$INFO[Window.Property(Hourly.7.OutlookIcon)]</thumb>
<icon>$INFO[Window.Property(Hourly.7.Outlook),[COLOR=grey2]$LOCALIZE[33030]: [/COLOR]][CR][COLOR=grey2]$LOCALIZE[383]: [/COLOR]$INFO[Window.Property(Hourly.7.WindSpeed)] $INFO[Window.Property(Hourly.7.WindDirection)]</icon>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(Window.Property(Hourly.IsFetched))</visible>
</item>
<item>
<label2>$INFO[Window.Property(Hourly.8.Temperature),[COLOR=grey2]$LOCALIZE[419] :[/COLOR][B] ,[/B]] $INFO[Window.Property(Hourly.8.FeelsLike),[COLOR=grey2]$LOCALIZE[402] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Hourly.8.Humidity),[COLOR=grey2]$LOCALIZE[406] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Hourly.8.ChancePrecipitation),[COLOR=grey2]$LOCALIZE[31908] :[/COLOR][B] ,[/B]]</label2>
<thumb>$INFO[Window.Property(Hourly.8.OutlookIcon)]</thumb>
<icon>$INFO[Window.Property(Hourly.8.Outlook),[COLOR=grey2]$LOCALIZE[33030]: [/COLOR]][CR][COLOR=grey2]$LOCALIZE[383]: [/COLOR]$INFO[Window.Property(Hourly.8.WindSpeed)] $INFO[Window.Property(Hourly.8.WindDirection)]</icon>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(Window.Property(Hourly.IsFetched))</visible>
</item>
<item>
<label2>$INFO[Window.Property(Hourly.9.Temperature),[COLOR=grey2]$LOCALIZE[419] :[/COLOR][B] ,[/B]] $INFO[Window.Property(Hourly.9.FeelsLike),[COLOR=grey2]$LOCALIZE[402] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Hourly.9.Humidity),[COLOR=grey2]$LOCALIZE[406] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Hourly.9.ChancePrecipitation),[COLOR=grey2]$LOCALIZE[31908] :[/COLOR][B] ,[/B]]</label2>
<thumb>$INFO[Window.Property(Hourly.9.OutlookIcon)]</thumb>
<icon>$INFO[Window.Property(Hourly.9.Outlook),[COLOR=grey2]$LOCALIZE[33030]: [/COLOR]][CR][COLOR=grey2]$LOCALIZE[383]: [/COLOR]$INFO[Window.Property(Hourly.9.WindSpeed)] $INFO[Window.Property(Hourly.9.WindDirection)]</icon>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(Window.Property(Hourly.IsFetched))</visible>
</item>
<item>
<label2>$INFO[Window.Property(Hourly.10.Temperature),[COLOR=grey2]$LOCALIZE[419] :[/COLOR][B] ,[/B]] $INFO[Window.Property(Hourly.10.FeelsLike),[COLOR=grey2]$LOCALIZE[402] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Hourly.10.Humidity),[COLOR=grey2]$LOCALIZE[406] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Hourly.10.ChancePrecipitation),[COLOR=grey2]$LOCALIZE[31908] :[/COLOR][B] ,[/B]]</label2>
<thumb>$INFO[Window.Property(Hourly.10.OutlookIcon)]</thumb>
<icon>$INFO[Window.Property(Hourly.10.Outlook),[COLOR=grey2]$LOCALIZE[33030]: [/COLOR]][CR][COLOR=grey2]$LOCALIZE[383]: [/COLOR]$INFO[Window.Property(Hourly.10.WindSpeed)] $INFO[Window.Property(Hourly.10.WindDirection)]</icon>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(Window.Property(Hourly.IsFetched))</visible>
</item>
<item>
<label2>$INFO[Window.Property(Hourly.11.Temperature),[COLOR=grey2]$LOCALIZE[419] :[/COLOR][B] ,[/B]] $INFO[Window.Property(Hourly.11.FeelsLike),[COLOR=grey2]$LOCALIZE[402] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Hourly.11.Humidity),[COLOR=grey2]$LOCALIZE[406] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Hourly.11.ChancePrecipitation),[COLOR=grey2]$LOCALIZE[31908] :[/COLOR][B] ,[/B]]</label2>
<thumb>$INFO[Window.Property(Hourly.11.OutlookIcon)]</thumb>
<icon>$INFO[Window.Property(Hourly.11.Outlook),[COLOR=grey2]$LOCALIZE[33030]: [/COLOR]][CR][COLOR=grey2]$LOCALIZE[383]: [/COLOR]$INFO[Window.Property(Hourly.11.WindSpeed)] $INFO[Window.Property(Hourly.11.WindDirection)]</icon>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(Window.Property(Hourly.IsFetched))</visible>
</item>
<item>
<label2>$INFO[Window.Property(Hourly.12.Temperature),[COLOR=grey2]$LOCALIZE[419] :[/COLOR][B] ,[/B]] $INFO[Window.Property(Hourly.12.FeelsLike),[COLOR=grey2]$LOCALIZE[402] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Hourly.12.Humidity),[COLOR=grey2]$LOCALIZE[406] :[/COLOR][B] ,[/B]][CR]$INFO[Window.Property(Hourly.12.ChancePrecipitation),[COLOR=grey2]$LOCALIZE[31908] :[/COLOR][B] ,[/B]]</label2>
<thumb>$INFO[Window.Property(Hourly.12.OutlookIcon)]</thumb>
<icon>$INFO[Window.Property(Hourly.12.Outlook),[COLOR=grey2]$LOCALIZE[33030]: [/COLOR]][CR][COLOR=grey2]$LOCALIZE[383]: [/COLOR]$INFO[Window.Property(Hourly.12.WindSpeed)] $INFO[Window.Property(Hourly.12.WindDirection)]</icon>
- <onclick>-</onclick>
+ <onclick>noop</onclick>
<visible>!IsEmpty(Window.Property(Hourly.IsFetched))</visible>
</item>
</content>
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<addon
+ id="visualization.fishbmc"
+ version="4.0.1"
+ name="FishBMC"
+ provider-name="26elf">
+ <extension
+ point="xbmc.player.musicviz"
+ library_linux="fishbmc.vis"
+ library_osx="fishbmc.vis"/>
+ <extension point="xbmc.addon.metadata">
+ <summary lang="en">Visualisation showing Waves and Blur Effects</summary>
+ <description lang="en">fishBMC is based on fische, a standalone sound visualisation tool. The basic principle is: draw the waveform and smear the drawing along predefined vectors. However fische also incorporates advanced beat detection, which really guarantees a great visual experience.</description>
+ <disclaimer>If this breaks anything, the pieces remain yours.</disclaimer>
+ <platform>linux osx</platform>
+ </extension>
+</addon>
--- /dev/null
+2011-02-26:
+ Initial release for XBMC 11, based on fische 4
+
--- /dev/null
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<strings>
+ <!-- settings labels -->
+ <string id="30000">Detail [CPU intensive]</string>
+ <string id="30001">Low</string>
+ <string id="30002">Normal</string>
+ <string id="30003">High</string>
+ <string id="30004">Extreme</string>
+ <string id="30005">Nervous Mode [more mode changes]</string>
+ <string id="30006">Speed [skip some frames]</string>
+ <string id="30007">Very Low</string>
+ <string id="30008">Low</string>
+ <string id="30009">Normal</string>
+ <string id="30010">High</string>
+ <string id="30011">Use a Persistence File [faster startup]</string>
+</strings>
--- /dev/null
+<settings>
+<setting id="detail" label="30000" type="enum" lvalues="30001|30002|30003|30004" default="2"/>
+<setting id="divisor" label="30006" type="enum" lvalues="30007|30008|30009|30010" default="2"/>
+<setting id="nervous" label="30005" type="bool" default="false"/>
+<setting id="filemode" label="30011" type="bool" default="false"/>
+</settings>
<?xml version="1.0" encoding="UTF-8"?>
-<addon id="xbmc.addon" version="12.0.0" provider-name="Team XBMC">
+<addon id="xbmc.addon" version="12.9.1" provider-name="Team XBMC">
<backwards-compatibility abi="12.0"/>
<requires>
<import addon="xbmc.core" version="0.1.0"/>
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59)
-AC_INIT([xbmc], [12.0rc3], [http://trac.xbmc.org])
+AC_INIT([xbmc], [12.9.1], [http://trac.xbmc.org])
AC_CONFIG_HEADERS([xbmc/config.h])
AH_TOP([#pragma once])
m4_include([m4/ax_python_devel.m4])
xbmc/visualizations/XBMCProjectM/Makefile \
xbmc/visualizations/Goom/Makefile \
xbmc/visualizations/OpenGLSpectrum/Makefile \
+ xbmc/visualizations/fishBMC/Makefile \
xbmc/visualizations/WaveForm/Makefile \
lib/addons/library.xbmc.addon/Makefile \
lib/addons/library.xbmc.gui/Makefile \
char *ssh_get_user_home_dir(void) {
+#if defined(__ANDROID__)
-+ return NULL;
++ return strdup(getenv("HOME"));
+#else
char *szPath = NULL;
struct passwd pwd;
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.xbmc.xbmc"
- android:versionCode="5"
- android:versionName="12.0-RC3" >
+ android:versionCode="7"
+ android:versionName="13.0-ALPHA1" >
<!-- This is the platform API where NativeActivity was introduced. -->
<uses-sdk android:minSdkVersion="9" />
PACKAGE=org.xbmc.xbmc-atv2
-VERSION=12.0
-REVISION=0~rc3
+VERSION=13.0
+REVISION=0~alpha1
ARCHIVE=${PACKAGE}_${VERSION}-${REVISION}_iphoneos-arm.deb
echo Creating $PACKAGE package version $VERSION revision $REVISION
PACKAGE=org.xbmc.xbmc-ios
-VERSION=12.0
-REVISION=0~rc3
+VERSION=13.0
+REVISION=0~alpha1
ARCHIVE=${PACKAGE}_${VERSION}-${REVISION}_iphoneos-arm.deb
echo Creating $PACKAGE package version $VERSION revision $REVISION
#define CONTROL_GROUP_HAS_FOCUS 29999
#define CONTROL_HAS_FOCUS 30000
-#define VERSION_MAJOR 12
+#define VERSION_MAJOR 13
#define VERSION_MINOR 0
-#define VERSION_TAG "-RC3"
+#define VERSION_TAG "-ALPHA1"
#define LISTITEM_START 35000
#define LISTITEM_THUMB (LISTITEM_START)
#define OMX_MPEG2V_DECODER OMX_VIDEO_DECODER
#define OMX_VC1_DECODER OMX_VIDEO_DECODER
#define OMX_WMV3_DECODER OMX_VIDEO_DECODER
+#define OMX_VP6_DECODER OMX_VIDEO_DECODER
#define OMX_VP8_DECODER OMX_VIDEO_DECODER
+#define OMX_THEORA_DECODER OMX_VIDEO_DECODER
+#define OMX_MJPEG_DECODER OMX_VIDEO_DECODER
#define MAX_TEXT_LENGTH 1024
bool COMXVideo::Open(CDVDStreamInfo &hints, OMXClock *clock, bool deinterlace, bool hdmi_clock_sync)
{
+ bool vflip = false;
Close();
OMX_ERRORTYPE omx_err = OMX_ErrorNone;
m_codingType = OMX_VIDEO_CodingMPEG4;
m_video_codec_name = "omx-h263";
break;
+ case CODEC_ID_VP6:
+ // this form is encoded upside down
+ vflip = true;
+ // fall through
+ case CODEC_ID_VP6F:
+ case CODEC_ID_VP6A:
+ // (role name) video_decoder.vp6
+ // VP6
+ decoder_name = OMX_VP6_DECODER;
+ m_codingType = OMX_VIDEO_CodingVP6;
+ m_video_codec_name = "omx-vp6";
+ break;
case CODEC_ID_VP8:
// (role name) video_decoder.vp8
// VP8
m_codingType = OMX_VIDEO_CodingVP8;
m_video_codec_name = "omx-vp8";
break;
+ case CODEC_ID_THEORA:
+ // (role name) video_decoder.theora
+ // theora
+ decoder_name = OMX_THEORA_DECODER;
+ m_codingType = OMX_VIDEO_CodingTheora;
+ m_video_codec_name = "omx-theora";
+ break;
+ case CODEC_ID_MJPEG:
+ case CODEC_ID_MJPEGB:
+ // (role name) video_decoder.mjpg
+ // mjpg
+ decoder_name = OMX_MJPEG_DECODER;
+ m_codingType = OMX_VIDEO_CodingMJPEG;
+ m_video_codec_name = "omx-mjpeg";
+ break;
case CODEC_ID_VC1:
case CODEC_ID_WMV3:
// (role name) video_decoder.vc1
configDisplay.transform = OMX_DISPLAY_ROT0;
break;
}
+ if (vflip)
+ configDisplay.transform = OMX_DISPLAY_MIRROR_ROT180;
omx_err = m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay);
if(omx_err != OMX_ErrorNone)
si->m_playNextAtFrame = 0;
si->m_playNextTriggered = false;
- PrepareStream(si);
+ if (!PrepareStream(si))
+ {
+ CLog::Log(LOGINFO, "PAPlayer::QueueNextFileEx - Error preparing stream");
+
+ si->m_decoder.Destroy();
+ delete si;
+ m_callback.OnQueueNextItem();
+ return false;
+ }
/* add the stream to the list */
CExclusiveLock lock(m_streamsLock);
m_pReader = NULL;
m_rtl = false;
+ m_stopped = false;
ControlType = GUICONTROL_RSS;
}
m_strRSSTags = from.m_strRSSTags;
m_pReader = NULL;
m_rtl = from.m_rtl;
+ m_stopped = from.m_stopped;
ControlType = GUICONTROL_RSS;
}
m_scrollInfo.pixelSpeed *= -1;
}
+void CGUIRSSControl::OnFocus()
+{
+ m_stopped = true;
+}
+
+void CGUIRSSControl::OnUnFocus()
+{
+ m_stopped = false;
+}
+
void CGUIRSSControl::SetIntervals(const vector<int>& vecIntervals)
{
m_vecIntervals = vecIntervals;
colors.push_back(m_label.textColor);
colors.push_back(m_headlineColor);
colors.push_back(m_channelColor);
+
+ if ( m_stopped )
+ m_scrollInfo.SetSpeed(0);
+ else
+ m_scrollInfo.SetSpeed(m_label.scrollSpeed);
+
m_label.font->DrawScrollingText(m_posX, m_posY, colors, m_label.shadowColor, m_feed, 0, m_width, m_scrollInfo);
}
virtual void Render();
virtual void OnFeedUpdate(const vecText &feed);
virtual void OnFeedRelease();
- virtual bool CanFocus() const { return false; };
+ virtual bool CanFocus() const { return true; };
virtual CRect CalcRenderRegion() const;
void SetIntervals(const std::vector<int>& vecIntervals);
void SetUrls(const std::vector<std::string>& vecUrl, bool rtl);
+ virtual void OnFocus();
+ virtual void OnUnFocus();
+
protected:
virtual bool UpdateColors();
std::vector<int> m_vecIntervals;
bool m_rtl;
CScrollInfo m_scrollInfo;
+ bool m_stopped;
};
#endif
#include "utils/StringUtils.h"
#include "TextureCache.h"
#include "music/MusicThumbLoader.h"
+#include "filesystem/Directory.h"
using namespace std;
using namespace XFILE;
CStdString result;
bool flip=false;
VECSOURCES sources(g_settings.m_musicSources);
+ AddItemPathToFileBrowserSources(sources, *m_albumItem);
g_mediaManager.GetLocalDrives(sources);
if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(1030), result, &flip))
return; // user cancelled
return m_albumItem;
}
+void CGUIDialogMusicInfo::AddItemPathToFileBrowserSources(VECSOURCES &sources, const CFileItem &item)
+{
+ CStdString itemDir;
+
+ if (item.HasMusicInfoTag() && item.GetMusicInfoTag()->GetType() == "song")
+ itemDir = URIUtils::GetParentPath(item.GetMusicInfoTag()->GetURL());
+ else
+ itemDir = item.GetPath();
+
+ if (!itemDir.IsEmpty() && CDirectory::Exists(itemDir))
+ {
+ CMediaSource itemSource;
+ itemSource.strName = g_localizeStrings.Get(36041);
+ itemSource.strPath = itemDir;
+ sources.push_back(itemSource);
+ }
+}
#include "music/Song.h"
#include "music/Artist.h"
#include "music/Album.h"
+#include "FileItem.h"
class CFileItem;
class CFileItemList;
virtual bool HasListItems() const { return true; };
virtual CFileItemPtr GetCurrentListItem(int offset = 0);
const CFileItemList& CurrentDirectory() const { return *m_albumSongs; };
+ static void AddItemPathToFileBrowserSources(VECSOURCES &sources, const CFileItem &item);
protected:
virtual void OnInitWindow();
void Update();
#include "guilib/LocalizeStrings.h"
#include "TextureCache.h"
#include "music/Album.h"
+#include "storage/MediaManager.h"
+#include "GUIDialogMusicInfo.h"
using namespace XFILE;
}
CStdString result;
- if (!CGUIDialogFileBrowser::ShowAndGetImage(items, g_settings.m_musicSources, g_localizeStrings.Get(1030), result))
+ VECSOURCES sources(g_settings.m_musicSources);
+ CGUIDialogMusicInfo::AddItemPathToFileBrowserSources(sources, *m_song);
+ g_mediaManager.GetLocalDrives(sources);
+ if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(1030), result))
return; // user cancelled
if (result == "thumb://Current")
newconnection.m_socket = accept(m_ServerSocket, &newconnection.m_cliaddr, &newconnection.m_addrlen);
if (newconnection.m_socket == INVALID_SOCKET)
- CLog::Log(LOGERROR, "AIRPLAY Server: Accept of new connection failed");
+ {
+ CLog::Log(LOGERROR, "AIRPLAY Server: Accept of new connection failed: %d", errno);
+ if (EBADF == errno)
+ {
+ Sleep(1000);
+ Initialize();
+ break;
+ }
+ }
else
{
CLog::Log(LOGINFO, "AIRPLAY Server: New connection added");
newconnection->m_socket = accept(*it, (sockaddr*)&newconnection->m_cliaddr, &newconnection->m_addrlen);
if (newconnection->m_socket == INVALID_SOCKET)
- CLog::Log(LOGERROR, "JSONRPC Server: Accept of new connection failed");
+ {
+ CLog::Log(LOGERROR, "JSONRPC Server: Accept of new connection failed: %d", errno);
+ if (EBADF == errno)
+ {
+ Sleep(1000);
+ Initialize();
+ break;
+ }
+ }
else
{
CLog::Log(LOGINFO, "JSONRPC Server: New connection added");
<key>CFBundleExecutable</key>
<string>XBMC</string>
<key>CFBundleGetInfoString</key>
- <string>12.0.rc3</string>
+ <string>13.0.alpha1</string>
<key>CFBundleIconFile</key>
<string>xbmc.icns</string>
<key>CFBundleIdentifier</key>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
- <string>12.0.rc3</string>
+ <string>13.0.alpha1</string>
<key>CFBundleVersion</key>
<string>r####</string>
<key>CFBundleSignature</key>
if (type.empty())
return; // cancelled
+ // TODO: this can be removed once these are unified.
if (type == "fanart")
- { // TODO: this can be removed once these are unified.
OnGetFanart();
- return;
- }
-
- CFileItemList items;
-
- // Current thumb
- if (m_movieItem->HasArt(type))
- {
- CFileItemPtr item(new CFileItem("thumb://Current", false));
- item->SetArt("thumb", m_movieItem->GetArt(type));
- item->SetLabel(g_localizeStrings.Get(13512));
- items.Add(item);
- }
- else if ((type == "poster" || type == "banner") && currentArt.find("thumb") != currentArt.end())
- { // add the 'thumb' type in
- CFileItemPtr item(new CFileItem("thumb://Thumb", false));
- item->SetArt("thumb", currentArt["thumb"]);
- item->SetLabel(g_localizeStrings.Get(13512));
- items.Add(item);
- }
-
- // Grab the thumbnails from the web
- vector<CStdString> thumbs;
- int season = (m_movieItem->GetVideoInfoTag()->m_type == "season") ? m_movieItem->GetVideoInfoTag()->m_iSeason : -1;
- m_movieItem->GetVideoInfoTag()->m_strPictureURL.GetThumbURLs(thumbs, type, season);
-
- for (unsigned int i = 0; i < thumbs.size(); ++i)
+ else
{
- CStdString strItemPath;
- strItemPath.Format("thumb://Remote%i", i);
- CFileItemPtr item(new CFileItem(strItemPath, false));
- item->SetArt("thumb", thumbs[i]);
- item->SetIconImage("DefaultPicture.png");
- item->SetLabel(g_localizeStrings.Get(13513));
+ CFileItemList items;
- // TODO: Do we need to clear the cached image?
- // CTextureCache::Get().ClearCachedImage(thumb);
- items.Add(item);
- }
+ // Current thumb
+ if (m_movieItem->HasArt(type))
+ {
+ CFileItemPtr item(new CFileItem("thumb://Current", false));
+ item->SetArt("thumb", m_movieItem->GetArt(type));
+ item->SetLabel(g_localizeStrings.Get(13512));
+ items.Add(item);
+ }
+ else if ((type == "poster" || type == "banner") && currentArt.find("thumb") != currentArt.end())
+ { // add the 'thumb' type in
+ CFileItemPtr item(new CFileItem("thumb://Thumb", false));
+ item->SetArt("thumb", currentArt["thumb"]);
+ item->SetLabel(g_localizeStrings.Get(13512));
+ items.Add(item);
+ }
- CStdString localThumb = CVideoThumbLoader::GetLocalArt(*m_movieItem, type);
- if (!localThumb.empty())
- {
- CFileItemPtr item(new CFileItem("thumb://Local", false));
- item->SetArt("thumb", localThumb);
- item->SetLabel(g_localizeStrings.Get(13514));
- items.Add(item);
- }
- else
- { // no local thumb exists, so we are just using the IMDb thumb or cached thumb
- // which is probably the IMDb thumb. These could be wrong, so allow the user
- // to delete the incorrect thumb
- CFileItemPtr item(new CFileItem("thumb://None", false));
- item->SetIconImage("DefaultVideo.png");
- item->SetLabel(g_localizeStrings.Get(13515));
- items.Add(item);
- }
+ // Grab the thumbnails from the web
+ vector<CStdString> thumbs;
+ int season = (m_movieItem->GetVideoInfoTag()->m_type == "season") ? m_movieItem->GetVideoInfoTag()->m_iSeason : -1;
+ m_movieItem->GetVideoInfoTag()->m_strPictureURL.GetThumbURLs(thumbs, type, season);
- CStdString result;
- VECSOURCES sources(g_settings.m_videoSources);
- AddItemPathToFileBrowserSources(sources, *m_movieItem);
- g_mediaManager.GetLocalDrives(sources);
- if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(13511), result))
- return; // user cancelled
+ for (unsigned int i = 0; i < thumbs.size(); ++i)
+ {
+ CStdString strItemPath;
+ strItemPath.Format("thumb://Remote%i", i);
+ CFileItemPtr item(new CFileItem(strItemPath, false));
+ item->SetArt("thumb", thumbs[i]);
+ item->SetIconImage("DefaultPicture.png");
+ item->SetLabel(g_localizeStrings.Get(13513));
+
+ // TODO: Do we need to clear the cached image?
+ // CTextureCache::Get().ClearCachedImage(thumb);
+ items.Add(item);
+ }
- if (result == "thumb://Current")
- return; // user chose the one they have
+ CStdString localThumb = CVideoThumbLoader::GetLocalArt(*m_movieItem, type);
+ if (!localThumb.empty())
+ {
+ CFileItemPtr item(new CFileItem("thumb://Local", false));
+ item->SetArt("thumb", localThumb);
+ item->SetLabel(g_localizeStrings.Get(13514));
+ items.Add(item);
+ }
+ else
+ { // no local thumb exists, so we are just using the IMDb thumb or cached thumb
+ // which is probably the IMDb thumb. These could be wrong, so allow the user
+ // to delete the incorrect thumb
+ CFileItemPtr item(new CFileItem("thumb://None", false));
+ item->SetIconImage("DefaultVideo.png");
+ item->SetLabel(g_localizeStrings.Get(13515));
+ items.Add(item);
+ }
- CStdString newThumb;
- if (result.Left(14) == "thumb://Remote")
- {
- int number = atoi(result.Mid(14));
- newThumb = thumbs[number];
- }
- else if (result == "thumb://Thumb")
- newThumb = currentArt["thumb"];
- else if (result == "thumb://Local")
- newThumb = localThumb;
- else if (CFile::Exists(result))
- newThumb = result;
- else // none
- newThumb.clear();
-
- // update thumb in the database
- CVideoDatabase db;
- if (db.Open())
- {
- db.SetArtForItem(m_movieItem->GetVideoInfoTag()->m_iDbId, m_movieItem->GetVideoInfoTag()->m_type, type, newThumb);
- db.Close();
- }
- CUtil::DeleteVideoDatabaseDirectoryCache(); // to get them new thumbs to show
- m_movieItem->SetArt(type, newThumb);
- if (m_movieItem->HasProperty("set_folder_thumb"))
- { // have a folder thumb to set as well
- VIDEO::CVideoInfoScanner::ApplyThumbToFolder(m_movieItem->GetProperty("set_folder_thumb").asString(), newThumb);
+ CStdString result;
+ VECSOURCES sources(g_settings.m_videoSources);
+ AddItemPathToFileBrowserSources(sources, *m_movieItem);
+ g_mediaManager.GetLocalDrives(sources);
+ if (CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(13511), result) &&
+ result != "thumb://Current") // user didn't choose the one they have
+ {
+ CStdString newThumb;
+ if (result.Left(14) == "thumb://Remote")
+ {
+ int number = atoi(result.Mid(14));
+ newThumb = thumbs[number];
+ }
+ else if (result == "thumb://Thumb")
+ newThumb = currentArt["thumb"];
+ else if (result == "thumb://Local")
+ newThumb = localThumb;
+ else if (CFile::Exists(result))
+ newThumb = result;
+ else // none
+ newThumb.clear();
+
+ // update thumb in the database
+ CVideoDatabase db;
+ if (db.Open())
+ {
+ db.SetArtForItem(m_movieItem->GetVideoInfoTag()->m_iDbId, m_movieItem->GetVideoInfoTag()->m_type, type, newThumb);
+ db.Close();
+ }
+ CUtil::DeleteVideoDatabaseDirectoryCache(); // to get them new thumbs to show
+ m_movieItem->SetArt(type, newThumb);
+ if (m_movieItem->HasProperty("set_folder_thumb"))
+ { // have a folder thumb to set as well
+ VIDEO::CVideoInfoScanner::ApplyThumbToFolder(m_movieItem->GetProperty("set_folder_thumb").asString(), newThumb);
+ }
+ m_hasUpdatedThumb = true;
+ }
}
- m_hasUpdatedThumb = true;
// Update our screen
Update();
+
+ // re-open the art selection dialog as we come back from
+ // the image selection dialog
+ OnGetArt();
}
// Allow user to select a Fanart
--- /dev/null
+ARCH=@ARCH@
+CXXFLAGS=-fPIC
+
+SLIB = @abs_top_srcdir@/addons/visualization.fishbmc/fishbmc.vis
+OBJS = analyst.o \
+ audiobuffer.o \
+ blurengine.o \
+ cpudetect.o \
+ fische.o \
+ fishbmc_addon.o \
+ screenbuffer.o \
+ vector.o \
+ vectorfield.o \
+ wavepainter.o
+
+ DEFINES += -DHAS_SDL
+ DEFINES += -DHAS_SDL_OPENGL
+ ifeq ($(findstring osx,$(ARCH)), osx)
+ LDFLAGS += -framework OpenGL
+ else
+ LDFLAGS += -lGL
+ endif
+
+
+$(SLIB): $(OBJS)
+ifeq ($(findstring osx,$(ARCH)), osx)
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -bundle -o $(SLIB) $(OBJS)
+else
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared -o $(SLIB) $(OBJS)
+endif
+
+include @abs_top_srcdir@/Makefile.include
--- /dev/null
+#include "fische_internal.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef DEBUG
+#include <stdio.h>
+#endif
+
+enum {_FISCHE__WAITING_, _FISCHE__MAYBEWAITING_, _FISCHE__BEAT_};
+
+int
+_fische__compare_int_fast16_t_ (void const* value1, void const* value2)
+{
+ return (* (int_fast16_t*) value1 - * (int_fast16_t*) value2);
+}
+
+double
+_fische__guess_frames_per_beat_ (uint_fast16_t* beat_gap_history)
+{
+ uint_fast16_t gap_history_sorted[30];
+
+ memcpy (gap_history_sorted, beat_gap_history, 30 * sizeof (uint_fast16_t));
+ qsort (gap_history_sorted, 30, sizeof (uint_fast16_t), _fische__compare_int_fast16_t_);
+
+ uint_fast16_t guess = gap_history_sorted[14];
+
+ double result = 0;
+ int count = 0;
+
+ uint_fast8_t i;
+ for (i = 0; i < 30; ++ i) {
+ if (abs (gap_history_sorted[i] - guess) <= 2) {
+ result += gap_history_sorted[i];
+ ++ count;
+ }
+ }
+
+ return result / count;
+}
+
+double
+_fische__get_audio_level_ (double* data, uint_fast32_t data_size)
+{
+ double E = 0;
+
+ uint_fast32_t i;
+ for (i = 0; i < data_size; ++ i) {
+ E += fabs (* (data + i));
+ }
+
+ if (E <= 0) E = 1e-9;
+ E /= data_size;
+
+ return log10 (E) * 10;
+}
+
+struct fische__analyst*
+fische__analyst_new (struct fische* parent) {
+
+ struct fische__analyst* retval = malloc (sizeof (struct fische__analyst));
+ retval->priv = malloc (sizeof (struct _fische__analyst_));
+
+ struct _fische__analyst_* P = retval->priv;
+
+ P->fische = parent;
+ P->bghist_head = 0;
+ P->intensity_moving_avg = 0;
+ P->intensity_std_dev = 0;
+ P->last_beat_frame = 0;
+ P->moving_avg_03 = 0;
+ P->moving_avg_30 = 0;
+ P->state = _FISCHE__WAITING_;
+ P->std_dev = 0;
+
+ P->beat_gap_history = malloc (30 * sizeof (uint_fast16_t));
+ memset (P->beat_gap_history, '\0', 30 * sizeof (uint_fast16_t));
+
+ retval->frames_per_beat = 0;
+ retval->relative_energy = 1;
+
+ return retval;
+}
+
+void
+fische__analyst_free (struct fische__analyst* self)
+{
+ if (!self)
+ return;
+
+ free (self->priv->beat_gap_history);
+ free (self->priv);
+ free (self);
+}
+
+int_fast8_t
+fische__analyst_analyse (struct fische__analyst* self,
+ double* data,
+ uint_fast16_t size)
+{
+ if (!size)
+ return -1;
+
+ struct _fische__analyst_* P = self->priv;
+
+ double dezibel = _fische__get_audio_level_ (data, size * 2);
+
+ if (P->moving_avg_30 == 0)
+ P->moving_avg_30 = dezibel;
+ else
+ P->moving_avg_30 = P->moving_avg_30 * 0.9667 + dezibel * 0.0333;
+
+ P->std_dev = P->std_dev * 0.9667 + fabs (dezibel - P->moving_avg_30) * 0.0333;
+
+ uint_fast32_t frameno = P->fische->frame_counter;
+ if ( (frameno - P->last_beat_frame) > 90) {
+ self->frames_per_beat = 0;
+ memset (P->beat_gap_history, '\0', 30 * sizeof (uint_fast16_t));
+ P->bghist_head = 0;
+ }
+
+ self->relative_energy = P->moving_avg_03 / P->moving_avg_30;
+
+ double relative_intensity = 0;
+ double new_frames_per_beat;
+
+ switch (P->state) {
+ case _FISCHE__WAITING_:
+ // don't bother if intensity too low
+ if (dezibel < P->moving_avg_30 + P->std_dev)
+ break;
+
+ // initialisation fallbacks
+ if (P->std_dev == 0)
+ relative_intensity = 1; // avoid div by 0
+ else
+ relative_intensity = (dezibel - P->moving_avg_30) / P->std_dev;
+
+ if (P->intensity_moving_avg == 0)
+ P->intensity_moving_avg = relative_intensity; // initial assignment
+ else
+ P->intensity_moving_avg = P->intensity_moving_avg * 0.95 + relative_intensity * 0.05;
+
+ // update intensity standard deviation
+ P->intensity_std_dev = P->intensity_std_dev * 0.95 + fabs (P->intensity_moving_avg - relative_intensity) * 0.05;
+
+ // we DO have a beat
+ P->state = _FISCHE__BEAT_;
+
+ // update beat gap history
+ P->beat_gap_history[P->bghist_head++] = frameno - P->last_beat_frame;
+ if (P->bghist_head == 30)
+ P->bghist_head = 0;
+
+ // remember this as the last beat
+ P->last_beat_frame = frameno;
+
+ // reset the short-term moving average
+ P->moving_avg_03 = dezibel;
+
+ // try a guess at the tempo
+ new_frames_per_beat = _fische__guess_frames_per_beat_ (P->beat_gap_history);
+ if ( (self->frames_per_beat) && (self->frames_per_beat / new_frames_per_beat < 1.2) && (new_frames_per_beat / self->frames_per_beat < 1.2))
+ self->frames_per_beat = (self->frames_per_beat * 2 + new_frames_per_beat) / 3;
+ else
+ self->frames_per_beat = new_frames_per_beat;
+
+ // return based on relative beat intensity
+ if (relative_intensity > P->intensity_moving_avg + 3 * P->intensity_std_dev)
+ return 4;
+ if (relative_intensity > P->intensity_moving_avg + 2 * P->intensity_std_dev)
+ return 3;
+ if (relative_intensity > P->intensity_moving_avg + 1 * P->intensity_std_dev)
+ return 2;
+
+ return 1;
+
+ case _FISCHE__BEAT_:
+ case _FISCHE__MAYBEWAITING_:
+ // update short term moving average
+ P->moving_avg_03 = P->moving_avg_03 * 0.6667 + dezibel * 0.3333;
+
+ // needs to be low enough twice to exit BEAT state
+ if (P->moving_avg_03 < P->moving_avg_30 + P->std_dev) {
+ P->state = P->state == _FISCHE__MAYBEWAITING_ ? _FISCHE__WAITING_ : _FISCHE__MAYBEWAITING_;
+ return 0;
+ }
+ }
+
+ // report level too low
+ if (dezibel < -45) return -1;
+ return 0;
+}
--- /dev/null
+#ifndef ANALYST_H
+#define ANALYST_H
+
+#include <stdint.h>
+
+struct fische;
+struct _fische__analyst_;
+struct fische__analyst;
+
+
+
+struct fische__analyst* fische__analyst_new (struct fische* parent);
+void fische__analyst_free (struct fische__analyst* self);
+
+int_fast8_t fische__analyst_analyse (struct fische__analyst* self, double* data, uint_fast16_t size);
+
+
+
+struct _fische__analyst_ {
+ uint_fast8_t state;
+ double moving_avg_30;
+ double moving_avg_03;
+ double std_dev;
+ double intensity_moving_avg;
+ double intensity_std_dev;
+ uint_fast32_t last_beat_frame;
+ uint_fast16_t* beat_gap_history;
+ uint_fast8_t bghist_head;
+
+ struct fische* fische;
+};
+
+struct fische__analyst {
+ double relative_energy;
+ double frames_per_beat;
+
+ struct _fische__analyst_* priv;
+};
+
+#endif
--- /dev/null
+#include "fische_internal.h"
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef DEBUG
+#include <stdio.h>
+#endif
+
+struct fische__audiobuffer*
+fische__audiobuffer_new (struct fische* parent) {
+
+ struct fische__audiobuffer* retval = malloc (sizeof (struct fische__audiobuffer));
+ retval->priv = malloc (sizeof (struct _fische__audiobuffer_));
+ struct _fische__audiobuffer_* P = retval->priv;
+
+ P->fische = parent;
+ P->buffer = 0;
+ P->buffer_size = 0;
+ P->format = parent->audio_format;
+ P->is_locked = 0;
+ P->puts = 0;
+ P->gets = 0;
+ P->last_get = 0;
+
+ retval->front_sample_count = 0;
+ retval->front_samples = 0;
+ retval->back_sample_count = 0;
+ retval->back_samples = 0;
+
+ return retval;
+}
+
+void
+fische__audiobuffer_free (struct fische__audiobuffer* self)
+{
+ if (!self)
+ return;
+
+ fische__audiobuffer_lock( self );
+
+ free (self->priv->buffer);
+ free (self->priv);
+ free (self);
+}
+
+void
+fische__audiobuffer_insert (struct fische__audiobuffer* self, const void* data, uint_fast32_t size)
+{
+ struct _fische__audiobuffer_* P = self->priv;
+
+ if (P->buffer_size > 44100)
+ return;
+
+ uint_fast8_t width = 1;
+
+ switch (P->format) {
+ case FISCHE_AUDIOFORMAT_DOUBLE:
+ width = 8;
+ break;
+ case FISCHE_AUDIOFORMAT_FLOAT:
+ case FISCHE_AUDIOFORMAT_S32:
+ case FISCHE_AUDIOFORMAT_U32:
+ width = 4;
+ break;
+ case FISCHE_AUDIOFORMAT_S16:
+ case FISCHE_AUDIOFORMAT_U16:
+ width = 2;
+ }
+
+ uint_fast32_t old_bufsize = P->buffer_size;
+ P->buffer_size += size / width;
+ P->buffer = realloc (P->buffer, P->buffer_size * sizeof (double));
+
+ uint_fast32_t i;
+ for (i = 0; i < size / width; ++ i) {
+ double* dest = (P->buffer + old_bufsize + i);
+
+ switch (P->format) {
+ case FISCHE_AUDIOFORMAT_FLOAT:
+ *dest = * ( (float*) data + i);
+ break;
+ case FISCHE_AUDIOFORMAT_DOUBLE:
+ *dest = * ( (double*) data + i);
+ break;
+ case FISCHE_AUDIOFORMAT_S32:
+ *dest = * ( (int32_t*) data + i);
+ *dest /= INT32_MAX;
+ break;
+ case FISCHE_AUDIOFORMAT_U32:
+ *dest = * ( (uint32_t*) data + i);
+ *dest -= INT32_MAX;
+ *dest /= INT32_MAX;
+ break;
+ case FISCHE_AUDIOFORMAT_S16:
+ *dest = * ( (int16_t*) data + i);
+ *dest /= INT16_MAX;
+ break;
+ case FISCHE_AUDIOFORMAT_U16:
+ *dest = * ( (uint16_t*) data + i);
+ *dest -= INT16_MAX;
+ *dest /= INT16_MAX;
+ break;
+ case FISCHE_AUDIOFORMAT_S8:
+ *dest = * ( (int8_t*) data + i);
+ *dest /= INT8_MAX;
+ break;
+ case FISCHE_AUDIOFORMAT_U8:
+ *dest = * ( (uint8_t*) data + i);
+ *dest /= INT8_MAX;
+ *dest /= INT8_MAX;
+ break;
+ }
+ }
+
+ ++ P->puts;
+}
+
+void
+fische__audiobuffer_get (struct fische__audiobuffer* self)
+{
+ struct _fische__audiobuffer_* P = self->priv;
+
+ if (P->buffer_size == 0)
+ return;
+
+ double* new_start = P->buffer + P->last_get * 2;
+ P->buffer_size -= P->last_get * 2;
+
+ // pop used data off front
+ memmove (P->buffer, new_start, P->buffer_size * sizeof (double));
+ P->buffer = realloc (P->buffer, P->buffer_size * sizeof (double));
+
+ if (!P->puts)
+ return;
+
+ // fallback for first get
+ if (P->gets == 0) {
+ P->gets = 3;
+ P->puts = 1;
+ }
+
+ // get/put ratio
+ double d_ratio = ( (double) P->gets ) / P->puts;
+ uint_fast8_t ratio = ceil( d_ratio );
+
+ // how many samples to return
+ uint_fast32_t n_samples = P->buffer_size / 2 / ratio;
+
+ // set return data size and remember
+ self->front_sample_count = n_samples;
+ self->back_sample_count = n_samples;
+ P->last_get = n_samples;
+
+ // set export buffer
+ self->front_samples = P->buffer;
+ self->back_samples = P->buffer + P->buffer_size - n_samples * 2;
+
+ // increment get counter
+ ++ P->gets;
+}
+
+void
+fische__audiobuffer_lock (struct fische__audiobuffer* self)
+{
+ #ifdef __GNUC__
+ while ( !__sync_bool_compare_and_swap( &self->priv->is_locked, 0, 1 ) )
+ usleep( 1 );
+ #else
+ while( self->priv->is_locked )
+ usleep( 1 );
+ self->priv->is_locked = 1;
+ #endif
+}
+
+void
+fische__audiobuffer_unlock (struct fische__audiobuffer* self)
+{
+ self->priv->is_locked = 0;
+}
--- /dev/null
+#ifndef AUDIOBUFFER_H
+#define AUDIOBUFFER_H
+
+#include <stdint.h>
+
+struct fische;
+struct _fische__audiobuffer_;
+struct fische__audiobuffer;
+
+
+
+struct fische__audiobuffer* fische__audiobuffer_new (struct fische* parent);
+void fische__audiobuffer_free (struct fische__audiobuffer* self);
+
+void fische__audiobuffer_insert (struct fische__audiobuffer* self, const void* data, uint_fast32_t size);
+void fische__audiobuffer_lock (struct fische__audiobuffer* self);
+void fische__audiobuffer_unlock (struct fische__audiobuffer* self);
+void fische__audiobuffer_get (struct fische__audiobuffer* self);
+
+
+
+struct _fische__audiobuffer_ {
+ double* buffer;
+ uint_fast32_t buffer_size;
+ uint_fast8_t format;
+ uint_fast8_t is_locked;
+ uint_fast32_t puts;
+ uint_fast32_t gets;
+ uint_fast32_t last_get;
+
+ struct fische* fische;
+};
+
+struct fische__audiobuffer {
+ double* front_samples;
+ uint_fast16_t front_sample_count;
+ double* back_samples;
+ uint_fast16_t back_sample_count;
+
+ struct _fische__audiobuffer_* priv;
+};
+
+#endif
--- /dev/null
+#include "fische_internal.h"
+
+#include <pthread.h>
+#include <unistd.h>
+
+#ifdef DEBUG
+#include <stdio.h>
+#endif
+
+void*
+blur_worker (void* arg)
+{
+ struct _fische__blurworker_* params = arg;
+
+ uint_fast16_t const width = params->width;
+ uint_fast16_t const width_x2 = 2 * width;
+ uint_fast16_t const y_start = params->y_start;
+ uint_fast16_t const y_end = params->y_end;
+
+ uint32_t source_component[4];
+
+ uint_fast16_t const two_lines = 2 * width;
+ uint_fast16_t const one_line = width;
+ uint_fast16_t const two_columns = 2;
+
+ uint_fast16_t x, y;
+ int_fast8_t vector_x, vector_y;
+
+ while (!params->kill) {
+
+ if (!params->work) {
+ usleep (1);
+ continue;
+ }
+
+ uint32_t* source = params->source;
+ uint32_t* source_pixel;
+
+ uint32_t* destination = params->destination;
+ uint32_t* destination_pixel = destination + y_start * width;
+
+ int8_t* vectors = (int8_t*) params->vectors;
+ int8_t* vector_pointer = vectors + y_start * width_x2;
+
+ // vertical loop
+ for (y = y_start; y < y_end; y ++) {
+ // horizontal loop
+ for (x = 0; x < width; x ++) {
+
+ // read the motion vector (actually its opposite)
+ vector_x = * (vector_pointer + 0);
+ vector_y = * (vector_pointer + 1);
+
+ // point to the pixel at [present + motion vector]
+ source_pixel = source + (y + vector_y) * width + x + vector_x;
+
+ // read the pixels at [source + (2,1)] [source + (-2,1)] [source + (0,-2)]
+ // shift them right by 2 and remove the bits that overflow each byte
+ source_component[0] = (* (source_pixel + one_line - two_columns) >> 2) & 0x3f3f3f3f;
+ source_component[1] = (* (source_pixel + one_line + two_columns) >> 2) & 0x3f3f3f3f;
+ source_component[2] = (* (source_pixel - two_lines) >> 2) & 0x3f3f3f3f;
+ source_component[3] = (* (source_pixel) >> 2) & 0x3f3f3f3f;
+
+ // add those four components and write to the destination
+ // increment destination pointer
+ * (destination_pixel ++) = source_component[0]
+ + source_component[1]
+ + source_component[2]
+ + source_component[3];
+
+ // increment vector source pointer
+ vector_pointer += 2;
+ }
+ }
+
+ // mark work as done
+ params->work = 0;
+ }
+
+ return 0;
+}
+
+struct fische__blurengine*
+fische__blurengine_new (struct fische* parent) {
+
+ struct fische__blurengine* retval = malloc (sizeof (struct fische__blurengine));
+ retval->priv = malloc (sizeof (struct _fische__blurengine_));
+ struct _fische__blurengine_* P = retval->priv;
+
+ P->fische = parent;
+ P->width = parent->width;
+ P->height = parent->height;
+ P->threads = parent->used_cpus;
+ P->sourcebuffer = FISCHE_PRIVATE(P)->screenbuffer->pixels;
+ P->destinationbuffer = malloc (P->width * P->height * sizeof (uint32_t));
+
+ uint_fast8_t i;
+ for (i = 0; i < P->threads; ++ i) {
+ P->worker[i].source = P->sourcebuffer;
+ P->worker[i].destination = P->destinationbuffer;
+ P->worker[i].vectors = 0;
+ P->worker[i].width = P->width;
+ P->worker[i].y_start = (i * P->height) / P->threads;
+ P->worker[i].y_end = ( (i + 1) * P->height) / P->threads;
+ P->worker[i].kill = 0;
+ P->worker[i].work = 0;
+
+ pthread_create (&P->worker[i].thread_id, NULL, blur_worker, &P->worker[i]);
+ }
+
+ return retval;
+}
+
+void
+fische__blurengine_free (struct fische__blurengine* self)
+{
+ if (!self)
+ return;
+
+ struct _fische__blurengine_* P = self->priv;
+
+ uint_fast8_t i;
+ for (i = 0; i < P->threads; ++ i) {
+ P->worker[i].kill = 1;
+ pthread_join (P->worker[i].thread_id, NULL);
+ }
+
+ free (self->priv->destinationbuffer);
+ free (self->priv);
+ free (self);
+}
+
+void
+fische__blurengine_blur (struct fische__blurengine* self, uint16_t* vectors)
+{
+ struct _fische__blurengine_* P = self->priv;
+ uint_fast8_t i;
+ for (i = 0; i < P->threads; ++ i) {
+ P->worker[i].source = P->sourcebuffer;
+ P->worker[i].destination = P->destinationbuffer;
+ P->worker[i].vectors = vectors;
+ P->worker[i].work = 1;
+ }
+}
+
+void
+fische__blurengine_swapbuffers (struct fische__blurengine* self)
+{
+ struct _fische__blurengine_* P = self->priv;
+
+ // wait for all workers to finish
+ uint_fast8_t work = 1;
+ while (work) {
+
+ work = 0;
+ uint_fast8_t i;
+ for (i = 0; i < P->threads; ++ i) {
+ work += P->worker[i].work;
+ }
+
+ if (work)
+ usleep (1);
+ }
+
+ uint32_t* t = P->destinationbuffer;
+ P->destinationbuffer = P->sourcebuffer;
+ P->sourcebuffer = t;
+ FISCHE_PRIVATE(P)->screenbuffer->pixels = t;
+}
--- /dev/null
+#ifndef BLURENGINE_H
+#define BLURENGINE_H
+
+#include <stdint.h>
+#include <pthread.h>
+
+struct fische;
+
+struct _fische__blurworker_;
+struct _fische__blurengine_;
+struct fische__blurengine;
+
+
+
+struct fische__blurengine* fische__blurengine_new (struct fische* parent);
+void fische__blurengine_free (struct fische__blurengine* self);
+
+void fische__blurengine_blur (struct fische__blurengine* self, uint16_t* vectors);
+void fische__blurengine_swapbuffers (struct fische__blurengine* self);
+
+
+
+struct _fische__blurworker_ {
+ pthread_t thread_id;
+ uint32_t* source;
+ uint32_t* destination;
+ uint_fast16_t width;
+ uint_fast16_t y_start;
+ uint_fast16_t y_end;
+ uint16_t* vectors;
+ uint_fast8_t work;
+ uint_fast8_t kill;
+};
+
+struct _fische__blurengine_ {
+ int_fast16_t width;
+ int_fast16_t height;
+ uint_fast8_t threads;
+ uint32_t* sourcebuffer;
+ uint32_t* destinationbuffer;
+
+ struct _fische__blurworker_ worker[8];
+
+ struct fische* fische;
+};
+
+struct fische__blurengine {
+ struct _fische__blurengine_* priv;
+};
+
+#endif
--- /dev/null
+#include "fische_internal.h"
+
+#include <string.h>
+
+#if defined(__x86_64__)
+
+void
+_fische__host_get_cpuid_ (uint32_t t, uint32_t* eax, uint32_t* ebx, uint32_t* ecx, uint32_t* edx)
+{
+ asm ("pushq %%rbx\n\t"
+ "movl %%esi, %%ebx\n\t"
+ "cpuid\n\t"
+ "movl %%ebx, %%esi\n\t"
+ "popq %%rbx"
+ : "=a" (*eax)
+ , "=S" (*ebx)
+ , "=c" (*ecx)
+ , "=d" (*edx)
+ : "a" (t)
+ , "c" (0)
+ , "S" (0)
+ , "d" (0));
+}
+
+#elif defined(__i386__)
+
+void
+_fische__host_get_cpuid_ (uint32_t t, uint32_t* eax, uint32_t* ebx, uint32_t* ecx, uint32_t* edx)
+{
+ asm ("pushl %%ebx\n\t"
+ "movl %%esi, %%ebx\n\t"
+ "cpuid\n\t"
+ "movl %%ebx, %%esi\n\t"
+ "popl %%ebx"
+ : "=a" (*eax)
+ , "=S" (*ebx)
+ , "=c" (*ecx)
+ , "=d" (*edx)
+ : "a" (t)
+ , "c" (0)
+ , "S" (0)
+ , "d" (0));
+}
+
+#endif
+
+uint_fast8_t
+_fische__cpu_detect_()
+{
+
+#if defined(__i386__) || defined(__x86_64__)
+ int n = 1;
+ uint32_t eax, ebx, ecx, edx;
+ _fische__host_get_cpuid_ (0x0, &eax, &ebx, &ecx, &edx);
+
+ char cpuid[13];
+ memcpy (cpuid, &ebx, 4);
+ memcpy (cpuid + 4, &edx, 4);
+ memcpy (cpuid + 8, &ecx, 4);
+ cpuid[12] = '\0';
+
+ if (!strcmp (cpuid, "AuthenticAMD")) {
+ _fische__host_get_cpuid_ (0x80000000, &eax, &ebx, &ecx, &edx);
+ if (eax < 0x80000008) return 1;
+ _fische__host_get_cpuid_ (0x80000008, &eax, &ebx, &ecx, &edx);
+ n = (ecx & 0xff) + 1;
+
+ } else { /* if it's not AMD, it's most likely Intel */
+ _fische__host_get_cpuid_ (0x00000000, &eax, &ebx, &ecx, &edx);
+ if (eax < 4) return 1;
+ _fische__host_get_cpuid_ (0x4, &eax, &ebx, &ecx, &edx);
+ n = (eax >> 26) + 1;
+ }
+
+ return n;
+
+#else /* don't have cpuid */
+ return 1;
+#endif
+}
--- /dev/null
+#include "fische_internal.h"
+
+#include <string.h>
+#include <pthread.h>
+#include <unistd.h>
+
+#ifdef DEBUG
+#include <stdio.h>
+#endif
+
+void*
+create_vectors (void* arg)
+{
+ struct fische* F = arg;
+ struct _fische__internal_ * P = F->priv;
+ P->vectorfield = fische__vectorfield_new (F,
+ &P->init_progress,
+ &P->init_cancel);
+ return 0;
+}
+
+void*
+indicate_busy (void* arg)
+{
+ struct fische* F = arg;
+ struct _fische__internal_ * P = F->priv;
+ struct fische__screenbuffer* sbuf = P->screenbuffer;
+
+ fische__point center;
+ center.x = sbuf->priv->width / 2;
+ center.y = sbuf->priv->height / 2;
+ double dim = (center.x > center.y) ? center.y / 2 : center.x / 2;
+
+ double last = -1;
+
+ while ( (P->init_progress < 1) && (!P->init_cancel)) {
+
+ if ( (P->init_progress < 0) || (P->init_progress == last)) {
+ usleep (10000);
+ continue;
+ }
+
+ last = P->init_progress;
+ double angle = P->init_progress * -2 * 3.1415 + 3.0415;
+
+ fische__vector c1;
+ c1.x = sin (angle) * dim;
+ c1.y = cos (angle) * dim;
+
+ fische__vector c2;
+ c2.x = sin (angle + 0.1) * dim;
+ c2.y = cos (angle + 0.1) * dim;
+
+ fische__vector e1 = fische__vector_single (&c1);
+ fische__vector_mul (&e1, dim / 2);
+ fische__vector e2 = fische__vector_single (&c2);
+ fische__vector_mul (&e2, dim / 2);
+
+ fische__vector c3 = c2;
+ fische__vector_sub (&c3, &e2);
+ fische__vector c4 = c1;
+ fische__vector_sub (&c4, &e1);
+
+ fische__vector_mul (&c1, F->scale);
+ fische__vector_mul (&c2, F->scale);
+ fische__vector_mul (&c3, F->scale);
+ fische__vector_mul (&c4, F->scale);
+
+ fische__vector_add (&c1, ¢er);
+ fische__vector_add (&c2, ¢er);
+ fische__vector_add (&c3, ¢er);
+ fische__vector_add (&c4, ¢er);
+
+ fische__screenbuffer_lock (sbuf);
+ fische__screenbuffer_line (sbuf, c1.x, c1.y, c2.x, c2.y, 0xffffffff);
+ fische__screenbuffer_line (sbuf, c2.x, c2.y, c3.x, c3.y, 0xffffffff);
+ fische__screenbuffer_line (sbuf, c3.x, c3.y, c4.x, c4.y, 0xffffffff);
+ fische__screenbuffer_line (sbuf, c4.x, c4.y, c1.x, c1.y, 0xffffffff);
+ fische__screenbuffer_unlock (sbuf);
+ }
+
+ return 0;
+}
+
+struct fische *
+fische_new() {
+ struct fische* retval = malloc (sizeof (struct fische));
+
+ retval->used_cpus = _fische__cpu_detect_();
+ if (retval->used_cpus > 8)
+ retval->used_cpus = 8;
+
+ retval->frame_counter = 0;
+ retval->audio_format = FISCHE_AUDIOFORMAT_FLOAT;
+ retval->pixel_format = FISCHE_PIXELFORMAT_0xAABBGGRR;
+ retval->width = 512;
+ retval->height = 256;
+ retval->read_vectors = 0;
+ retval->write_vectors = 0;
+ retval->on_beat = 0;
+ retval->nervous_mode = 0;
+ retval->blur_mode = FISCHE_BLUR_SLICK;
+ retval->line_style = FISCHE_LINESTYLE_ALPHA_SIMULATION;
+ retval->scale = 1;
+ retval->amplification = 0;
+ retval->priv = 0;
+ retval->error_text = "no error";
+
+ return retval;
+}
+
+int
+fische_start (struct fische* handle)
+{
+ // plausibility checks
+ if ( (handle->used_cpus > 8) || (handle->used_cpus < 1)) {
+ handle->error_text = "CPU count out of range (1 <= used_cpus <= 8)";
+ return 1;
+ }
+
+ if (handle->audio_format >= _FISCHE__AUDIOFORMAT_LAST_) {
+ handle->error_text = "audio format invalid";
+ return 1;
+ }
+
+ if (handle->line_style >= _FISCHE__LINESTYLE_LAST_) {
+ handle->error_text = "line style invalid";
+ return 1;
+ }
+
+ if (handle->frame_counter != 0) {
+ handle->error_text = "frame counter garbled";
+ return 1;
+ }
+
+ if ( (handle->amplification < -10) || (handle->amplification > 10)) {
+ handle->error_text = "amplification value out of range (-10 <= amplification <= 10)";
+ return 1;
+ }
+
+ if ( (handle->height < 16) || (handle->height > 2048)) {
+ handle->error_text = "height value out of range (16 <= height <= 2048)";
+ return 1;
+ }
+
+ if ( (handle->width < 16) || (handle->width > 2048)) {
+ handle->error_text = "width value out of range (16 <= width <= 2048)";
+ return 1;
+ }
+
+ if (handle->width % 4 != 0) {
+ handle->error_text = "width value invalid (must be a multiple of four)";
+ return 1;
+ }
+
+ if (handle->pixel_format >= _FISCHE__PIXELFORMAT_LAST_) {
+ handle->error_text = "pixel format invalid";
+ return 1;
+ }
+
+ if ( (handle->scale < 0.5) || (handle->scale > 2)) {
+ handle->error_text = "scale value out of range (0.5 <= scale <= 2.0)";
+ return 1;
+ }
+
+ if (handle->blur_mode >= _FISCHE__BLUR_LAST_) {
+ handle->error_text = "blur option invalid";
+ return 1;
+ }
+
+ // initialize private struct
+ handle->priv = malloc (sizeof (struct _fische__internal_));
+ memset (handle->priv, '\0', sizeof (struct _fische__internal_));
+ struct _fische__internal_* P = handle->priv;
+
+ P->init_progress = -1;
+
+ P->analyst = fische__analyst_new (handle);
+ P->screenbuffer = fische__screenbuffer_new (handle);
+ P->wavepainter = fische__wavepainter_new (handle);
+ P->blurengine = fische__blurengine_new (handle);
+ P->audiobuffer = fische__audiobuffer_new (handle);
+
+ // start vector creation and busy indicator threads
+ pthread_t vector_thread;
+ pthread_create (&vector_thread, NULL, create_vectors, handle);
+ pthread_detach (vector_thread);
+
+ pthread_t busy_thread;
+ pthread_create (&busy_thread, NULL, indicate_busy, handle);
+ pthread_detach (busy_thread);
+
+ return 0;
+}
+
+uint32_t*
+fische_render (struct fische* handle)
+{
+ struct _fische__internal_* P = handle->priv;
+
+ // only if init completed
+ if (P->init_progress >= 1) {
+
+ // analyse sound data
+ fische__audiobuffer_lock (P->audiobuffer);
+ fische__audiobuffer_get (P->audiobuffer);
+ int_fast8_t analysis = fische__analyst_analyse (P->analyst, P->audiobuffer->back_samples, P->audiobuffer->back_sample_count);
+
+ // act accordingly
+ if (handle->nervous_mode) {
+ if (analysis >= 2)
+ fische__wavepainter_change_shape (P->wavepainter);
+ if (analysis >= 1)
+ fische__vectorfield_change (P->vectorfield);
+ } else {
+ if (analysis >= 1)
+ fische__wavepainter_change_shape (P->wavepainter);
+ if (analysis >= 2)
+ fische__vectorfield_change (P->vectorfield);
+ }
+
+ if (analysis >= 3) {
+ fische__wavepainter_beat (P->wavepainter, P->analyst->frames_per_beat);
+ }
+ if (analysis >= 4) {
+ if (handle->on_beat)
+ handle->on_beat (P->analyst->frames_per_beat);
+ }
+
+ P->audio_valid = analysis >= 0 ? 1 : 0;
+
+ fische__wavepainter_change_color (P->wavepainter, P->analyst->frames_per_beat, P->analyst->relative_energy);
+
+
+ // wait for blurring to be finished
+ // and swap buffers
+ fische__screenbuffer_lock (P->screenbuffer);
+ fische__blurengine_swapbuffers (P->blurengine);
+ fische__screenbuffer_unlock (P->screenbuffer);
+
+ // draw waves
+ if (P->audio_valid)
+ fische__wavepainter_paint (P->wavepainter, P->audiobuffer->front_samples, P->audiobuffer->front_sample_count);
+
+ // start blurring for the next frame
+ fische__blurengine_blur (P->blurengine, P->vectorfield->field);
+
+ fische__audiobuffer_unlock (P->audiobuffer);
+ }
+
+ handle->frame_counter ++;
+
+ return P->screenbuffer->pixels;
+}
+
+void
+fische_free (struct fische* handle)
+{
+ if (!handle)
+ return;
+
+ struct _fische__internal_* P = handle->priv;
+
+ if (handle->priv) {
+ // tell init threads to quit
+ P->init_cancel = 1;
+
+ // wait for init threads to quit
+ while (P->init_progress < 1)
+ usleep (10);
+
+ fische__audiobuffer_free (P->audiobuffer);
+ fische__blurengine_free (P->blurengine);
+ fische__vectorfield_free (P->vectorfield);
+ fische__wavepainter_free (P->wavepainter);
+ fische__screenbuffer_free (P->screenbuffer);
+ fische__analyst_free (P->analyst);
+
+ free (handle->priv);
+ }
+
+ free (handle);
+}
+
+void
+fische_audiodata (struct fische* handle, const void* data, size_t data_size)
+{
+ struct _fische__internal_* P = handle->priv;
+
+ if (NULL == P->audiobuffer)
+ return;
+
+ fische__audiobuffer_lock (P->audiobuffer);
+ fische__audiobuffer_insert (P->audiobuffer, data, data_size);
+ fische__audiobuffer_unlock (P->audiobuffer);
+}
--- /dev/null
+#ifndef FISCHE_H
+#define FISCHE_H
+
+/* int types */
+#include <stdint.h>
+/* size_t */
+#include <stdlib.h>
+
+typedef struct fische {
+
+ /* 16 <= width <= 2048
+ * DEFAULT: 512
+ * constant after fische_start() */
+ uint16_t width;
+
+ /* 16 <= height <= 2048
+ * DEFAULT: 256
+ * constant after fische_start() */
+ uint16_t height;
+
+ /* 1 <= used_cpus <= 8
+ * DEFAULT: all available (autodetect)
+ * constant after fische_start() */
+ uint8_t used_cpus;
+
+ /* true (!=0) or false (0)
+ * DEFAULT: 0 */
+ uint8_t nervous_mode;
+
+ /* see below (audio format enum)
+ * DEFAULT: FISCHE_AUDIOFORMAT_FLOAT
+ * constant after fische_start() */
+ uint8_t audio_format;
+
+ /* see below (pixel format enum)
+ * DEFAULT: FISCHE_PIXELFORMAT_0xAABBGGRR
+ * constant after fische_start() */
+ uint8_t pixel_format;
+
+ /* see below (blur mode enum)
+ * DEFAULT: FISCHE_BLUR_SLICK
+ * constant after fische_start() */
+ uint8_t blur_mode;
+
+ /* see below (line style enum)
+ * DEFAULT: FISCHE_LINESTYLE_ALPHA_SIMULATION */
+ uint8_t line_style;
+
+ /* 0.5 <= scale <= 2.0
+ * DEFAULT: 1.0
+ * constant after fische_start() */
+ double scale;
+
+ /* -10 <= amplification <= 10
+ * DEFAULT: 0 */
+ double amplification;
+
+ /* if non-NULL,
+ * fische calls this to read vector fields from an external source
+ * takes a void** for data placement
+ * returns the number of bytes read */
+ size_t (*read_vectors) (void**);
+
+ /* if non-NULL,
+ * fische calls this to write vector field data to an external sink
+ * takes a void* and the number of bytes to be written */
+ void (*write_vectors) (const void*, size_t);
+
+ /* if non-NULL,
+ * fische calls this on major beats that are not handled internally
+ * takes frames per beat */
+ void (*on_beat) (double);
+
+ /* read only */
+ uint32_t frame_counter;
+
+ /* read only */
+ char* error_text;
+
+ void* priv;
+
+} FISCHE;
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+ /* creates a new FISCHE object
+ * and initialzes it with default values */
+ FISCHE* fische_new();
+
+ /* starts FISCHE */
+ int fische_start (FISCHE* handle);
+
+ /* makes the next frame available */
+ uint32_t* fische_render (FISCHE* handle);
+
+ /* destructs the FISCHE object */
+ void fische_free (FISCHE* handle);
+
+ /* inserts audio data */
+ void fische_audiodata (FISCHE* handle, const void* data, size_t data_size);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+
+/* audio sample formats */
+enum {FISCHE_AUDIOFORMAT_U8,
+ FISCHE_AUDIOFORMAT_S8,
+ FISCHE_AUDIOFORMAT_U16,
+ FISCHE_AUDIOFORMAT_S16,
+ FISCHE_AUDIOFORMAT_U32,
+ FISCHE_AUDIOFORMAT_S32,
+ FISCHE_AUDIOFORMAT_FLOAT,
+ FISCHE_AUDIOFORMAT_DOUBLE,
+ _FISCHE__AUDIOFORMAT_LAST_
+ };
+
+/* pixel formats */
+enum {FISCHE_PIXELFORMAT_0xRRGGBBAA,
+ FISCHE_PIXELFORMAT_0xAABBGGRR,
+ FISCHE_PIXELFORMAT_0xAARRGGBB,
+ FISCHE_PIXELFORMAT_0xBBGGRRAA,
+ _FISCHE__PIXELFORMAT_LAST_
+ };
+
+/* blur style */
+enum {FISCHE_BLUR_SLICK,
+ FISCHE_BLUR_FUZZY,
+ _FISCHE__BLUR_LAST_
+ };
+
+/* line style */
+enum {FISCHE_LINESTYLE_THIN,
+ FISCHE_LINESTYLE_THICK,
+ FISCHE_LINESTYLE_ALPHA_SIMULATION,
+ _FISCHE__LINESTYLE_LAST_
+ };
+
+#endif
--- /dev/null
+#ifndef FISCHE_INTERNAL_H
+#define FISCHE_INTERNAL_H
+
+#include "fische.h"
+#include "wavepainter.h"
+#include "screenbuffer.h"
+#include "analyst.h"
+#include "vector.h"
+#include "vectorfield.h"
+#include "blurengine.h"
+#include "audiobuffer.h"
+
+#ifdef WIN32
+#define rand_r(_seed) (_seed == _seed ? rand() : rand())
+#endif
+
+#define FISCHE_PRIVATE(P) ((struct _fische__internal_*) P->fische->priv)
+
+uint_fast8_t _fische__cpu_detect_();
+
+
+struct _fische__internal_ {
+ struct fische__screenbuffer* screenbuffer;
+ struct fische__wavepainter* wavepainter;
+ struct fische__analyst* analyst;
+ struct fische__blurengine* blurengine;
+ struct fische__vectorfield* vectorfield;
+ struct fische__audiobuffer* audiobuffer;
+ double init_progress;
+ uint_fast8_t init_cancel;
+ uint_fast8_t audio_valid;
+};
+
+#endif
--- /dev/null
+/* fishBMC visualization plugin
+ * Copyright (C) 2012 Marcel Ebmer
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#ifdef __GNUC__
+#define __cdecl
+#endif
+
+#include "addons/include/xbmc_vis_types.h"
+#include "addons/include/xbmc_vis_dll.h"
+
+#include "fische.h"
+
+#include <cmath>
+#include <cstring>
+#include <unistd.h>
+#include <iostream>
+#include <string>
+#include <fstream>
+#include <sstream>
+#include <sys/stat.h>
+
+
+// global variables
+FISCHE* g_fische;
+double g_aspect;
+bool g_isrotating;
+double g_angle;
+double g_lastangle;
+bool g_errorstate;
+int g_framedivisor;
+double g_angleincrement;
+double g_texright;
+double g_texleft;
+bool g_filemode;
+int g_size;
+uint8_t* g_axis = 0;
+
+
+
+
+
+// render functions
+// must be included AFTER global variables
+#if defined(_WIN32)
+#include "fishbmc_directx.hpp"
+#else // _WIN32
+#include "fishbmc_opengl.hpp"
+#endif // _WIN32
+
+
+
+
+
+void on_beat (double frames_per_beat)
+{
+ if (!g_isrotating) {
+ g_isrotating = true;
+ if (frames_per_beat < 1) frames_per_beat = 12;
+ g_angleincrement = 180 / 4 / frames_per_beat;
+ }
+}
+
+void write_vectors (const void* data, size_t bytes)
+{
+ char const * homedir = getenv ("HOME");
+ if (!homedir)
+ return;
+
+ std::string dirname = std::string (homedir) + "/.fishBMC-data";
+ mkdir (dirname.c_str(), 0755);
+
+ std::ostringstream filename;
+ filename << dirname << "/" << g_fische->height;
+
+ // open the file
+ std::fstream vectorsfile (filename.str().c_str(), std::fstream::out | std::fstream::binary);
+ if (!vectorsfile.good())
+ return;
+
+ // write it
+ vectorsfile.write (reinterpret_cast<const char*> (data), bytes);
+ vectorsfile.close();
+}
+
+size_t read_vectors (void** data)
+{
+ char const * homedir = getenv ("HOME");
+ if (!homedir)
+ return 0;
+
+ std::string dirname = std::string (homedir) + "/.fishBMC-data";
+ mkdir (dirname.c_str(), 0755);
+
+ std::ostringstream filename;
+ filename << dirname << "/" << g_fische->height;
+
+ // open the file
+ std::fstream vectorsfile (filename.str().c_str(), std::fstream::in);
+ if (!vectorsfile.good())
+ return 0;
+
+ vectorsfile.seekg (0, std::ios::end);
+ size_t n = vectorsfile.tellg();
+ vectorsfile.seekg (0, std::ios::beg);
+
+ *data = malloc (n);
+ vectorsfile.read (reinterpret_cast<char*> (*data), n);
+ vectorsfile.close();
+
+ return n;
+}
+
+void delete_vectors()
+{
+ char const * homedir = getenv ("HOME");
+ if (!homedir)
+ return;
+
+ std::string dirname = std::string (homedir) + "/.fishBMC-data";
+ mkdir (dirname.c_str(), 0755);
+
+ for (int i = 64; i <= 2048; i *= 2) {
+ std::ostringstream filename;
+ filename << dirname << "/" << i;
+ unlink (filename.str().c_str());
+ }
+}
+
+extern "C" ADDON_STATUS ADDON_Create (void* hdl, void* props)
+{
+ if (!props)
+ return ADDON_STATUS_UNKNOWN;
+
+ VIS_PROPS* visProps = (VIS_PROPS*) props;
+
+ init (visProps);
+
+ g_fische = fische_new();
+ g_fische->on_beat = &on_beat;
+ g_fische->pixel_format = FISCHE_PIXELFORMAT_0xAABBGGRR;
+ g_fische->line_style = FISCHE_LINESTYLE_THICK;
+ g_aspect = double (visProps->width) / double (visProps->height);
+ g_texleft = (2 - g_aspect) / 4;
+ g_texright = 1 - g_texleft;
+ g_framedivisor = 1;
+ g_filemode = false;
+ g_size = 128;
+
+ return ADDON_STATUS_NEED_SETTINGS;
+}
+
+extern "C" void Start (int, int, int, const char*)
+{
+ g_errorstate = false;
+
+ g_fische->audio_format = FISCHE_AUDIOFORMAT_FLOAT;
+
+ g_fische->height = g_size;
+ g_fische->width = 2 * g_size;
+
+ if (g_filemode) {
+ g_fische->read_vectors = &read_vectors;
+ g_fische->write_vectors = &write_vectors;
+ }
+
+ else {
+ delete_vectors();
+ }
+
+ if (fische_start (g_fische) != 0) {
+ std::cerr << "fische failed to start" << std::endl;
+ g_errorstate = true;
+ return;
+ }
+
+ uint32_t* pixels = fische_render (g_fische);
+
+ init_texture (g_fische->width, g_fische->height, pixels);
+
+ g_isrotating = false;
+ g_angle = 0;
+ g_lastangle = 0;
+ g_angleincrement = 0;
+}
+
+extern "C" void AudioData (const float* pAudioData, int iAudioDataLength, float*, int)
+{
+ fische_audiodata (g_fische, pAudioData, iAudioDataLength * 4);
+}
+
+extern "C" void Render()
+{
+ static int frame = 0;
+
+ // check if this frame is to be skipped
+ if (++ frame % g_framedivisor == 0) {
+ uint32_t* pixels = fische_render (g_fische);
+ replace_texture (g_fische->width, g_fische->height, pixels);
+ if (g_isrotating)
+ g_angle += g_angleincrement;
+ }
+
+ // stop rotation if required
+ if (g_isrotating) {
+ if (g_angle - g_lastangle > 180) {
+ g_lastangle = g_lastangle ? 0 : 180;
+ g_angle = g_lastangle;
+ g_isrotating = false;
+ }
+ }
+
+ // how many quads will there be?
+ int n_Y = 8;
+ int n_X = (g_aspect * 8 + 0.5);
+
+ // one-time initialization of rotation axis array
+ if (!g_axis) {
+ g_axis = new uint8_t[n_X * n_Y];
+ for (int i = 0; i < n_X * n_Y; ++ i) {
+ g_axis[i] = rand() % 2;
+ }
+ }
+
+ start_render();
+
+ // loop over and draw all quads
+ int quad_count = 0;
+ double quad_width = 4.0 / n_X;
+ double quad_height = 4.0 / n_Y;
+ double tex_width = (g_texright - g_texleft);
+
+ for (double X = 0; X < n_X; X += 1) {
+ for (double Y = 0; Y < n_Y; Y += 1) {
+ double center_x = -2 + (X + 0.5) * 4 / n_X;
+ double center_y = -2 + (Y + 0.5) * 4 / n_Y;
+ double tex_left = g_texleft + tex_width * X / n_X;
+ double tex_right = g_texleft + tex_width * (X + 1) / n_X;
+ double tex_top = Y / n_Y;
+ double tex_bottom = (Y + 1) / n_Y;
+ double angle = (g_angle - g_lastangle) * 4 - (X + Y * n_X) / (n_X * n_Y) * 360;
+ if (angle < 0) angle = 0;
+ if (angle > 360) angle = 360;
+
+ textured_quad (center_x,
+ center_y,
+ angle,
+ g_axis[quad_count ++],
+ quad_width,
+ quad_height,
+ tex_left,
+ tex_right,
+ tex_top,
+ tex_bottom);
+ }
+ }
+
+ finish_render();
+}
+
+extern "C" void GetInfo (VIS_INFO* pInfo)
+{
+ // std::cerr << "fishBMC::GetInfo" << std::endl;
+ pInfo->bWantsFreq = false;
+ pInfo->iSyncDelay = 0;
+}
+
+extern "C" bool OnAction (long flags, const void *param)
+{
+ return false;
+}
+
+extern "C" unsigned int GetPresets (char ***presets)
+{
+ return 0;
+}
+
+extern "C" unsigned GetPreset()
+{
+ return 0;
+}
+
+extern "C" bool IsLocked()
+{
+ return false;
+}
+
+extern "C" unsigned int GetSubModules (char ***names)
+{
+ return 0;
+}
+
+extern "C" void ADDON_Stop()
+{
+ fische_free (g_fische);
+ g_fische = 0;
+ delete_texture();
+ delete [] g_axis;
+ g_axis = 0;
+}
+
+extern "C" void ADDON_Destroy()
+{
+ return;
+}
+
+extern "C" bool ADDON_HasSettings()
+{
+ return false;
+}
+
+extern "C" ADDON_STATUS ADDON_GetStatus()
+{
+ if (g_errorstate)
+ return ADDON_STATUS_UNKNOWN;
+
+ return ADDON_STATUS_OK;
+}
+
+extern "C" unsigned int ADDON_GetSettings (ADDON_StructSetting ***sSet)
+{
+ return 0;
+}
+
+extern "C" void ADDON_FreeSettings()
+{
+}
+
+extern "C" ADDON_STATUS ADDON_SetSetting (const char *strSetting, const void* value)
+{
+ if (!strSetting || !value)
+ return ADDON_STATUS_UNKNOWN;
+
+ if (!strncmp (strSetting, "nervous", 7)) {
+ bool nervous = * ( (bool*) value);
+ g_fische->nervous_mode = nervous ? 1 : 0;
+ }
+
+ else if (!strncmp (strSetting, "filemode", 7)) {
+ bool filemode = * ( (bool*) value);
+ g_filemode = filemode;
+ }
+
+ else if (!strncmp (strSetting, "detail", 6)) {
+ int detail = * ( (int*) value);
+ g_size = 128;
+ while (detail--) {
+ g_size *= 2;
+ }
+ }
+
+ else if (!strncmp (strSetting, "divisor", 7)) {
+ int divisor = * ( (int*) value);
+ g_framedivisor = 8;
+ while (divisor--) {
+ g_framedivisor /= 2;
+ }
+ }
+
+ return ADDON_STATUS_OK;
+}
--- /dev/null
+// TEMPLATE for DirectX implementation
+// check out fishbmc_opengl.hpp
+// contact marcel@26elf.at for questions
+// thanks for porting!
+
+#include <any headers you may need>
+
+// declare a global texture
+// declare other globals you may need
+
+void init (VIS_PROPS* vis_props)
+{
+ // take anything from the VIS_PROPS struct
+}
+
+inline void init_texture (int width, int height, uint32_t* pixels)
+{
+ // initialize the global texture of size (width x height) with data from pixels
+}
+
+inline void replace_texture (int width, int height, uint32_t* pixels)
+{
+ // replace the texture data with pixels
+}
+
+inline void delete_texture()
+{
+ // free memory held by global texture
+}
+
+void textured_quad (double center_x,
+ double center_y,
+ double angle,
+ double axis,
+ double width,
+ double height,
+ double tex_left,
+ double tex_right,
+ double tex_top,
+ double tex_bottom)
+{
+ // draw a textured quad, sized (width x height), centered at (center_x, center_y),
+ // rotated by angle (this is in degrees) around an axis defined as (axis, 1 - axis, 0),
+ // scaled by (1 - sin (angle / 360 * M_PI) / 3)
+ // texture coordinates are given for all corners
+}
+
+inline void start_render()
+{
+ // save state
+ // enable blending
+ // disable depth testing
+ // paint both sides of polygons
+
+ // setup coordinate system:
+ // screen top left: (-1, -1)
+ // screen bottom right: (1, 1)
+ // screen distance from eye: 3
+ // screen depth clipping: 3 to 15
+
+ // bind global texture
+ // move 6 units into the screen
+ // rotate by global variable g_angle, around axis (0, 1, 0)
+}
+
+inline void finish_render()
+{
+ // restore original state
+}
--- /dev/null
+#if defined(__APPLE__)
+#include <OpenGL/gl.h>
+#include <OpenGL/glu.h>
+#else // __APPLE__
+#include <GL/gl.h>
+#include <GL/glu.h>
+#endif // __APPLE__
+
+
+// additional global variable
+GLuint g_texture;
+
+
+// OpenGL: ignores VIS_PROPS
+void init (VIS_PROPS*) {}
+
+// OpenGL: texture initialization
+inline void init_texture (int width, int height, uint32_t* pixels)
+{
+ // generate a texture for drawing into
+ glEnable (GL_TEXTURE_2D);
+ glGenTextures (1, &g_texture);
+ glBindTexture (GL_TEXTURE_2D, g_texture);
+ glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+}
+
+// OpenGL: replace texture
+inline void replace_texture (int width, int height, uint32_t* pixels)
+{
+ glBindTexture (GL_TEXTURE_2D, g_texture);
+ glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+}
+
+// OpenGL: delete texture
+inline void delete_texture()
+{
+ glDeleteTextures (1, &g_texture);
+}
+
+// OpenGL: paint a textured quad
+void textured_quad (double center_x,
+ double center_y,
+ double angle,
+ double axis,
+ double width,
+ double height,
+ double tex_left,
+ double tex_right,
+ double tex_top,
+ double tex_bottom)
+{
+ glPushMatrix();
+
+ glTranslatef (center_x, center_y, 0);
+ glRotatef (angle, axis, 1 - axis, 0);
+
+ double scale = 1 - sin (angle / 360 * M_PI) / 3;
+ glScalef (scale, scale, scale);
+
+ glBegin (GL_QUADS);
+
+ glTexCoord2d (tex_left, tex_top);
+ glVertex3d (- width / 2, - height / 2, 0);
+
+ glTexCoord2d (tex_right, tex_top);
+ glVertex3d (width / 2, - height / 2, 0);
+
+ glTexCoord2d (tex_right, tex_bottom);
+ glVertex3d (width / 2, height / 2, 0);
+
+ glTexCoord2d (tex_left, tex_bottom);
+ glVertex3d (- width / 2, height / 2, 0);
+
+ glEnd();
+
+ glPopMatrix();
+}
+
+// OpenGL: setup to start rendering
+inline void start_render()
+{
+ // save state
+ glPushAttrib (GL_ENABLE_BIT | GL_TEXTURE_BIT);
+
+ // enable blending
+ glEnable (GL_BLEND);
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ // enable texturing
+ glEnable (GL_TEXTURE_2D);
+
+ // disable depth testing
+ glDisable (GL_DEPTH_TEST);
+
+ // paint both sides of polygons
+ glPolygonMode (GL_FRONT, GL_FILL);
+
+ // OpenGL projection matrix setup
+ glMatrixMode (GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+
+ // coordinate system:
+ // screen top left: (-1, -1)
+ // screen bottom right: (1, 1)
+ // screen depth clipping: 3 to 15
+ glFrustum (-1, 1, 1, -1, 3, 15);
+
+ glMatrixMode (GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ // bind global texture
+ glBindTexture (GL_TEXTURE_2D, g_texture);
+
+ // move 6 units into the screen
+ glTranslatef (0, 0, -6.0f);
+
+ // rotate
+ glRotatef (g_angle, 0, 1, 0);
+}
+
+// OpenGL: done rendering
+inline void finish_render()
+{
+ // return OpenGL matrices to original state
+ glPopMatrix();
+ glMatrixMode (GL_PROJECTION);
+ glPopMatrix();
+
+ // restore OpenGl original state
+ glPopAttrib();
+}
--- /dev/null
+#include "fische_internal.h"
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+struct fische__screenbuffer*
+fische__screenbuffer_new (struct fische* parent) {
+ struct fische__screenbuffer* retval = malloc (sizeof (struct fische__screenbuffer));
+ retval->priv = malloc (sizeof (struct _fische__screenbuffer_));
+ struct _fische__screenbuffer_* P = retval->priv;
+
+ P->fische = parent;
+ P->width = parent->width;
+ P->height = parent->height;
+ P->is_locked = 0;
+
+ retval->pixels = malloc (P->width * P->height * sizeof (uint32_t));
+ memset (retval->pixels, '\0', P->width * P->height * sizeof (uint32_t));
+
+ switch (parent->pixel_format) {
+
+ case FISCHE_PIXELFORMAT_0xAABBGGRR:
+ P->alpha_shift = 24;
+ P->blue_shift = 16;
+ P->green_shift = 8;
+ P->red_shift = 0;
+ break;
+
+ case FISCHE_PIXELFORMAT_0xAARRGGBB:
+ P->alpha_shift = 24;
+ P->blue_shift = 0;
+ P->green_shift = 8;
+ P->red_shift = 16;
+ break;
+
+ case FISCHE_PIXELFORMAT_0xBBGGRRAA:
+ P->alpha_shift = 0;
+ P->blue_shift = 24;
+ P->green_shift = 16;
+ P->red_shift = 8;
+ break;
+
+ case FISCHE_PIXELFORMAT_0xRRGGBBAA:
+ P->alpha_shift = 0;
+ P->blue_shift = 8;
+ P->green_shift = 16;
+ P->red_shift = 24;
+ break;
+ }
+
+ return retval;
+}
+
+void
+fische__screenbuffer_free (struct fische__screenbuffer* self)
+{
+ if (!self)
+ return;
+
+ fische__screenbuffer_lock( self );
+
+ free (self->priv);
+ free (self->pixels);
+ free (self);
+}
+
+void
+fische__screenbuffer_lock (struct fische__screenbuffer* self)
+{
+ #ifdef __GNUC__
+ while ( !__sync_bool_compare_and_swap( &self->priv->is_locked, 0, 1 ) )
+ usleep( 1 );
+ #else
+ while( self->priv->is_locked )
+ usleep( 1 );
+ self->priv->is_locked = 1;
+ #endif
+}
+
+void
+fische__screenbuffer_unlock (struct fische__screenbuffer* self)
+{
+ self->priv->is_locked = 0;
+}
+
+void
+fische__screenbuffer_line (struct fische__screenbuffer* self,
+ int_fast16_t x1,
+ int_fast16_t y1,
+ int_fast16_t x2,
+ int_fast16_t y2,
+ uint32_t color)
+{
+ struct _fische__screenbuffer_* P = self->priv;
+
+ double diff_x = (x1 > x2) ? (x1 - x2) : (x2 - x1);
+ double diff_y = (y1 > y2) ? (y1 - y2) : (y2 - y1);
+ double dir_x = (x2 < x1) ? -1 : 1;
+ double dir_y = (y2 < y1) ? -1 : 1;
+
+ if (!diff_x && !diff_y)
+ return;
+
+ uint32_t half_alpha_mask;
+
+ if (P->fische->line_style == FISCHE_LINESTYLE_ALPHA_SIMULATION)
+ half_alpha_mask = (0x7f << P->red_shift)
+ + (0x7f << P->green_shift)
+ + (0x7f << P->blue_shift)
+ + (0x7f << P->alpha_shift);
+ else
+ half_alpha_mask = (0xff << P->red_shift)
+ + (0xff << P->green_shift)
+ + (0xff << P->blue_shift)
+ + (0x7f << P->alpha_shift);
+
+
+ if (diff_x > diff_y) {
+
+ int_fast16_t x;
+ for (x = x1; x * dir_x <= x2 * dir_x; x += dir_x) {
+
+ int_fast16_t y = (y1 + diff_y / diff_x * dir_y * abs (x - x1) + 0.5);
+
+ if ( (x < 0) || (x >= P->width) || (y < 0) || (y >= P->height))
+ continue;
+
+ if (P->fische->line_style != FISCHE_LINESTYLE_THIN) {
+ y ++;
+ if (! (y < 0) && ! (y >= P->height))
+ * (self->pixels + y * P->width + x) = color & half_alpha_mask;
+
+ y -= 2;
+ if ( (y < 0) || (y >= P->height)) continue;
+ * (self->pixels + y * P->width + x) = color & half_alpha_mask;
+
+ y ++;
+ }
+
+ * (self->pixels + y * P->width + x) = color;
+ }
+ } else {
+
+ int_fast16_t y;
+ for (y = y1; y * dir_y <= y2 * dir_y; y += dir_y) {
+
+ int_fast16_t x = (x1 + diff_x / diff_y * dir_x * abs (y - y1) + 0.5);
+
+ if ( (x < 0) || (x >= P->width) || (y < 0) || (y >= P->height))
+ continue;
+
+ if (P->fische->line_style != FISCHE_LINESTYLE_THIN) {
+ x ++;
+ if (! (x < 0) && ! (x >= P->width))
+ * (self->pixels + y * P->width + x) = color & half_alpha_mask;
+
+ x -= 2;
+ if ( (x < 0) || (x >= P->width)) continue;
+ * (self->pixels + y * P->width + x) = color & half_alpha_mask;
+
+ x ++;
+ }
+
+ * (self->pixels + y * P->width + x) = color;
+ }
+ }
+}
--- /dev/null
+#ifndef SCREENBUFFER_H
+#define SCREENBUFFER_H
+
+#include <stdint.h>
+
+struct fische;
+struct _fische__screenbuffer;
+struct fische__screenbuffer;
+
+
+
+struct fische__screenbuffer* fische__screenbuffer_new (struct fische* parent);
+void fische__screenbuffer_free (struct fische__screenbuffer* self);
+
+void fische__screenbuffer_lock (struct fische__screenbuffer* self);
+void fische__screenbuffer_unlock (struct fische__screenbuffer* self);
+
+void fische__screenbuffer_line (struct fische__screenbuffer* self,
+ int_fast16_t x1,
+ int_fast16_t y1,
+ int_fast16_t x2,
+ int_fast16_t y2,
+ uint32_t color);
+
+
+
+struct _fische__screenbuffer_ {
+ uint_fast8_t is_locked;
+ int_fast16_t width;
+ int_fast16_t height;
+ uint_fast8_t red_shift;
+ uint_fast8_t blue_shift;
+ uint_fast8_t green_shift;
+ uint_fast8_t alpha_shift;
+
+ struct fische* fische;
+};
+
+struct fische__screenbuffer {
+ uint32_t* pixels;
+
+ struct _fische__screenbuffer_* priv;
+};
+
+#endif
--- /dev/null
+#include "vector.h"
+
+inline double fische__vector_length (fische__vector* self)
+{
+ return sqrt (pow (self->x, 2) + pow (self->y, 2));
+}
+
+inline fische__vector fische__vector_normal (fische__vector* self)
+{
+ fische__vector r;
+ r.x = self->y;
+ r.y = - self->x;
+ return r;
+}
+
+inline fische__vector fische__vector_single (fische__vector* self)
+{
+ double l = fische__vector_length (self);
+ fische__vector r;
+ r.x = self->x / l;
+ r.y = self->y / l;
+ return r;
+}
+
+inline double fische__vector_angle (fische__vector* self)
+{
+ fische__vector su = fische__vector_single (self);
+ double a = acos (su.x);
+ if (self->y > 0) return a;
+ else return -a;
+}
+
+// conversion to 2x int8
+inline uint16_t fische__vector_to_uint16 (fische__vector* self)
+{
+ if (self->x < -127) self->x = -127;
+ if (self->x > 127) self->x = 127;
+ if (self->y < -127) self->y = -127;
+ if (self->y > 127) self->y = 127;
+
+ int8_t ix = (self->x < 0) ? self->x - 0.5 : self->x + 0.5;
+ int8_t iy = (self->y < 0) ? self->y - 0.5 : self->y + 0.5;
+
+ uint16_t retval = (uint8_t) ix;
+ retval |= ( (uint8_t) iy) << 8;
+ return retval;
+}
+
+inline fische__vector fische__vector_from_uint16 (uint16_t val)
+{
+ int8_t ix = val & 0xff;
+ int8_t iy = val >> 8;
+ fische__vector r;
+ r.x = ix;
+ r.y = iy;
+ return r;
+}
+
+inline void fische__vector_add (fische__vector* self, fische__vector* other)
+{
+ self->x += other->x;
+ self->y += other->y;
+}
+
+inline void fische__vector_sub (fische__vector* self, fische__vector* other)
+{
+ self->x -= other->x;
+ self->y -= other->y;
+}
+
+inline void fische__vector_mul (fische__vector* self, double val)
+{
+ self->x *= val;
+ self->y *= val;
+}
+
+inline void fische__vector_div (fische__vector* self, double val)
+{
+ self->x /= val;
+ self->y /= val;
+}
+
+fische__vector fische__vector_intersect_border (fische__vector* self,
+ fische__vector* normal_vec,
+ uint_fast16_t width,
+ uint_fast16_t height,
+ int_fast8_t direction)
+{
+ width--;
+ height--;
+
+ fische__vector nvec = *normal_vec;
+ if (direction == _FISCHE__VECTOR_RIGHT_) {
+ fische__vector_mul (&nvec, -1);
+ }
+
+ double t1, t2, t3, t4;
+
+ if (nvec.x == 0) {
+ t1 = 1e6;
+ t2 = 1e6;
+ } else {
+ t1 = - self->x / nvec.x;
+ t2 = (width - self->x) / nvec.x;
+ }
+
+ if (nvec.y == 0) {
+ t3 = 1e6;
+ t4 = 1e6;
+ } else {
+ t3 = -self->y / nvec.y;
+ t4 = (height - self->y) / nvec.y;
+ }
+
+ t1 = (t1 < 0) ? 1e6 : t1;
+ t2 = (t2 < 0) ? 1e6 : t2;
+ t3 = (t3 < 0) ? 1e6 : t3;
+ t4 = (t4 < 0) ? 1e6 : t4;
+
+ double a = (t1 < t2) ? t1 : t2;
+ double b = (t3 < t4) ? t3 : t4;
+
+ double min_t = (a < b) ? a : b;
+
+ int_fast16_t ret_x = self->x + nvec.x * min_t;
+ while (ret_x < 0) ret_x ++;
+ while ( (unsigned) ret_x > width) ret_x --;
+
+ int_fast16_t ret_y = self->y + nvec.y * min_t;
+ while (ret_y < 0) ret_y ++;
+ while ( (unsigned) ret_y > height) ret_y --;
+
+ fische__vector r;
+ r.x = ret_x;
+ r.y = ret_y;
+ return r;
+}
--- /dev/null
+#ifndef F_VECTOR_H
+#define F_VECTOR_H
+
+#include <math.h>
+#include <stdint.h>
+
+struct _fische__vector_ {
+ double x;
+ double y;
+};
+
+typedef struct _fische__vector_ fische__vector;
+typedef struct _fische__vector_ fische__point;
+
+enum {_FISCHE__VECTOR_LEFT_, _FISCHE__VECTOR_RIGHT_};
+
+double fische__vector_length (fische__vector* self);
+fische__vector fische__vector_normal (fische__vector* self);
+fische__vector fische__vector_single (fische__vector* self);
+double fische__vector_angle (fische__vector* self);
+uint16_t fische__vector_to_uint16 (fische__vector* self);
+fische__vector fische__vector_from_uint16 (uint16_t val);
+void fische__vector_add (fische__vector* self, fische__vector* other);
+void fische__vector_sub (fische__vector* self, fische__vector* other);
+void fische__vector_mul (fische__vector* self, double val);
+void fische__vector_div (fische__vector* self, double val);
+
+fische__vector fische__vector_intersect_border (fische__vector* self,
+ fische__vector* normal_vec,
+ uint_fast16_t width,
+ uint_fast16_t height,
+ int_fast8_t direction);
+
+#endif
--- /dev/null
+#include "fische_internal.h"
+
+#include <stdlib.h>
+#include <pthread.h>
+#include <string.h>
+
+#ifdef DEBUG
+#include <stdio.h>
+#endif
+
+#define N_FIELDS 20
+
+struct field_param {
+ uint16_t* data;
+ uint_fast8_t number;
+ uint_fast16_t start_y;
+ uint_fast16_t end_y;
+ struct _fische__vectorfield_* vecfield;
+};
+
+unsigned int rand_seed;
+
+inline void
+_fische__vectorfield_randomize_ (fische__vector* vec)
+{
+ vec->x += rand_r (&rand_seed) % 3;
+ vec->x -= 1;
+ vec->y += rand_r (&rand_seed) % 3;
+ vec->y -= 1;
+}
+
+
+inline void
+_fische__vectorfield_validate_ (struct _fische__vectorfield_* P,
+ fische__vector* vec,
+ double x,
+ double y)
+{
+ while (x + vec->x < 2)
+ vec->x += 1;
+ while (x + vec->x > P->width - 3)
+ vec->x -= 1;
+ while (y + vec->y < 2)
+ vec->y += 1;
+ while (y + vec->y > P->height - 2)
+ vec->y -= 1;
+}
+
+void*
+_fische__fill_thread_ (void* arg)
+{
+ struct field_param* params = arg;
+ uint16_t* field = params->data;
+ int fieldno = params->number;
+ struct _fische__vectorfield_* P = params->vecfield;
+
+ uint_fast16_t y;
+ for (y = params->start_y; y < params->end_y; y ++) {
+
+ uint_fast16_t x;
+ for (x = 0; x < P->width; x++) {
+
+ uint16_t* vector = field + x + y * P->width;
+
+ // distance and direction relative to center
+ fische__vector rvec;
+ rvec.x = x;
+ rvec.x -= P->center_x;
+ rvec.y = y;
+ rvec.y -= P->center_y;
+
+ fische__vector e = fische__vector_single (&rvec);
+ fische__vector n = fische__vector_normal (&e);
+
+ double r = fische__vector_length (&rvec) / P->dimension;
+
+ // distance and direction relative to left co-center
+ fische__vector rvec_left;
+ rvec_left.x = (double)x - P->center_x + P->width / 3 * P->fische->scale;
+ rvec_left.y = (double)y - P->center_y;
+
+ fische__vector e_left = fische__vector_single (&rvec_left);
+ fische__vector n_left = fische__vector_normal (&e_left);
+
+ double r_left = fische__vector_length (&rvec_left) / P->dimension;
+
+ // distance and direction relative to right co-center
+ fische__vector rvec_right;
+ rvec_right.x = (double)x - P->center_x - P->width / 3 * P->fische->scale;
+ rvec_right.y = (double)y - P->center_y;
+
+ fische__vector e_right = fische__vector_single (&rvec_right);
+ fische__vector n_right = fische__vector_normal (&e_right);
+
+ double r_right = fische__vector_length (&rvec_right) / P->dimension;
+
+ double speed = P->dimension / 45;
+
+ // correction factors ensure consistent average speeds with all field types
+ // double const corr[] = {0.77, 0.92, 1.72, 2.06, 1.45, 1.45, 1.73, 1.18, 3.24, 2.76, 0.82, 1.21, 1.73, 3.55, 0.47, 0.66, 0.96, 0.97, 1.00, 1.00};
+ double const corr[] = {0.83, 0.83, 1.56, 1.56, 1.08, 3.54, 1.56, 1.00, 4.47, 2.77, 0.74, 1.01, 1.56, 3.12, 0.67, 0.67, 0.83, 2.43, 1.21, 0.77};
+
+ fische__vector v;
+ switch (fieldno) {
+
+ case 0:
+ // linear vectors showing away from a horizontal mirror axis
+ v.x = 0;
+ v.y = (y < P->center_y) ? speed * corr[fieldno] : -speed * corr[fieldno];
+ break;
+
+ case 1:
+ // linear vectors showing away from a vertical mirror axis
+ v.x = (x < P->center_x) ? speed * corr[fieldno] : -speed * corr[fieldno];
+ v.y = 0;
+ break;
+
+ case 2:
+ // radial vectors showing away from the center
+ v = e;
+ fische__vector_mul (&v, -r * speed * corr[fieldno]);
+ break;
+
+ case 3:
+ // tangential vectors (right)
+ v = n;
+ fische__vector_mul (&v, r * speed * corr[fieldno]);
+ break;
+
+ case 4: {
+ // tangential-radial vectors (left)
+ fische__vector _v1 = n;
+ fische__vector_mul (&_v1, -r * speed * corr[fieldno]);
+ v = e;
+ fische__vector_mul (&v, -r * speed * corr[fieldno]);
+ fische__vector_add (&v, &_v1);
+ break;
+ }
+
+ case 5: {
+ // tree rings
+ double dv = cos (M_PI * 24 * r);
+ v = e;
+ fische__vector_mul (&v, speed * 0.33 * corr[fieldno] * dv);
+ break;
+ }
+
+ case 6: {
+ // hyperbolic vectors
+ v.x = e.y;
+ v.y = e.x;
+ fische__vector_mul (&v, -r * speed * corr[fieldno]);
+ break;
+ }
+
+ case 7:
+ // purely random
+ v.x = rand_r (&rand_seed) % (int_fast32_t) (2 * speed * corr[fieldno] + 1) - (speed * corr[fieldno]);
+ v.y = rand_r (&rand_seed) % (int_fast32_t) (2 * speed * corr[fieldno] + 1) - (speed * corr[fieldno]);
+ break;
+
+ case 8: {
+ // sphere
+ double dv = cos (M_PI * r);
+ v = e;
+ fische__vector_mul (&v, -r * dv * speed * corr[fieldno]);
+ break;
+ }
+
+ case 9: {
+ // sine distortion
+ double dv = sin (M_PI * 8 * r);
+ v = e;
+ fische__vector_mul (&v, -r * dv * speed * corr[fieldno]);
+ break;
+ }
+
+ case 10: {
+ // black hole
+ fische__vector _v1 = n;
+ fische__vector_mul (&_v1, speed * corr[fieldno]);
+ v = e;
+ fische__vector_mul (&v, r * speed * corr[fieldno]);
+ fische__vector_add (&v, &_v1);
+ if (r * P->dimension < 10) {
+ v.x = 0;
+ v.y = 0;
+ }
+ break;
+ }
+
+ case 11: {
+ // circular waves
+ double dim = pow (11 * M_PI, 2);
+ double _r = r * dim;
+ v = e;
+ fische__vector_mul (&v, -speed * corr[fieldno] * sqrt (1.04 - pow (cos (sqrt (_r)), 2) + 0.25 * pow (sin (sqrt (_r)), 2)));
+ break;
+ }
+
+ case 12:
+ // spinning CD
+ v = n;
+ if (fabs (r - 0.25) < 0.15)
+ fische__vector_mul (&v, r * speed * corr[fieldno]);
+ else
+ fische__vector_mul (&v, -r * speed * corr[fieldno]);
+ break;
+
+ case 13: {
+ // three spinning disks
+ double rt = 0.3;
+ if (r < rt * 1.2) {
+ v = n;
+ fische__vector_mul (&v, -r * speed * corr[fieldno]);
+ } else if (r_left < rt) {
+ v = n_left;
+ fische__vector_mul (&v, r_left * speed * corr[fieldno]);
+ } else if (r_right < rt) {
+ v = n_right;
+ fische__vector_mul (&v, r_right * speed * corr[fieldno]);
+ } else {
+ v.x = 0;
+ v.y = 0;
+ }
+ break;
+ }
+
+ case 14: {
+ // 3-centered fields - radial
+ fische__vector _v1 = e_left;
+ fische__vector_mul (&_v1, (2 - r_left) * speed * corr[fieldno]);
+ fische__vector _v2 = e_right;
+ fische__vector_mul (&_v2, (2 - r_right) * speed * corr[fieldno]);
+ v = e;
+ fische__vector_mul (&v, (2 - r) * -speed * corr[fieldno]);
+ fische__vector_add (&v, &_v1);
+ fische__vector_add (&v, &_v2);
+ break;
+ }
+
+ case 15: {
+ // 3-centered fields - tangential
+ fische__vector _v1 = n_left;
+ fische__vector_mul (&_v1, (2 - r_left) * -speed * corr[fieldno]);
+ fische__vector _v2 = n_right;
+ fische__vector_mul (&_v2, (2 - r_right) * -speed * corr[fieldno]);
+ v = n;
+ fische__vector_mul (&v, (2 - r) * speed * corr[fieldno]);
+ fische__vector_add (&v, &_v1);
+ fische__vector_add (&v, &_v2);
+ break;
+ }
+
+ case 16: {
+ // lenses effect
+ double _r = r * 8 * M_PI;
+ fische__vector _v1 = e;
+ fische__vector_mul (&_v1, sin (_r) * -speed * corr[fieldno]);
+ v = n;
+ fische__vector_mul (&v, sin (8 * fische__vector_angle (&e)) * -speed * corr[fieldno]);
+ fische__vector_add (&v, &_v1);
+ break;
+ }
+
+ case 17: {
+ // lenses effect
+ double _r = r * 24 * M_PI;
+ fische__vector _v1 = e;
+ fische__vector_mul (&_v1, sin (_r) * -speed * 0.33 * corr[fieldno]);
+ v = n;
+ fische__vector_mul (&v, sin (24 * fische__vector_angle (&e)) * -speed * 0.33 * corr[fieldno]);
+ fische__vector_add (&v, &_v1);
+ break;
+ }
+
+ case 18: {
+ // fan 1
+ v = e;
+ double angle = fische__vector_angle (&e);
+ fische__vector_mul (&v, -speed * corr[fieldno] * sin (8 * angle));
+ break;
+ }
+
+ case 19: {
+ // fan 1
+ v = e;
+ double angle = fische__vector_angle (&e);
+ fische__vector_mul (&v, -speed * corr[fieldno] * (1.1 + sin (8 * angle)));
+ break;
+ }
+
+ default:
+ // index too high. return nothing.
+ return 0;
+ }
+
+ if (P->fische->blur_mode == FISCHE_BLUR_FUZZY)
+ _fische__vectorfield_randomize_ (&v);
+
+ _fische__vectorfield_validate_ (P, &v, x, y);
+ *vector = fische__vector_to_uint16 (&v);
+ }
+ }
+ return 0;
+}
+
+void
+_fische__fill_field_ (struct _fische__vectorfield_* P, uint_fast8_t fieldno)
+{
+ uint16_t* field = P->fields + fieldno * P->fieldsize / 2;
+
+ // threads maximum is 8
+ pthread_t vec_threads[8];
+ struct field_param params[8];
+
+ uint_fast8_t i;
+ for (i = 0; i < P->threads; ++ i) {
+ params[i].data = field;
+ params[i].number = fieldno;
+ params[i].start_y = (i * P->height) / P->threads;
+ params[i].end_y = ( (i + 1) * P->height) / P->threads;
+ params[i].vecfield = P;
+
+ pthread_create (&vec_threads[i], NULL, _fische__fill_thread_, ¶ms[i]);
+ }
+
+ for (i = 0; i < P->threads; ++ i) {
+ pthread_join (vec_threads[i], NULL);
+ }
+}
+
+struct fische__vectorfield*
+fische__vectorfield_new (struct fische* parent,
+ double* progress,
+ uint_fast8_t* cancel) {
+
+ struct fische__vectorfield* retval = malloc (sizeof (struct fische__vectorfield));
+ retval->priv = malloc (sizeof (struct _fische__vectorfield_));
+ struct _fische__vectorfield_* P = retval->priv;
+
+ rand_seed = time (NULL);
+ *progress = 0;
+
+ P->fische = parent;
+ P->width = parent->width;
+ P->height = parent->height;
+ P->center_x = P->width / 2;
+ P->center_y = P->height / 2;
+ P->dimension = P->width < P->height ? P->width * P->fische->scale : P->height * P->fische->scale;
+ P->fieldsize = P->width * P->height * sizeof (int16_t);
+ P->threads = parent->used_cpus;
+ P->cancelled = 0;
+
+ // if we have stored fields, load them
+ if (parent->read_vectors) {
+ size_t bytes = parent->read_vectors ( (void**) (&P->fields));
+ if (bytes) {
+ *progress = 1;
+ P->n_fields = bytes / P->fieldsize;
+ retval->field = P->fields;
+ return retval;
+ }
+ }
+
+ // if not, recalculate everything
+ P->fields = malloc (N_FIELDS * P->fieldsize);
+ P->n_fields = N_FIELDS;
+
+ uint_fast8_t i;
+ for (i = 0; i < N_FIELDS; ++i) {
+ if (*cancel) {
+ P->cancelled = 1;
+ break;
+ }
+ _fische__fill_field_ (P, i);
+ *progress = (i + 1);
+ *progress /= N_FIELDS;
+ }
+
+ *progress = 1;
+
+ retval->field = P->fields;
+
+ return retval;
+}
+
+void
+fische__vectorfield_free (struct fische__vectorfield* self)
+{
+ if (!self)
+ return;
+
+ struct _fische__vectorfield_* P = self->priv;
+
+ if (!P->cancelled && P->fische->write_vectors) {
+ P->fische->write_vectors (P->fields, P->n_fields * P->fieldsize);
+ }
+
+ free (self->priv->fields);
+ free (self->priv);
+ free (self);
+}
+
+void
+fische__vectorfield_change (struct fische__vectorfield* self)
+{
+ struct _fische__vectorfield_* P = self->priv;
+
+ uint16_t* n = self->field;
+ while (n == self->field) {
+ self->field = P->fields + rand() % P->n_fields * P->width * P->height;
+ }
+}
--- /dev/null
+#ifndef VECTORFIELD_H
+#define VECTORFIELD_H
+
+#include <stdint.h>
+
+struct fische;
+struct _fische__vectorfield_;
+struct fische__vectorfield;
+
+
+
+struct fische__vectorfield* fische__vectorfield_new (struct fische* parent, double* progress, uint_fast8_t* cancel);
+void fische__vectorfield_free (struct fische__vectorfield* self);
+
+void fische__vectorfield_change (struct fische__vectorfield* self);
+
+
+
+struct _fische__vectorfield_ {
+ uint16_t* fields;
+ uint_fast32_t fieldsize;
+ uint_fast16_t width;
+ uint_fast16_t height;
+ uint_fast16_t dimension;
+ uint_fast16_t center_x;
+ uint_fast16_t center_y;
+ uint_fast8_t threads;
+ uint_fast8_t n_fields;
+ uint_fast8_t cancelled;
+
+ struct fische* fische;
+};
+
+
+
+struct fische__vectorfield {
+ uint16_t* field;
+
+ struct _fische__vectorfield_* priv;
+};
+
+#endif
--- /dev/null
+#include "fische_internal.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#ifdef DEBUG
+#include <stdio.h>
+#endif
+
+struct fische__wavepainter*
+fische__wavepainter_new (struct fische* parent) {
+
+ struct fische__wavepainter* retval = malloc (sizeof (struct fische__wavepainter));
+ retval->priv = malloc (sizeof (struct _fische__wavepainter_));
+ struct _fische__wavepainter_* P = retval->priv;
+
+ P->fische = parent;
+
+ uint32_t full_alpha = 0xff << FISCHE_PRIVATE (P)->screenbuffer->priv->alpha_shift;
+
+ P->width = parent->width;
+ P->height = parent->height;
+ P->angle = 0;
+ P->center_x = P->width / 2;
+ P->center_y = P->height / 2;
+ P->color_1 = (rand() % 0xffffffff) | full_alpha;
+ P->color_2 = (~P->color_1) | full_alpha;
+ P->direction = 1;
+ P->is_rotating = 0;
+ P->n_shapes = 2;
+ P->rotation_increment = 0;
+ P->shape = 0;
+
+ return retval;
+}
+
+void
+fische__wavepainter_free (struct fische__wavepainter* self)
+{
+ if (!self)
+ return;
+
+ free (self->priv);
+ free (self);
+}
+
+void
+fische__wavepainter_paint (struct fische__wavepainter* self,
+ double* data,
+ uint_fast16_t size)
+{
+ if (!size)
+ return;
+
+ struct _fische__wavepainter_* P = self->priv;
+
+ // rotation
+ if (P->is_rotating) {
+ P->angle += P->rotation_increment;
+ if ( (P->angle > 2 * M_PI) || (P->angle < -2 * M_PI)) {
+ P->angle = 0;
+ P->is_rotating = 0;
+ }
+ }
+
+ // only init fische scale once
+ static double f_scale = 0;
+ if (f_scale == 0)
+ f_scale = P->fische->scale;
+
+ // necessary parameters
+ double dim = (P->height < P->width) ? P->height : P->width;
+ dim *= f_scale;
+ double factor = pow (10, P->fische->amplification / 10);
+ double scale = 6 / dim / factor;
+
+ // alpha saturation fix
+ struct fische__screenbuffer* sbuf = FISCHE_PRIVATE (P)->screenbuffer;
+ fische__screenbuffer_line (sbuf, 0, 0, P->width - 1, 0, 0);
+ fische__screenbuffer_line (sbuf, P->width - 1, 0, P->width - 1, P->height - 1, 0);
+ fische__screenbuffer_line (sbuf, P->width - 1, P->height - 1, 0, P->height - 1, 0);
+ fische__screenbuffer_line (sbuf, 0, P->height - 1, 0, 0, 0);
+
+ switch (P->shape) {
+
+ case 0: {
+ fische__point center;
+ center.x = P->center_x;
+ center.y = P->center_y;
+
+ // base will be the middle of a line,
+ // normally horizontal (angle = 0), but could be rotating
+ fische__point base1;
+ base1.x = center.x + (dim / 6) * sin (P->angle);
+ base1.y = center.y + (dim / 6) * cos (P->angle);
+
+ fische__point base2;
+ base2.x = P->width / 2 - (dim / 6) * sin (P->angle);
+ base2.y = P->height / 2 - (dim / 6) * cos (P->angle);
+
+ // create vectors perpendicular to the line center->base
+ fische__vector _nvec1 = base1;
+ fische__vector_sub (&_nvec1, ¢er);
+ fische__vector nvec1 = fische__vector_normal (&_nvec1);
+
+ fische__vector _nvec2 = base2;
+ fische__vector_sub (&_nvec2, ¢er);
+ fische__vector nvec2 = fische__vector_normal (&_nvec2);
+
+ // find the points where the line would exit the screen
+ fische__point start1 = fische__vector_intersect_border (&base1, &nvec1, P->width, P->height, _FISCHE__VECTOR_LEFT_);
+ fische__point end1 = fische__vector_intersect_border (&base1, &nvec1, P->width, P->height, _FISCHE__VECTOR_RIGHT_);
+
+ fische__point start2 = fische__vector_intersect_border (&base2, &nvec2, P->width, P->height, _FISCHE__VECTOR_LEFT_);
+ fische__point end2 = fische__vector_intersect_border (&base2, &nvec2, P->width, P->height, _FISCHE__VECTOR_RIGHT_);
+
+ // determine the direction and length (i.e. vector)
+ // of the increment between two sound samples
+ fische__vector v1 = end1;
+ fische__vector_sub (&v1, &start1);
+ fische__vector_div (&v1, size);
+
+ fische__vector v2 = end2;
+ fische__vector_sub (&v2, &start2);
+ fische__vector_div (&v2, size);
+
+ // determine the normal vectors
+ // for calculating the sound sample offset (amplitude)
+ fische__vector _n1 = fische__vector_normal (&v1);
+ fische__vector n1 = fische__vector_single (&_n1);
+ fische__vector _n2 = fische__vector_normal (&v2);
+ fische__vector n2 = fische__vector_single (&_n2);
+
+ // draw both lines
+ fische__point base_p1 = start1;
+ fische__point base_p2 = start2;
+
+ uint_fast16_t i;
+ for (i = 0; i < size - 1; i ++) {
+ fische__point pt11 = base_p1;
+ fische__vector offset11 = n1;
+ fische__vector_mul (&offset11, (* (data + 2 * i)));
+ fische__vector_div (&offset11, scale);
+ fische__vector_add (&pt11, &offset11);
+
+ fische__point pt21 = base_p2;
+ fische__vector offset21 = n2;
+ fische__vector_mul (&offset21, (* (data + 1 + 2 * i)));
+ fische__vector_div (&offset21, scale);
+ fische__vector_add (&pt21, &offset21);
+
+ fische__vector_add (&base_p1, &v1);
+ fische__vector_add (&base_p2, &v2);
+
+ fische__point pt12 = base_p1;
+ fische__vector offset12 = n1;
+ fische__vector_mul (&offset12, (* (data + 2 * (i + 1))));
+ fische__vector_div (&offset12, scale);
+ fische__vector_add (&pt12, &offset12);
+
+ fische__point pt22 = base_p2;
+ fische__vector offset22 = n2;
+ fische__vector_mul (&offset22, (* (data + 1 + 2 * (i + 1))));
+ fische__vector_div (&offset22, scale);
+ fische__vector_add (&pt22, &offset22);
+
+ fische__screenbuffer_line (sbuf, pt11.x, pt11.y, pt12.x, pt12.y, P->color_1);
+ fische__screenbuffer_line (sbuf, pt21.x, pt21.y, pt22.x, pt22.y, P->color_2);
+ }
+ return;
+ }
+
+ // circular shape
+ case 1: {
+ double f = cos (M_PI / 3 + 2 * P->angle) + 0.5;
+ double e = 1;
+
+ uint_fast16_t i;
+ for (i = 0; i < size - 1; i ++) {
+
+ double incr = i;
+
+ // calculate angles for this and the next sound sample
+ double phi1 = M_PI * (0.25 + incr / size) + P->angle;
+ double phi2 = phi1 + M_PI / size;
+
+ // calculate the corresponding radius
+ double r1 = dim / 4 + * (data + 2 * i) / scale;
+ double r2 = dim / 4 + * (data + 2 * (i + 1)) / scale;
+
+ uint_fast16_t x1 = floor ( (P->center_x + f * r1 * sin (phi1)) + 0.5);
+ uint_fast16_t x2 = floor ( (P->center_x + f * r2 * sin (phi2)) + 0.5);
+ uint_fast16_t y1 = floor ( (P->center_y + e * r1 * cos (phi1)) + 0.5);
+ uint_fast16_t y2 = floor ( (P->center_y + e * r2 * cos (phi2)) + 0.5);
+
+ fische__screenbuffer_line (sbuf, x1, y1, x2, y2, P->color_1);
+
+ // the second line will be exactly on the
+ // opposite side of the circle
+ phi1 += M_PI;
+ phi2 += M_PI;
+
+ r1 = dim / 4 + * (data + 1 + 2 * i) / scale;
+ r2 = dim / 4 + * (data + 1 + 2 * (i + 1)) / scale;
+
+ x1 = floor ( (P->center_x + f * r1 * sin (phi1)) + 0.5);
+ x2 = floor ( (P->center_x + f * r2 * sin (phi2)) + 0.5);
+ y1 = floor ( (P->center_y + e * r1 * cos (phi1)) + 0.5);
+ y2 = floor ( (P->center_y + e * r2 * cos (phi2)) + 0.5);
+
+ fische__screenbuffer_line (sbuf, x1, y1, x2, y2, P->color_2);
+ }
+ return;
+ }
+ }
+
+}
+
+void
+fische__wavepainter_beat (struct fische__wavepainter* self, double frames_per_beat)
+{
+ struct _fische__wavepainter_* P = self->priv;
+ if (!P->is_rotating) {
+ if (frames_per_beat != 0) {
+ P->direction = 1 - 2 * (rand() % 2);
+ P->rotation_increment = M_PI / frames_per_beat / 2 * P->direction;
+ P->angle = 0;
+ P->is_rotating = 1;
+ }
+ }
+
+}
+
+void
+fische__wavepainter_change_color (struct fische__wavepainter* self,
+ double frames_per_beat,
+ double energy)
+{
+ struct _fische__wavepainter_* P = self->priv;
+
+ uint32_t full_alpha = 0xff << FISCHE_PRIVATE (P)->screenbuffer->priv->alpha_shift;
+
+ if (!frames_per_beat && !energy) {
+ P->color_1 = (rand() % 0xffffffff) | full_alpha;
+ P->color_2 = (~P->color_1) | full_alpha;
+ }
+
+ if (!frames_per_beat)
+ return;
+
+ double hue = frames_per_beat / 2;
+ while (hue >= 6)
+ hue -= 6;
+
+ double sv = (energy > 1) ? 1 : pow (energy, 4);
+ double x = sv * (1 - fabs ( (int_fast32_t) hue % 2 - 1));
+
+ double r, g, b;
+
+ switch ( (int_fast32_t) hue) {
+ case 0:
+ r = sv;
+ g = x;
+ b = 0;
+ break;
+ case 1:
+ r = x;
+ g = sv;
+ b = 0;
+ break;
+ case 2:
+ r = 0;
+ g = sv;
+ b = x;
+ break;
+ case 3:
+ r = 0;
+ g = x;
+ b = sv;
+ break;
+ case 4:
+ r = x;
+ g = 0;
+ b = sv;
+ break;
+ default:
+ case 5:
+ r = sv;
+ g = 0;
+ b = x;
+ }
+
+ uint32_t red = floor (r * 255 + 0.5);
+ uint32_t green = floor (b * 255 + 0.5);
+ uint32_t blue = floor (g * 255 + 0.5);
+
+ P->color_1 = (blue << FISCHE_PRIVATE (P)->screenbuffer->priv->blue_shift)
+ + (green << FISCHE_PRIVATE (P)->screenbuffer->priv->green_shift)
+ + (red << FISCHE_PRIVATE (P)->screenbuffer->priv->red_shift)
+ + (0xff << FISCHE_PRIVATE (P)->screenbuffer->priv->alpha_shift);
+
+ P->color_2 = (~P->color_1) | full_alpha;
+}
+
+void
+fische__wavepainter_change_shape (struct fische__wavepainter* self)
+{
+ struct _fische__wavepainter_* P = self->priv;
+
+ if (P->is_rotating) return;
+ int_fast8_t n = P->shape;
+ while (n == P->shape) n = rand() % P->n_shapes;
+ P->shape = n;
+}
--- /dev/null
+#ifndef WAVEPAINTER_H
+#define WAVEPAINTER_H
+
+#include <stdint.h>
+
+struct fische;
+struct _fische__wavepainter_;
+struct fische__wavepainter;
+
+
+
+struct fische__wavepainter* fische__wavepainter_new (struct fische* parent);
+void fische__wavepainter_free (struct fische__wavepainter* self);
+
+void fische__wavepainter_paint (struct fische__wavepainter* self, double* data, uint_fast16_t size);
+void fische__wavepainter_beat (struct fische__wavepainter* self, double bpm);
+void fische__wavepainter_change_color (struct fische__wavepainter* self, double bpm, double energy);
+void fische__wavepainter_change_shape (struct fische__wavepainter* self);
+
+
+
+struct _fische__wavepainter_ {
+ uint_fast16_t width;
+ uint_fast16_t height;
+ uint_fast16_t center_x;
+ uint_fast16_t center_y;
+ int_fast8_t direction;
+ uint_fast8_t shape;
+ uint_fast8_t n_shapes;
+ uint32_t color_1;
+ uint32_t color_2;
+ double angle;
+ uint_fast8_t is_rotating;
+ double rotation_increment;
+
+ struct fische* fische;
+};
+
+struct fische__wavepainter {
+ struct _fische__wavepainter_* priv;
+};
+
+#endif
BEGIN
VALUE "CompanyName", "Team XBMC"
VALUE "FileDescription", "XBMC"
- VALUE "FileVersion", "12.0-RC3"
+ VALUE "FileVersion", "13.0-ALPHA1"
VALUE "InternalName", "XBMC.exe"
VALUE "LegalCopyright", "Copyright (c) Team XBMC. All rights reserved."
VALUE "OriginalFilename", "XBMC.exe"
VALUE "ProductName", "XBMC for Windows"
- VALUE "ProductVersion", "12.0-RC3"
+ VALUE "ProductVersion", "13.0-ALPHA1"
END
END
BLOCK "VarFileInfo"
#define EXTKEYPAD(keypad) ((scancode & 0x100)?(mvke):(keypad))
static XBMCKey VK_keymap[XBMCK_LAST];
-static HKL hLayoutUS = NULL;
static GUID USB_HID_GUID = { 0x4D1E55B2, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } };
GetKeyboardLayoutName(current_layout);
- hLayoutUS = LoadKeyboardLayout("00000409", KLF_NOTELLSHELL);
- if (!hLayoutUS)
- hLayoutUS = GetKeyboardLayout(0);
-
LoadKeyboardLayout(current_layout, KLF_ACTIVATE);
/* Map the VK keysyms */
static int XBMC_MapVirtualKey(int scancode, int vkey)
{
-// It isn't clear why the US keyboard layout was being used. This causes
-// problems with e.g. the \ key. I have provisionally switched the code
-// to use the Windows layout.
-// int mvke = MapVirtualKeyEx(scancode & 0xFF, 1, hLayoutUS);
int mvke = MapVirtualKeyEx(scancode & 0xFF, 1, NULL);
switch(vkey)