Browse Source

Merge pull request #48 from cuu/master

spectrum
dphys 5 years ago
parent
commit
c5675b7ec7

+ 2 - 1
Menu/GameShell/Music Player/list_item.py

@@ -65,7 +65,7 @@ class ListItem(object):
     _PlayingProcess = 0 # 0 - 100
     _Parent  = None
     
-    
+    _Text = ""
     def __init__(self):
         self._Labels = {}
         self._Icons  = {}
@@ -75,6 +75,7 @@ class ListItem(object):
     def Init(self,text):
 
         #self._Fonts["normal"] = fonts["veramono12"]
+        self._Text = text
         
         l = ListItemLabel()
         l._PosX = 22

+ 259 - 175
Menu/GameShell/Music Player/mpd_spectrum_page.py

@@ -1,10 +1,11 @@
 # -*- coding: utf-8 -*- 
 
+import os
 import time
 import pygame
 
-from numpy import fromstring,ceil,abs,log10,isnan,isinf,int16,sqrt,mean
-from numpy import fft as Fft
+import numpy
+import math
 
 import gobject
 
@@ -20,141 +21,51 @@ from UI.keys_def   import CurKeys
 from UI.icon_item import IconItem
 from UI.icon_pool  import MyIconPool
 
-from Queue import Queue, Empty
 from threading  import Thread
 
-
-
 from list_item  import ListItem
 
 import myvars
 
 class PIFI(object):
     _MPD_FIFO = "/tmp/mpd.fifo"
-    _SAMPLE_SIZE = 256
+    _SAMPLE_SIZE = 1024
     _SAMPLING_RATE = 44100
     _FIRST_SELECTED_BIN = 5
-    _NUMBER_OF_SELECTED_BINS = 10
-    _SCALE_WIDTH = Height/2 - 20
+    _NUMBER_OF_SELECTED_BINS = 1024
 
-    count = 0
-    average = 0
-
-    rmscount=0
-    rmsaverage=0
     
     def __init__(self):
         self.sampleSize = self._SAMPLE_SIZE
         self.samplingRate = self._SAMPLING_RATE
-        self.firstSelectedBin = self._FIRST_SELECTED_BIN
-        self.numberOfSelectedBins = self._NUMBER_OF_SELECTED_BINS
-        
-        # Initialization : frequency bins
-        freq = Fft.fftfreq(self.sampleSize) * self.samplingRate
-        freqR = freq[:self.sampleSize/2]
-        self.bins = freqR[self.firstSelectedBin:self.firstSelectedBin+self.numberOfSelectedBins]
         
-        self.resetSmoothing()
-    
-    def resetSmoothing(self):
-        self.count = 0
-        self.average = 0
-        self.rmscount = 0
-        self.rmsaverage = 0
-
-    def rms_smoothOut(self, x):
-        self.rmscount += 1
-        self.rmsaverage = (self.rmsaverage*self.rmscount + x) / (self.rmscount+1)
-        return self.rmsaverage
-    
-    def smoothOut(self, x):
-        self.count += 1
-        self.average = (self.average*self.count + x) / (self.count+1)
-        return self.average
+    def GetSpectrum(self,fifoFile,trim_by=10,log_scale=False,div_by=100):
+        try:
+            rawSamples = os.read(fifoFile,self.sampleSize)    # will return empty lines (non-blocking)
+            if len(rawSamples) < 1:
+#                print("Read error")
+                return rawSamples
+        except Exception,e:
+            return ""
     
-    def scaleList(self, _list):
-        for i,x in enumerate(_list):
-            if isnan(x) or isinf(x):
-                _list[i] = 0
-
-        # Compute a simple just-above 'moving average' of maximums
-        maximum = 1.1*self.smoothOut(max( _list ))
-        if maximum == 0:
-            scaleFactor = 0.0
-        else:
-            scaleFactor = self._SCALE_WIDTH/float(maximum)
-            
-        # Compute the scaled list of values
-        scaledList = [int(x*scaleFactor) for x in _list ]
-        return scaledList
+        data = numpy.fromstring(rawSamples, dtype=numpy.int16)
 
