Changeset 1502

Show
Ignore:
Timestamp:
Tue May 6 21:49:54 2008
Author:
laszlop
Message:

Move the levels out of the project file and into their own *.leveldata files.
These files will resize in their own levels/ directory inside the project folder.
Each leveldata file is just a dump of a few 4-byte integer arrays from memory one after another.
The loading and saving as well as endianness detection is done in the new LevelsList?.py file.

Many things do not work including splitting, fades, and anything else that depended on the old levels format.
However at this point you can load your old 0.9 project files, and all the audio tracks will be reloaded.
Then when you save everything will be converted to the new format with levels in separate files.

Splitting, etc should be simple to implementing using the find_endtime_index() method on the LevelsList? class.
Simply give it a time in milliseconds and it will tell you where to split the list.

Additionally the draw method in EventViewer? has been partially rewritten to use the new endtime data in the LevelsList?.
List allows much more accurate waveforms, especially for Oggs which do not give evenly spaced samples.

Files:

Legend:

Unmodified
Added
Removed
Modified
  • JonoEdit/trunk/Jokosher/ProjectManager.py

    r1501 r1502  
    11 11  
    12 12 import urlparse, os, gzip, shutil, gst  
    13   import Globals, Utils, UndoSystem  
      13 import Globals, Utils, UndoSystem, LevelsList  
    13 13 import Project, Instrument, Event  
    14 14 import xml.dom.minidom as xml  
     
    59 59                 raise CreateProjectError(2)  
    60 60         else:  
    61                   audio_dir = os.path.join(projectdir, "audio")  
    62 61                 try:  
    63 62                         os.mkdir(projectdir)  
    64                           os.mkdir(audio_dir)  
      63                         os.mkdir(project.audio_path)  
      64                         os.mkdir(project.levels_path)  
    65 65                 except:  
    66 66                         raise CreateProjectError(3)  
     
    249 249                                         event._Event__fadePointsDict[pos] = value  
    250 250                  
    251                   try:     
    252                           levelsXML = xmlNode.getElementsByTagName("Levels")[0]  
    253                   except IndexError:  
    254                           Globals.debug("No event levels in project file")  
    255                           event.GenerateWaveform()  
    256                   else:  
    257                           if levelsXML.nodeType == xml.Node.ELEMENT_NODE:  
    258                                   value = str(levelsXML.getAttribute("value"))  
    259                                   event.levels = map(float, value.split(","))  
    260                    
    261                   if event.isLoading:  
    262                           event.GenerateWaveform()  
    263    
      251                 event.GenerateWaveform()  
    264 252                 event._Event__UpdateAudioFadePoints()  
    265 253                 event.CreateFilesource()  
     
    416 404  
    417 405                 if not isDead:  
    418                           if event.isLoading or event.isRecording:  
    419                                   event.GenerateWaveform()  
      406                         #if event.isLoading or event.isRecording:  
      407                         # we have to always generate waveform because 0.10 uses different levels format  
      408                         event.GenerateWaveform()  
    420 409                         event._Event__UpdateAudioFadePoints()  
    421 410                         event.CreateFilesource()  
     
    510 499 class _LoadZPTenFile(_LoadZPNFile):  
    511 500         LOADING_VERSION = "0.10"  
      501          
      502         def LoadEvent(self, event, xmlNode, isDead=False):  
      503                 """  
      504                 Restores an Event from its version 0.10 XML representation.  
      505                  
      506                 Parameters:  
      507                         event -- the Event instance to apply loaded properties to.  
      508                         xmlNode -- the XML node to retreive data from.  
      509                 """  
      510                 params = xmlNode.getElementsByTagName("Parameters")[0]  
      511                  
      512                 Utils.LoadParametersFromXML(event, params)  
      513                  
      514                 if not os.path.isabs(event.file):  
      515                         # If there is a relative path for event.file, assume it is in the audio dir  
      516                         event.file = os.path.join(self.project.audio_path, event.file)  
      517                  
      518                 try:  
      519                         xmlPoints = xmlNode.getElementsByTagName("FadePoints")[0]  
      520                 except IndexError:  
      521                         Globals.debug("Missing FadePoints in Event XML")  
      522                 else:  
      523                         event._Event__fadePointsDict = Utils.LoadDictionaryFromXML(xmlPoints)  
      524  
      525                 if not isDead:  
      526                         levels_path = os.path.join(self.project.levels_path, event.levels_file)  
      527                         try:  
      528                                 event.levels_list.fromfile(levels_path)  
      529                         except LevelsList.CorruptFileError:  
      530                                 Globals.debug("Cannot load levels from file", levels_path)  
      531                         if not event.levels_list:  
      532                                 event.GenerateWaveform()  
      533                         event._Event__UpdateAudioFadePoints()  
      534                         event.CreateFilesource()  
      535          
      536         #_____________________________________________________________________  
    512 537  
    513 538 #=========================================================================  
  • JonoEdit/trunk/Jokosher/EventViewer.py

    r1490 r1502  
    16 16 from Project import Project  
    17 17 import Utils  
    18   import os  
      18 import os, sys  
    18 18 import gettext  
    19 19 _ = gettext.gettext  
    20 20 import Globals  
      21 import itertools  
    21 22  
    22 23 #=========================================================================  
     
    352 353                 context.fill()  
    353 354                  
    354                   if self.event.levels and (self.event.duration or self.event.loadingLength):  
      355                 if self.event.levels_list and (self.event.duration or self.event.loadingLength):  
    354 355                         if self.event.loadingLength:  
    355                                   scale = (self.event.loadingLength * self.project.viewScale) / float(len(self.event.levels))  
      356                                 duration = self.event.loadingLength  
    355 356                         else:  
    356                                   scale = (self.event.duration * self.project.viewScale) / float(len(self.event.levels))  
      357                                 duration = self.event.duration  
    356 357                          
    357                           # Draw waveform  
    358                           x_pos = int(rect.x/scale)  
    359                           x = 0  
    360                           skipFactor = max(int(self._MIN_POINT_SEPARATION / scale), 1)  
    361 358                         context.move_to(0,rect.height)  
    362                                            
      359                          
    362 359                         # get levels list  
    363                           fadedLevels = self.event.GetFadeLevels()  
      360                         #fadedLevels = self.event.GetFadeLevels()  
      361                          
      362                         levels = self.event.levels_list  
      363                         length = len(levels)  
    364 364                          
    365                           for peak in fadedLevels[x_pos::skipFactor]:  
    366                                   x = (x_pos * scale) - rect.x  
    367                                   peakOnScreen = int(peak * rect.height)  
      365                         # time offset of the start of the drawing area in milliseconds  
      366                         starting_time = int(rect.x / self.project.viewScale * 1000)  
      367                         starting_index = levels.find_endtime_index(starting_time)  
      368  
      369                         last_x = -2  
      370                         skip_list = []  
      371                         iterator = itertools.islice(levels, starting_index, length)  
      372                         for endtime, peak in iterator:  
      373                                 x = int((endtime - starting_time) * self.project.viewScale / 1000)  
      374                                  
      375                                 peakOnScreen = int(peak * rect.height / sys.maxint)  
      376                                 skip_list.append(peakOnScreen)  
      377                                 if (x - last_x) < self._MIN_POINT_SEPARATION:  
      378                                         continue  
      379                                  
      380                                 peakOnScreen = sum(skip_list) / len(skip_list)  
    368 381                                 context.line_to(x, rect.height - peakOnScreen)  
    369 382                                  
      383                                 skip_list = []  
      384                                 last_x = x  
    370 385                                 if x > rect.width:  
    371 386                                         break  
    372                                   x_pos += skipFactor  
    373 387                          
    374 388                         context.line_to(x, rect.height)  
     
    1177 1191                         self.redrawWaveform = True  
    1178 1192                         self.queue_resize()  
    1179                           self.last_num_levels = len(self.event.levels)  
    1180 1193                         self.currentScale = self.project.viewScale  
    1181 1194                         self.queue_draw()  
  • JonoEdit/trunk/Jokosher/Event.py

    r1501 r1502  
    18 18 pygst.require("0.10")  
    19 19 import gst, gobject  
    20   import Utils  
      20 import Utils, LevelsList  
    20 20 import UndoSystem  
    21 21 import Globals  
     
    90 90                  
    91 91                 self.selection  = [0, 0]        # List start and end of selection (for fades, etc) measured in seconds  
    92                   self.levels = []                        # Array of audio levels to be drawn for this event  
      92                 self.levels_list = LevelsList.LevelsList()      # LevelsList class containing array of audio levels to be drawn for this event  
    92 92                  
    93 93                 self.id = instrument.project.GenerateUniqueID(id)  #check is id is already taken, then set it.  
     
    224 224                 Utils.StoreDictionaryToXML(doc, xmlPoints, self.__fadePointsDict, "FadePoint")  
    225 225                  
    226                   if self.levels:  
    227                           levelsXML = doc.createElement("Levels")  
    228                           ev.appendChild(levelsXML)  
    229                           stringList = map(str, self.levels)  
    230                           levelsXML.setAttribute("value", ",".join(stringList))  
      226                 if self.levels_list:  
      227                         self.levels_list.tofile(os.path.join(self.instrument.project.levels_path, self.levels_file))  
    231 228                  
    232 229         #_____________________________________________________________________  
     
    580 577  
    581 578                 elif st.get_name() == "level":  
    582                           newLevel = self.__CalculateAudioLevel(st["peak"])  
    583                           self.levels.append(newLevel)  
    584                            
    585                           end = st["endtime"] / float(gst.SECOND)  
    586                           self.loadingLength = int(end)  
      579                         end = st["endtime"]  
      580                         self.levels_list.append(end, st["peak"])  
      581                         self.loadingLength = int(end / gst.SECOND)  
    587 582                          
    588 583                         # Only send events every second processed to reduce GUI load  
     
    722 717                 self.bus.connect("message::error", self.bus_error)  
    723 718  
    724                   self.levels = []  
      719                 self.levels_list = LevelsList.LevelsList()  
    724 719                 self.isLoading = True  
    725 720                 self.emit("loading")  
     
    752 747                 self.bus.connect("message::error", self.bus_error)  
    753 748  
    754                   self.levels = []  
      749                 self.levels_list = LevelsList.LevelsList()  
    754 749                 self.isLoading = True  
    755 750                 self.emit("loading")  
     
    809 804                 st = message.structure  
    810 805                 if st and message.src.get_name() == "recordlevel":  
    811                           newLevel = self.__CalculateAudioLevel(st["peak"])  
    812                           self.levels.append(newLevel)  
      806                         self.levels_list.append(st["endtime"],  st["peak"])  
    813 807                          
    814 808                         end = st["endtime"] / float(gst.SECOND)  
     
    824 818         #_____________________________________________________________________  
    825 819          
    826           def __CalculateAudioLevel(self, channelLevels):  
    827                   """  
    828                   Calculates an average for all channel levels.  
    829                    
    830                   Parameters:  
    831                           channelLevels -- list of levels from each channel.  
    832                            
    833                   Returns:  
    834                           an average level, also taking into account negative infinity numbers,  
    835                           which will be discarded in the average.  
    836                   """  
    837                   negInf = float("-inf")  
    838                   peaktotal = 0  
    839                   peakcount = 0  
    840                   for peak in channelLevels:  
    841                           #don't add -inf values cause 500 + -inf is still -inf  
    842                           if peak != negInf:  
    843                                   peaktotal += peak  
    844                                   peakcount += 1  
    845                   #avoid a divide by zero here  
    846                   if peakcount > 0:  
    847                           peaktotal /= peakcount  
    848                   #it must be put back to -inf if nothing has been added to it, so that the DbToFloat conversion will work  
    849                   elif peakcount == 0:  
    850                           peaktotal = negInf  
    851                    
    852                   #convert to 0...1 float, and return  
    853                   return Utils.DbToFloat(peaktotal)  
    854                    
    855           #_____________________________________________________________________  
    856            
    857 820         def SetSelected(self, sel):  
    858 821                 """  
  • JonoEdit/trunk/Jokosher/Project.py

    r1501 r1502  
    38 38          
    39 39         """ The Project structure version. Will be useful for handling old save files. """  
    40           Globals.VERSION = "1.0"  
      40         Globals.VERSION = "0.10"  
    40 40          
    41 41         """ The audio playback state enum values """  
     
    656 656                                 raise "No save path specified!"  
    657 657                         path = self.projectfile  
    658                            
      658                  
      659                 if not self.audio_path:  
      660                         self.audio_path = os.path.join(os.path.dirname(path), "audio")  
      661                 if not self.levels_path:  
      662                         self.levels_path = os.path.join(os.path.dirname(path), "levels")  
      663                          
      664                 if os.path.exists(self.audio_path):  
      665                         if not os.path.isdir(self.audio_path):  
      666                                 raise "Audio save location is not a directory"  
      667                 else:  
      668                         os.mkdir(self.audio_path)  
      669                  
      670                 if os.path.exists(self.levels_path):  
      671                         if not os.path.isdir(self.levels_path):  
      672                                 raise "Levels save location is not a directory"  
      673                 else:  
      674                         os.mkdir(self.levels_path)  
      675                  
    659 676                 if not path.endswith(".jokosher"):  
    660 677                         path = path + ".jokosher"  
     
    680 697                 head.appendChild(params)  
    681 698                  
    682                   items = ["viewScale", "viewStart", "name", "author", "project_audio_path",  "project_levels_path",  
      699                 items = ["viewScale", "viewStart", "name", "author", "audio_path",  "levels_path",  
    682 699                               "transportMode", "bpm", "meter_nom", "meter_denom"]  
    683 700