-    
-    def computeSpectrum(self, fifoFile):
-        
-        # Read PCM samples from fifo
-        rawSamples = fifoFile.read(self.sampleSize)    # will return empty lines (non-blocking)
-        if len(rawSamples) == 0:
-            print("computeSpectrum read zero")
-            return [],[]
-        else:
-            pass
-##            print("computeSpectrum %d " % len(rawSamples))
-            
-        pcm = fromstring(rawSamples, dtype=int16)
-        
-        # Normalize [-1; +1]
-        pcm = pcm / (2.**15)
-
-        # Compute RMS directly from signal
-        rms = sqrt(mean(pcm**2))
-        # Compute a simple 'moving maximum'
-        maximum = 2*self.rms_smoothOut(rms)
-        if maximum == 0:
-            scaleFactor = 0.0
-        else:
-            scaleFactor = self._SCALE_WIDTH/float(maximum)
-        
-        final_rms = int(rms*scaleFactor)
-        
-        # Compute FFT
-        N = pcm.size
-        fft = Fft.fft(pcm)
-        uniquePts = ceil((N+1)/2.0)
-        fft = fft[0:int(uniquePts)]
-        
-        # Compute amplitude spectrum
-        amplitudeSpectrum = abs(fft) / float(N)
-        
-        # Compute power spectrum
-        p = amplitudeSpectrum**2
-        
-        # Multiply by two to keep same energy
-        # See explanation:
-        # https://web.archive.org/web/20120615002031/http://www.mathworks.com/support/tech-notes/1700/1702.html
-        if N % 2 > 0: 
-            # odd number of points
-            # odd nfft excludes Nyquist point
-            p[1:len(p)] = p[1:len(p)] * 2 
-        else:
-            # even number of points
-            p[1:len(p) -1] = p[1:len(p) - 1] * 2
-        
-        # Power in logarithmic scale (dB)
-        logPower = 10*log10(p)
-        
-        # Compute RMS from power
-        #rms = numpy.sqrt(numpy.sum(p))
-        #print "RMS(power):", rms
+        data = data * numpy.hanning(len(data))
+
+        left,right = numpy.split(numpy.abs(numpy.fft.fft(data)),2)
+        spec_y     = numpy.add(left,right[::-1])
+
+        if log_scale:
+            spec_y=numpy.multiply(20,numpy.log10(spec_y))
+        if trim_by:
+            i=int((self.sampleSize/2)/trim_by)
+            spec_y=spec_y[:i]
+        if div_by:
+            spec_y=spec_y/float(div_by)
         
-        # Select a significant range in the spectrum
-        spectrum = logPower[self.firstSelectedBin:self.firstSelectedBin+self.numberOfSelectedBins]
-            
-        # Scale the spectrum 
-        scaledSpectrum = self.scaleList(spectrum)
-        scaledSpectrum.append( final_rms)
-        return scaledSpectrum
+        return spec_y
+    
     
-
 class MPDSpectrumPage(Page):
 
     _Icons = {}
@@ -162,14 +73,15 @@ class MPDSpectrumPage(Page):
     _FootMsg = ["Nav","","","Back",""]
     _MyList = []
     _ListFont = fonts["veramono12"]
-
+    _SongFont = fonts["notosanscjk12"]
     _PIFI   =  None
     _FIFO   = None
     _Color  = pygame.Color(126,206,244)
     _GobjectIntervalId = -1
     _Queue = None
     _KeepReading = True
-
+    _ReadingThread = None
+    
     _BGpng = None
     _BGwidth = 320
     _BGheight = 200
@@ -182,10 +94,23 @@ class MPDSpectrumPage(Page):
     _SheepBodyW = 105
     _SheepBodyH = 81
 
+    _RollCanvas = None
+    _RollW     = 180
+    _RollH     = 18
+    
     _freq_count = 0
     _head_dir = 0
 
     _Neighbor = None
+
+
+    _bby  = []
+    _bbs  = []
+    _capYPositionArray = []
+    _frames = 0
+    read_retry = 0
+    _queue_data = []
+    _vis_values = []
     
     def __init__(self):
         Page.__init__(self)
@@ -200,7 +125,8 @@ class MPDSpectrumPage(Page):
         self._Height = self._Screen._Height
 
         self._CanvasHWND = self._Screen._CanvasHWND
-
+        self._RollCanvas = pygame.Surface(( self._RollW,self._RollH))
+        
         """
         self._BGpng = IconItem()
         self._BGpng._ImgSurf = MyIconPool._Icons["sheep_bg"]
@@ -221,19 +147,46 @@ class MPDSpectrumPage(Page):
         self._SheepBody.Adjust(0,0,self._SheepBodyW,self._SheepBodyH,0)
         """
         
-        self.Start()
-        self._GobjectIntervalId = gobject.timeout_add(50,self.Playing)
+        self._cwp_png = IconItem()
+        self._cwp_png._ImgSurf = MyIconPool._Icons["tape"]
+        self._cwp_png._MyType = ICON_TYPES["STAT"]
+        self._cwp_png._Parent = self
+        self._cwp_png.Adjust(0,0,79,79,0)
+
+
+        self._song_title = Label()
+        self._song_title.SetCanvasHWND(self._RollCanvas)
+        self._song_title.Init("Untitled",self._SongFont,(255,255,255))
+
+
+        self._title = Label()
+        self._title.SetCanvasHWND(self._CanvasHWND)
+        self._title.Init("Title:",self._ListFont,(255,255,255))
+
+        self._time = Label()
+        self._time.SetCanvasHWND(self._CanvasHWND)
+        self._time.Init("Time:",self._ListFont,(255,255,255))        
+
+
+        self._time2 = Label()
+        self._time2.SetCanvasHWND(self._CanvasHWND)
+        self._time2.Init("00:00-00:00",self._ListFont,(255,255,255))        
+
         
+        self.Start()
+                
     def Start(self):
+
+        if self._Screen.CurPage() != self:
+            return
         
         try:
-            self._FIFO = open(self._PIFI._MPD_FIFO)
-            q = Queue()
-            self._Queue = q
+            self._FIFO = os.open(self._PIFI._MPD_FIFO, os.O_RDONLY | os.O_NONBLOCK)
             
             t = Thread(target=self.GetSpectrum)
             t.daemon = True # thread dies with the program
             t.start()
+            self._ReadingThread = t
             
         except IOError:
             print("open %s failed"%self._PIFI._MPD_FIFO)
@@ -242,61 +195,79 @@ class MPDSpectrumPage(Page):
 
 
     def GetSpectrum(self):
-        if self._FIFO == None:
-            print("self._FIFO none")
-            return
+        while self._KeepReading and self._FIFO != None:
+            raw_samples = self._PIFI.GetSpectrum(self._FIFO)
+            if len(raw_samples) < 1:
+                #print("sleeping... 0.01")
+                time.sleep(0.01)
+                self.read_retry+=1
+                if self.read_retry > 40:
+                    os.close(self._FIFO)
+                    self._FIFO = os.open(self._PIFI._MPD_FIFO, os.O_RDONLY | os.O_NONBLOCK)
+                    self.read_retry = 0
+                
+                self.Playing()
+                
+            else:
+                self.read_retry = 0
+                self._queue_data = raw_samples
+                self.Playing()        
         
-        scaledSpectrum = self._PIFI.computeSpectrum(self._FIFO)
-        self._Queue.put( scaledSpectrum )
 
-        self._KeepReading = False
+    def Playing(self):
         
-        return ## Thread ends
+        self._Screen.Draw()
+        self._Screen.SwapAndShow()
 
-    def Playing(self):
-        if self._Screen.CurPage() == self:
-            if self._KeepReading == False:
-                self._KeepReading = True
-                
-                t = Thread(target=self.GetSpectrum)
-                t.daemon=True
-                t.start()
-                
-            self._Screen.Draw()
-            self._Screen.SwapAndShow()
-            
-        else:
+
+    def ClearCanvas(self):
+        self._CanvasHWND.fill((0,0,0))
+
+    def SgsSmooth(self):
+        passes = 1
+        points = 3
+        origs = self._bby[:]
+        for p in range(0,passes):
+            pivot = int(points/2.0)
             
-            return False
-        
-        return True
+            for i in range(0,pivot):
+                self._bby[i] = origs[i]
+                self._bby[ len(origs) -i -1 ] = origs[ len(origs) -i -1 ]
+
+            smooth_constant = 1.0/(2.0*pivot+1.0)
+            for i in range(pivot, len(origs)-pivot):
+                _sum = 0.0
+                for j in range(0,(2*pivot)+1):
+                    _sum += (smooth_constant * origs[i+j-pivot]) +j -pivot
+
+                self._bby[i] = _sum
+
+            if p < (passes - 1):
+                origs = self._bby[:]
     
     def OnLoadCb(self):
+        if self._Neighbor != None:
+            pass
+        
+        if self._KeepReading == False:
+            self._KeepReading = True
+        
         if self._FIFO == None:
             self.Start()
             
-        if self._Queue != None:
-            with self._Queue.mutex:
-                self._Queue.queue.clear()
-        
-        try:
-            if self._GobjectIntervalId != -1:
-                gobject.source_remove(self._GobjectIntervalId)
-        except:
-            pass
-        
-        self._GobjectIntervalId = gobject.timeout_add(50,self.Playing)
-
     
     def KeyDown(self,event):
         if event.key == CurKeys["Menu"] or event.key == CurKeys["A"]:
-            if self._FIFO != None and self._FIFO.closed == False:
-                try:
-                    self._FIFO.close()
-                    self._FIFO = None
-                except Exception, e:
-                    print(e)
+            try:
+                os.close(self._FIFO)
+                self._FIFO = None
+                    
+            except Exception, e:
+                print(e)
                 
+            self._KeepReading = False
+            self._ReadingThread.join()
+            self._ReadingThread = None
             
             self.ReturnToUpLevelPage()
             self._Screen.Draw()
@@ -308,25 +279,138 @@ class MPDSpectrumPage(Page):
             
         if event.key == CurKeys["Enter"]:
             pass
+
         
     def Draw(self):
         self.ClearCanvas()
-
+        self._frames+=1
+        
         bw = 10
+        gap = 2
+        margin_bottom = 72
+
         spects = None
+        meterNum =  self._Width / float(bw +gap )  ## 320/12= 26
+        meter_left = meterNum - int(meterNum)
+        meter_left = meter_left*int(bw+gap)
+        margin_left = meter_left / 2 + gap
+        meterNum = int(meterNum)
+        
+        self._cwp_png.NewCoord(43,159)
+        self._cwp_png.Draw()
+
+        if self._Neighbor != None:
+            if self._Neighbor._CurSongName != "":
+                self._song_title.SetText(self._Neighbor._CurSongName)
+            if self._Neighbor._CurSongTime != "":
+                times = self._Neighbor._CurSongTime
+                times_ = times.split(":")
+                if len(times_)> 1:
+                    cur = int(times_[0])
+                    end = int(times_[1])
+                    if cur > 3600:
+                        cur_text = time.strftime('%H:%M:%S', time.gmtime(cur))
+                    else:
+                        cur_text = time.strftime('%M:%S', time.gmtime(cur))
+
+                    if end > 3600:
+                        end_text = time.strftime('%H:%M:%S', time.gmtime(end))
+                    else:
+                        end_text = time.strftime('%M:%S', time.gmtime(end))
+                else:
+                    cur_text = ""
+                    end_text = times
+                
+                self._time2.SetText(cur_text+"-"+end_text)
+                
+                
+        self._title.NewCoord(90,167)
+        self._title.Draw()
+
+        self._time.NewCoord(90,140)
+        self._time.Draw()
+
+        self._time2.NewCoord(135,140)
+        self._time2.Draw()
+    
+        if self._RollCanvas != None:
+#            self._RollCanvas.fill((111,22,33))
+            self._RollCanvas.fill((0,0,0))
+            if self._song_title._Width > self._RollW:
+                if (self._song_title._PosX + self._song_title._Width) > self._RollW and self._frames % 30 == 0:
+                    self._song_title._PosX -= 1
+                elif (self._song_title._PosX + self._song_title._Width) <= self._RollW and self._frames % 30 == 0:
+                    self._song_title._PosX  = 0
+            else:
+                self._song_title._PosX = 0
+            
+            self._song_title.Draw()
+            
+            self._CanvasHWND.blit(self._RollCanvas,(135,165,self._RollW,self._RollH))
+        
+        
         try:
-            spects = self._Queue.get_nowait() ## last element is rms
-#            print("get_nowait: " , spects)
+            spects = self._queue_data
+            if len(spects) == 0:
+                return
+#            print("spects:",spects)
+            step = int( round( len( spects ) / meterNum) )
+
+            self._bbs = []
+
+            for i in range(0,meterNum):
+                index = int(i*step)
+                total = 0
+                
+                value = spects[index]
+                self._bbs.append(value)
+            
+            if len(self._bby) < len(self._bbs):
+                self._bby = self._bbs
+            elif len(self._bby) == len(self._bbs):
+                for i in range(0,len(self._bbs)):
+                    self._bby[i] = (self._bby[i]+self._bbs[i])/2
+                    
+            self.SgsSmooth()
+            
+            for i in range(0,meterNum):
+                value = self._bby[ i ]
+                if math.isnan(value) or math.isinf(value):
+                    value = 0
+
+                value = value/32768.0
+                value = value * 100
+                value = value %  (self._Height-gap-margin_bottom)
+                
+                if len(self._vis_values) < len(self._bby):
+                    self._vis_values.append(value)
+                elif len(self._vis_values) == len(self._bby):
+                    if self._vis_values[i] < value:
+                        self._vis_values[i] = value
+
+
         except Empty:
             return
         else: # got line
-            if len(spects) == 0:
+            if len(self._vis_values) == 0:
                 return
-            w = self._Width / len( spects[0:-1] )
-            left_margin = (w-bw)/2
-            for i,v in enumerate(spects[0:-1]):
-                pygame.draw.rect(self._CanvasHWND,self._Color,(i*w+left_margin,self._Height-v,bw,v),0)
-        
+
+            for i in range(0,meterNum):
+                value = self._vis_values[i]
+         
+                if len(self._capYPositionArray) < round(meterNum):
+                    self._capYPositionArray.append(value)
+
+                if value < self._capYPositionArray[i]:
+                    self._capYPositionArray[i]-=0.5
+                else:
+                    self._capYPositionArray[i] = value
+
+                pygame.draw.rect(self._CanvasHWND,(255,255,255),(i*(bw+gap)+margin_left,self._Height-gap-self._capYPositionArray[i]-margin_bottom,bw,gap),0)
+                
+                pygame.draw.rect(self._CanvasHWND,(255,255,255),(i*(bw+gap)+margin_left,self._Height-value-gap-margin_bottom,bw,value+gap),0)
+                
+                self._vis_values[i] -= 2       
 
 
     

+ 1 - 1
Menu/GameShell/Music Player/pages.py

@@ -34,5 +34,5 @@ def InitListPage(main_screen):
 def InitSpectrumPage(main_screen):
     myvars.SpectrumPage = MPDSpectrumPage()
     myvars.SpectrumPage._Screen = main_screen
-    myvars.SpectrumPage._Name   = "Spectrum"
+    myvars.SpectrumPage._Name   = "GameShell RTA"
     myvars.SpectrumPage.Init()

+ 7 - 0
Menu/GameShell/Music Player/play_list_page.py

@@ -71,6 +71,8 @@ class PlayListPage(Page):
     _BGheight = 70
 
     _Scrolled = 0
+
+    _CurSongName = ""
     
     def __init__(self):
         self._Icons = {}
@@ -126,6 +128,7 @@ class PlayListPage(Page):
         
         current_song = myvars.Poller.poll()
         
+        
         for i ,v in enumerate(self._MyList):
             self._MyList[i]._Playing = False
             self._MyList[i]._PlayingProcess = 0
@@ -134,6 +137,8 @@ class PlayListPage(Page):
             if "song" in current_song:
                 posid = int(current_song["song"])
                 if posid < len(self._MyList): # out of index
+                    self._CurSongName = self._MyList[posid]._Text
+                    
                     if "state" in current_song:
                         if current_song["state"] == "stop":
                             self._MyList[posid]._Playing = False
@@ -244,6 +249,8 @@ class PlayListPage(Page):
         if event.key == CurKeys["A"] or event.key == CurKeys["Menu"]:
             if myvars.Poller != None:
                 myvars.Poller.stop()
+                self._CurSongTime=""
+                self._CurSongName=""
             
             self.ReturnToUpLevelPage()
             self._Screen.Draw()

BIN
skin/default/sys.py/gameshell/icons/tape.png