def search_channel_speech(channel, winlenght=0.010, minsildur=0.200, mintrackdur=0.300, shiftdurstart=0.010, shiftdurend=0.010 ): """ Return a list of tracks (i.e. speech intervals where energy is high enough). Use only default parameters. @param channel (Channel - IN) The channel we'll try to find tracks @return A list of tuples (fromtime,totime) """ chansil = ChannelSilence( channel, winlenght ) chansil.search_silences( threshold=0, mintrackdur=0.08 ) chansil.filter_silences( minsildur ) tracks = chansil.extract_tracks( mintrackdur, shiftdurstart, shiftdurend ) tracks.append( (channel.get_nframes(),channel.get_nframes()) ) trackstimes = frames2times(tracks, channel.get_framerate()) return trackstimes
class IPUsAudio( object ): """ @author: Brigitte Bigi @organization: Laboratoire Parole et Langage, Aix-en-Provence, France @contact: [email protected] @license: GPL, v3 @copyright: Copyright (C) 2011-2016 Brigitte Bigi @summary: An IPUs segmenter from audio. IPUs - Inter-Pausal Units are blocks of speech bounded by silent pauses of more than X ms, and time-aligned on the speech signal. """ MIN_SIL_DUR = 0.08 MIN_IPU_DUR = 0.08 def __init__(self, channel): """ Creates a new IPUsAudio instance. """ super(IPUsAudio, self).__init__() self.reset() self.set_channel(channel) # ------------------------------------------------------------------ def reset(self): """ Set default values. """ self.min_sil_dur = 0.250 self.min_ipu_dur = 0.300 self.vol_threshold = 0 self.shift_start = 0.010 self.shift_end = 0.020 self.win_lenght = 0.020 self.auto_vol = True self.bornestart = False self.borneend = False # ------------------------------------------------------------------ # Manage Channel # ------------------------------------------------------------------ def get_channel(self): """ Return the channel. """ return self.chansil.get_channel() # ------------------------------------------------------------------ def set_channel(self, channel): """ Set a new Channel. """ if channel is not None: self.chansil = ChannelSilence( channel, self.win_lenght ) else: self.chansil = None # ------------------------------------------------------------------ def reset_silences(self): """ Reset the list of silences. """ if self.chansil is not None: self.chansil.reset_silences() # ------------------------------------------------------------------ def set_silences(self, silences): """ Fix the list of silences. """ if self.chansil is not None: self.chansil.set_silences( silences ) # ------------------------------------------------------------------ # Setters for members # ------------------------------------------------------------------ def set_vol_threshold(self, vol_threshold): """ Fix the default minimum volume value to find silences. @param vol_threshold (int) RMS value """ self.vol_threshold = int(vol_threshold) if vol_threshold == 0: self.auto_vol = True else: self.auto_vol = False # ------------------------------------------------------------------ def set_min_silence(self, min_sil_dur): """ Fix the default minimum duration of a silence. @param min_sil_dur (float) Duration in seconds. """ self.min_sil_dur = float(min_sil_dur) # ------------------------------------------------------------------ def set_min_speech(self, min_ipu_dur): """ Fix the default minimum duration of an IPU. @param min_ipu_dur (float) Duration in seconds. """ self.min_ipu_dur = float(min_ipu_dur) # ------------------------------------------------------------------ def set_vol_win_lenght(self, winlength): """ Fix the default windows length for RMS estimations. @param winlength (float) Duration in seconds. """ self.win_lenght = max(winlength, 0.005) # ------------------------------------------------------------------ def set_shift(self, s): """ Fix the default minimum boundary shift value. @param s (float) Duration in seconds. """ self.shift_start = float(s) self.shift_end = float(s) # ------------------------------------------------------------------ def set_shift_start(self, s): """ Fix the default minimum boundary shift value. @param s (float) Duration in seconds. """ self.shift_start = float(s) # ------------------------------------------------------------------ def set_shift_end(self,s): """ Fix the default minimum boundary shift value. @param s (float) Duration in seconds. """ self.shift_end = float(s) # ------------------------------------------------------------------ def min_channel_duration(self): """ Return the minimum duration we expect for a channel. """ d1 = self.min_sil_dur+self.shift_start+self.shift_end d2 = self.min_ipu_dur+self.shift_start+self.shift_end return max(d1,d2) # ------------------------------------------------------------------ def set_bound_start(self, sil=False): """ Fix if it is expected (or not) to find a silence at the beginning of the channel. """ self.bornestart = sil # ------------------------------------------------------------------ def set_bound_end(self, sil=False): """ Fix if it is expected (or not) to find a silence at the end of the channel. """ self.borneend = sil # ------------------------------------------------------------------ # Silence/Speech segmentation # ------------------------------------------------------------------ def extract_tracks(self, min_ipu_dur=None, shift_start=None, shift_end=None): """ Return a list of tuples (from_pos,to_pos) of tracks. The tracks are found from the current list of silences. @param min_ipu_dur (float) The minimum duration for a track (in seconds) @param shiftdurstart (float) The time to remove to the start boundary (in seconds) @param shiftdurend (float) The time to add to the end boundary (in seconds) @return (list of tuples) """ if self.chansil is None: return [] if min_ipu_dur is None: min_ipu_dur=self.min_ipu_dur if shift_start is None: shift_start=self.shift_start if shift_end is None: shift_end=self.shift_end return self.chansil.extract_tracks(min_ipu_dur, shift_start, shift_end) # ------------------------------------------------------------------ def search_tracks(self, volume): """ Return the tracks if volume is used as threshold. """ if self.chansil is None: return [] self.chansil.search_silences(volume, mintrackdur=IPUsAudio.MIN_IPU_DUR) self.chansil.filter_silences(self.min_sil_dur) return self.extract_tracks() # ------------------------------------------------------------------ def check_boundaries(self, tracks): """ Check if silences at start and end are as expected. @return bool """ if len(tracks) == 0: return False if self.chansil is None: return False if self.bornestart is False and self.borneend is False: # we do not know anything about silences at start and end # then, everything is ALWAYS OK! return True first_from_pos = tracks[0][0] last_to_pos = tracks[len(tracks)-1][1] # If I expected a silence at start... and I found a track if self.bornestart is True and first_from_pos==0: return False # If I expected a silence at end... and I found a track if self.borneend is True and last_to_pos==self.chansil.get_channel().get_nframes(): return False return True # ------------------------------------------------------------------ def split_into_vol(self, nbtracks): """ Try various volume values to estimate silences then get the expected number of tracks. @param nbtracks is the expected number of IPUs @return number of tracks """ if self.chansil is None: return 0 volstats = self.chansil.get_volstats() # Min volume in the speech vmin = volstats.min() # Max is set to the mean vmax = volstats.mean() # Step is necessary to not exaggerate a detailed search! # step is set to 5% of the volume between min and mean. step = int( (vmax - vmin) / 20.0 ) # Min and max are adjusted vmin += step vmax -= step # First Test !!! self.vol_threshold = vmin tracks = self.search_tracks(vmin) n = len(tracks) b = self.check_boundaries(tracks) while (n != nbtracks or b is False): # We would never be done anyway. if (vmax==vmin) or (vmax-vmin) < step: return n # Try with the middle volume value vmid = int(vmin + (vmax - vmin) / 2.0) if n > nbtracks: # We split too often. Need to consider less as silence. vmax = vmid elif n < nbtracks: # We split too seldom. Need to consider more as silence. vmin = vmid else: # We did not find start/end silence. vmin += step # Find silences with these parameters self.vol_threshold = int(vmid) tracks = self.search_tracks(vmid) n = len(tracks) b = self.check_boundaries(tracks) return n # ------------------------------------------------------------------ def split_into(self, nbtracks): """ Try various volume values, pause durations and silence duration to get silences. @param nbtracks is the expected number of IPUs. 0=auto. """ if self.chansil is None: raise Exception('No audio data.') if self.auto_vol is True: self.vol_threshold = self.chansil.search_threshold_vol() if nbtracks == 0: self.search_tracks( self.vol_threshold ) return 0 # Try with default parameters: tracks = self.search_tracks( self.vol_threshold ) n = len(tracks) b = self.check_boundaries(tracks) if n == nbtracks and b is True: return n # Try with default lengths (change only volume): n = self.split_into_vol( nbtracks ) if n > nbtracks: # We split too often. Try with larger' values. while n > nbtracks: self.min_sil_dur += self.win_lenght self.min_ipu_dur += self.win_lenght n = self.split_into_vol( nbtracks ) elif n < nbtracks: # We split too seldom. Try with shorter' values of silences p = self.min_sil_dur m = self.min_ipu_dur while n < nbtracks and self.min_sil_dur > IPUsAudio.MIN_SIL_DUR: self.min_sil_dur -= self.win_lenght n = self.split_into_vol( nbtracks ) # we failed... try with shorter' values of ipus if n < nbtracks: self.min_sil_dur = p while n < nbtracks and self.min_ipu_dur > IPUsAudio.MIN_IPU_DUR: self.min_ipu_dur -= self.win_lenght n = self.split_into_vol( nbtracks ) # we failed... try with shorter' values of both sil/ipus if n < nbtracks: self.min_ipu_dur = m while n < nbtracks and self.min_sil_dur > IPUsAudio.MIN_SIL_DUR and self.min_ipu_dur > IPUsAudio.MIN_IPU_DUR: self.min_ipu_dur -= self.win_lenght self.min_sil_dur -= self.win_lenght n = self.split_into_vol( nbtracks ) return n
class AudioRoamerPanel( wx.Panel ): """ @author: Brigitte Bigi @organization: Laboratoire Parole et Langage, Aix-en-Provence, France @contact: [email protected] @license: GPL, v3 @copyright: Copyright (C) 2011-2016 Brigitte Bigi @summary: Display info about a channel. Allows to save. This panel display all information about a channel: - amplitudes: nb of frames, min/max values, zero crossing, - clipping rates - volumes: min/max/mean - silence/speech automatic segmentation. Methods allow to save: - the channel or a fragment of the channel, in an audio file; - the information in a text file. """ FRAMERATES = [ "16000", "32000", "48000" ] SAMPWIDTH = [ "8", "16", "32" ] INFO_LABELS = {"framerate":(" Frame rate (Hz): ",FRAMERATES[0]), "sampwidth":(" Samp. width (bits): ",SAMPWIDTH[0]), "mul": (" Multiply values by: ","1.0"), "bias": (" Add bias value: ","0"), "offset": (" Remove offset value: ",False), "nframes": (" Number of frames: "," ... "), "minmax": (" Min/Max values: "," ... "), "cross": (" Zero crossings: "," ... "), "volmin": (" Volume min: "," ... "), "volmax": (" Volume max: "," ... "), "volavg": (" Volume mean: "," ... "), "volsil": (" Threshold volume: "," ... "), "nbipus": (" Number of IPUs: "," ... "), "duripus": (" Nb frames of IPUs: "," ... ") } def __init__(self, parent, preferences, channel): """ Create a new AudioRoamerPanel instance. @param parent (wxWindow) @param preferences (structs.Preferences) @param channel (audiodata.Channel) """ wx.Panel.__init__(self, parent) self._channel = channel # Channel self._filename = None # Fixed when "Save as" is clicked self._cv = None # ChannelSilence, fixed by ShowInfos self._tracks = None # the IPUs we found automatically self._ca = None # AudioFrames with only this channel, fixed by ShowInfos self._wxobj = {} # Dict of wx objects sizer = self._create_content() self.MODIFIABLES = {} for key in ["framerate","sampwidth","mul","bias","offset"]: self.MODIFIABLES[key] = AudioRoamerPanel.INFO_LABELS[key][1] self.SetPreferences(preferences) self.SetSizer(sizer) self.SetAutoLayout( True ) self.SetMinSize((MIN_PANEL_W,MIN_PANEL_H)) self.Layout() # ----------------------------------------------------------------------- # Private methods to show information about the channel into the GUI. # ----------------------------------------------------------------------- def _create_content(self): """ Create the main sizer, add content then return it. """ sizer = wx.BoxSizer(wx.HORIZONTAL) info = self._create_content_infos() clip = self._create_content_clipping() ipus = self._create_content_ipus() sizer.AddSpacer(5) sizer.Add(info, 1, wx.EXPAND, 0) sizer.AddSpacer(10) sizer.Add(clip, 0, wx.ALL, 0) sizer.AddSpacer(10) sizer.Add(ipus, 1, wx.EXPAND, 0) sizer.AddSpacer(5) return sizer def _create_content_infos(self): """ GUI design for amplitude and volume information. """ gbs = wx.GridBagSizer(10, 2) static_tx = wx.StaticText(self, -1, "Amplitude values: ") gbs.Add(static_tx, (0,0), (1,2), flag=wx.LEFT, border=2) self._wxobj["titleamplitude"] = (static_tx,None) self.__add_info(self, gbs, "nframes", 1) self.__add_info(self, gbs, "minmax", 2) self.__add_info(self, gbs, "cross", 3) static_tx = wx.StaticText(self, -1, "") gbs.Add(static_tx, (4,0), (1,2), flag=wx.LEFT, border=2) cfm = wx.ComboBox(self, -1, choices=AudioRoamerPanel.FRAMERATES, style=wx.CB_READONLY) cfm.SetMinSize((120,24)) self.__add_modifiable(self, gbs, cfm, "framerate", 5) self.Bind(wx.EVT_COMBOBOX, self.OnModif, cfm) csp = wx.ComboBox(self, -1, choices=AudioRoamerPanel.SAMPWIDTH, style=wx.CB_READONLY) csp.SetMinSize((120,24)) self.__add_modifiable(self, gbs, csp, "sampwidth", 6) self.Bind(wx.EVT_COMBOBOX, self.OnModif, csp) txm = wx.TextCtrl(self, -1, AudioRoamerPanel.INFO_LABELS["mul"][1], validator=TextAsNumericValidator()) txm.SetInsertionPoint(0) self.__add_modifiable(self, gbs, txm, "mul", 7) self.Bind(wx.EVT_TEXT_ENTER , self.OnModif, txm) txb = wx.TextCtrl(self, -1, AudioRoamerPanel.INFO_LABELS["bias"][1], validator=TextAsNumericValidator()) txb.SetInsertionPoint(0) self.__add_modifiable(self, gbs, txb, "bias", 8) self.Bind(wx.EVT_TEXT_ENTER, self.OnModif, txb) cb = wx.CheckBox(self, -1, style=wx.NO_BORDER) cb.SetValue( AudioRoamerPanel.INFO_LABELS["offset"][1] ) self.__add_modifiable(self, gbs, cb, "offset", 9) self.Bind(wx.EVT_CHECKBOX, self.OnModif, cb) gbs.AddGrowableCol(1) border = wx.BoxSizer() border.Add(gbs, 1, wx.ALL | wx.EXPAND, 10) return border def _create_content_clipping(self): """ GUI design for clipping information. """ gbs = wx.GridBagSizer(11, 2) static_tx = wx.StaticText(self, -1, "Clipping rates:") gbs.Add(static_tx, (0,0), (1,2), flag=wx.LEFT, border=2) self._wxobj["titleclipping"] = (static_tx,None) for i in range(1,10): self.__add_clip(self, gbs, i) border = wx.BoxSizer() border.Add(gbs, 1, wx.ALL | wx.EXPAND, 10) return border def _create_content_ipus(self): """ GUI design for information about an IPUs segmentation... """ gbs = wx.GridBagSizer(9, 2) static_tx = wx.StaticText(self, -1, "Root-mean square:") gbs.Add(static_tx, (0,0), (1,2), flag=wx.LEFT, border=2) self._wxobj["titlevolume"] = (static_tx,None) self.__add_info(self, gbs, "volmin", 1) self.__add_info(self, gbs, "volmax", 2) self.__add_info(self, gbs, "volavg", 3) static_tx = wx.StaticText(self, -1, "") gbs.Add(static_tx, (4, 0), (1,2), flag=wx.LEFT, border=2) static_tx = wx.StaticText(self, -1, "Automatic detection of silences:") gbs.Add(static_tx, (5, 0), (1,2), flag=wx.LEFT, border=2) self._wxobj["titleipus"] = (static_tx,None) self.__add_info(self, gbs, "volsil", 6) self.__add_info(self, gbs, "nbipus", 7) self.__add_info(self, gbs, "duripus", 8) border = wx.BoxSizer() border.Add(gbs, 1, wx.ALL | wx.EXPAND, 10) return border # ----------------------------------------------------------------------- # Callbacks to events # ----------------------------------------------------------------------- def OnModif(self, evt): """ Callback on a modifiable object: adapt foreground color. """ evtobj = evt.GetEventObject() evtvalue = evtobj.GetValue() for (key,defaultvalue) in self.MODIFIABLES.iteritems(): (tx,obj) = self._wxobj[key] if evtobj == obj: if evtvalue == defaultvalue: obj.SetForegroundColour( self._prefs.GetValue('M_FG_COLOUR') ) tx.SetForegroundColour( self._prefs.GetValue('M_FG_COLOUR') ) else: obj.SetForegroundColour( INFO_COLOUR ) tx.SetForegroundColour( INFO_COLOUR ) obj.Refresh() tx.Refresh() return # ----------------------------------------------------------------------- # Setters for GUI # ---------------------------------------------------------------------- def SetPreferences(self, prefs): """ Set new preferences. Refresh GUI. """ self._prefs = prefs self.SetFont( prefs.GetValue('M_FONT') ) self.SetBackgroundColour( prefs.GetValue('M_BG_COLOUR') ) self.SetForegroundColour( prefs.GetValue('M_FG_COLOUR') ) self.Refresh() # ---------------------------------------------------------------------- def SetFont(self, font): """ Change font of all wx texts. """ wx.Window.SetFont( self, font ) for (tx,obj) in self._wxobj.values(): tx.SetFont(font) if obj is not None: obj.SetFont(font) else: # a title (make it bold) newfont = wx.Font(font.GetPointSize(), font.GetFamily(), font.GetStyle(), wx.BOLD, False, font.GetFaceName(), font.GetEncoding()) tx.SetFont(newfont) # ---------------------------------------------------------------------- def SetBackgroundColour(self, color): """ Change background of all texts. """ wx.Window.SetBackgroundColour( self, color ) for (tx,obj) in self._wxobj.values(): tx.SetBackgroundColour( color ) if obj is not None: obj.SetBackgroundColour( color ) # ---------------------------------------------------------------------- def SetForegroundColour(self, color): """ Change foreground of all texts. """ wx.Window.SetForegroundColour( self, color ) for (tx,obj) in self._wxobj.values(): tx.SetForegroundColour( color ) if obj is not None: obj.SetForegroundColour( color ) # ---------------------------------------------------------------------- # Methods of the workers # ---------------------------------------------------------------------- def ShowInfo(self): """ Estimate all values then display the information. """ # we never estimated values. we have to do it! if self._cv is None: try: self.SetChannel(self._channel) except Exception as e: ShowInformation(self, self._prefs, "Error: %s"%str(e)) return # Amplitude self._wxobj["nframes"][1].ChangeValue( " "+str(self._channel.get_nframes())+" " ) self._wxobj["minmax"][1].ChangeValue( " "+str(self._ca.minmax())+" " ) self._wxobj["cross"][1].ChangeValue( " "+str(self._ca.cross())+" " ) # Modifiable fm = str(self._channel.get_framerate()) if not fm in AudioRoamerPanel.FRAMERATES: self._wxobj["framerate"][1].Append( fm ) self._wxobj["framerate"][1].SetStringSelection(fm) self.MODIFIABLES["framerate"] = fm sp = str(self._channel.get_sampwidth()*8) if not sp in AudioRoamerPanel.SAMPWIDTH: self._wxobj["sampwidth"][1].Append( sp ) self._wxobj["sampwidth"][1].SetStringSelection( sp ) self.MODIFIABLES["sampwidth"] = sp # Clipping for i in range(1,10): cr = self._ca.clipping_rate( float(i)/10. ) * 100. self._wxobj["clip"+str(i)][1].ChangeValue( " "+str( round(cr,2))+"% ") # Volumes / Silences vmin = self._cv.get_volstats().min() vmax = self._cv.get_volstats().max() vavg = self._cv.get_volstats().mean() self._wxobj["volmin"][1].ChangeValue( " "+str(vmin)+" ("+str(amp2db(vmin))+" dB) " ) self._wxobj["volmax"][1].ChangeValue( " "+str(vmax)+" ("+str(amp2db(vmax))+" dB) " ) self._wxobj["volavg"][1].ChangeValue( " "+str(int(vavg) )+" ("+str(amp2db(vavg))+" dB) ") self._wxobj["volsil"][1].ChangeValue( " "+str(self._cv.search_threshold_vol())+" " ) self._wxobj["nbipus"][1].ChangeValue( " "+str(len(self._tracks))+" " ) d = sum( [(e-s) for (s,e) in self._tracks] ) self._wxobj["duripus"][1].ChangeValue( " "+str(d)+" " ) # ----------------------------------------------------------------------- def SetChannel(self, newchannel): """ Set a new channel, estimates the values to be displayed. """ # Set the channel self._channel = newchannel wx.BeginBusyCursor() b = wx.BusyInfo("Please wait while loading and analyzing data...") # To estimate values related to amplitude frames = self._channel.get_frames(self._channel.get_nframes()) self._ca = AudioFrames(frames, self._channel.get_sampwidth(), 1) # Estimates the RMS (=volume), then find where are silences, then IPUs self._cv = ChannelSilence(self._channel) self._cv.search_silences() # threshold=0, mintrackdur=0.08 self._cv.filter_silences() # minsildur=0.2 self._tracks = self._cv.extract_tracks() # mintrackdur=0.3 b.Destroy() b = None wx.EndBusyCursor() # ----------------------------------------------------------------------- def ApplyChanges(self, from_time=None, to_time=None): """ Return a channel with changed applied. @param from_time (float) @param to_time (float) @return (Channel) new channel or None if nothing changed """ # Get the list of modifiable values from wx objects fm = int(self._wxobj["framerate"][1].GetValue()) sp = int(int(self._wxobj["sampwidth"][1].GetValue())/8) mul = float(self._wxobj["mul"][1].GetValue()) bias = int(self._wxobj["bias"][1].GetValue()) offset = self._wxobj["offset"][1].GetValue() dirty = False if from_time is None: from_frame = 0 else: from_frame = int( from_time * fm ) dirty = True if to_time is None: to_frame = self._channel.get_nframes() else: dirty = True to_frame = int(to_time * fm) channel = self._channel.extract_fragment(from_frame,to_frame) # If something changed, apply this/these change-s to the channel if fm != self._channel.get_framerate() or sp != self._channel.get_sampwidth() or mul != 1. or bias != 0 or offset is True: wx.BeginBusyCursor() b = wx.BusyInfo("Please wait while formatting data...") channelfmt = ChannelFormatter( channel ) channelfmt.set_framerate(fm) channelfmt.set_sampwidth(sp) channelfmt.convert() channelfmt.mul(mul) channelfmt.bias(bias) if offset is True: channelfmt.remove_offset() channel = channelfmt.get_channel() dirty = True b.Destroy() b = None wx.EndBusyCursor() if dirty is True: return channel return None # ----------------------------------------------------------------------- def SaveChannel(self, parentfilename, period=False): """ Save the channel in an audio file. @param parentfilename (str) @param period (bool) Save a portion of the channel only """ s = None e = None if period is True: dlg = PeriodChooser( self, self._prefs, 0., float(self._channel.get_nframes())/float(self._channel.get_framerate()) ) answer = dlg.ShowModal() if answer == wx.ID_OK: (s,e) = dlg.GetValues() try: s = float(s) e = float(e) if e < s: raise Exception except Exception: ShowInformation( self, self._prefs, "Error in the definition of the portion of time.", style=wx.ICON_ERROR) return dlg.Destroy() if answer != wx.ID_OK: return newfilename = SaveAsAudioFile() # If it is the OK response, process the data. if newfilename is not None: if newfilename == parentfilename: ShowInformation( self, self._prefs, "Assigning the current file name is forbidden. Choose a new file name.", style=wx.ICON_ERROR) return # Create a formatted channel try: channel = self.ApplyChanges(s,e) except Exception as e: ShowInformation( self, self._prefs, "Error while formatting the channel: %s"%str(e), style=wx.ICON_ERROR) return message = "File %s saved successfully."%newfilename if channel is None: channel = self._channel else: message +="\nYou can now open it with AudioRoamer to see your changes!" # Save the channel try: audio = AudioPCM() audio.append_channel(channel) audiodata.io.save(newfilename, audio) except Exception as e: message = "File not saved. Error: %s"%str(e) else: # Update members self._filename = newfilename ShowInformation( self, self._prefs, message, style=wx.ICON_ERROR) # ----------------------------------------------------------------------- def SaveInfos(self, parentfilename): """ Ask for a filename then save all displayed information. """ newfilename = SaveAsAnyFile() # If it is the OK response, process the data. if newfilename is not None: content = self._infos_content(parentfilename) with codecs.open(newfilename, "w", encoding) as fp: fp.write(content) # ----------------------------------------------------------------------- # Private methods to list information in a "formatted" text. # ----------------------------------------------------------------------- def _infos_content(self, parentfilename): content = "" content += self.__separator() content += self.__line(program + ' - Version ' + version) content += self.__line(copyright) content += self.__line("Web site: "+ url) content += self.__line("Contact: "+ author + "("+ contact + ")") content += self.__separator() content += self.__newline() content += self.__line("Date: " + str(datetime.datetime.now())) # General information content += self.__section("General information") content += self.__line("Channel filename: %s"%self._filename) content += self.__line("Channel extracted from file: "+parentfilename) content += self.__line("Duration: %s sec."%self._channel.get_duration()) content += self.__line("Framerate: %d Hz"%self._channel.get_framerate()) content += self.__line("Samp. width: %d bits"%(int(self._channel.get_sampwidth())*8)) # Amplitude content += self.__section("Amplitude") content += self.__line(AudioRoamerPanel.INFO_LABELS["nframes"][0]+self._wxobj["nframes"][1].GetValue()) content += self.__line(AudioRoamerPanel.INFO_LABELS["minmax"][0]+self._wxobj["minmax"][1].GetValue()) content += self.__line(AudioRoamerPanel.INFO_LABELS["cross"][0]+self._wxobj["cross"][1].GetValue()) # Clipping content += self.__section("Amplitude clipping") for i in range(1,10): f = self._ca.clipping_rate( float(i)/10. ) * 100. content += self.__item(" factor "+str(float(i)/10.)+": "+str(round(f,2))+"%") # Volume content += self.__section("Root-mean square") content += self.__line(AudioRoamerPanel.INFO_LABELS["volmin"][0]+self._wxobj["volmin"][1].GetValue()) content += self.__line(AudioRoamerPanel.INFO_LABELS["volmax"][0]+self._wxobj["volmax"][1].GetValue()) content += self.__line(AudioRoamerPanel.INFO_LABELS["volavg"][0]+self._wxobj["volavg"][1].GetValue()) # IPUs content += self.__section("Inter-Pausal Units automatic segmentation") content += self.__line(AudioRoamerPanel.INFO_LABELS["volsil"][0]+self._wxobj["volsil"][1].GetValue()) content += self.__line(AudioRoamerPanel.INFO_LABELS["nbipus"][0]+self._wxobj["nbipus"][1].GetValue()) content += self.__line(AudioRoamerPanel.INFO_LABELS["duripus"][0]+self._wxobj["duripus"][1].GetValue()) content += self.__newline() content += self.__separator() return content # ----------------------------------------------------------------------- # Private methods. # ----------------------------------------------------------------------- def __add_info(self, parent, gbs, key, row): """ Private method to add an info into the GridBagSizer. """ static_tx = wx.StaticText(parent, -1, AudioRoamerPanel.INFO_LABELS[key][0]) gbs.Add(static_tx, (row, 0), flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT, border=2) tx = wx.TextCtrl(parent, -1, AudioRoamerPanel.INFO_LABELS[key][1], style=wx.TE_READONLY) tx.SetMinSize((120,24)) gbs.Add(tx, (row, 1), flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT, border=2) self._wxobj[key] = (static_tx,tx) def __add_clip(self, parent, gbs, i): """ Private method to add a clipping value in a GridBagSizer. """ static_tx = wx.StaticText(parent, -1, " factor "+str( float(i)/10.)+": " ) gbs.Add(static_tx, (i, 0), flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT, border=2) tx = wx.TextCtrl(parent, -1, " ... ", style=wx.TE_READONLY|wx.TE_RIGHT) gbs.Add(tx, (i, 1), flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, border=2) self._wxobj["clip"+str(i)] = (static_tx,tx) def __add_modifiable(self, parent, gbs, obj, key, row): static_tx = wx.StaticText(parent, -1, AudioRoamerPanel.INFO_LABELS[key][0]) #static_tx = wx.TextCtrl(parent, -1, AudioRoamerPanel.INFO_LABELS[key][0], style=wx.TE_READONLY|wx.TE_LEFT|wx.NO_BORDER) gbs.Add(static_tx, (row, 0), flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT, border=2) gbs.Add(obj, (row, 1), flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT, border=2) self._wxobj[key] = (static_tx,obj) # ----------------------------------------------------------------------- def __section(self, title): """ Private method to make to look like a title. """ text = self.__newline() text += self.__separator() text += self.__line(title) text += self.__separator() text += self.__newline() return text def __line(self, msg): """ Private method to make a text as a simple line. """ text = msg.strip() text += self.__newline() return text def __item(self, msg): """ Private method to make a text as a simple item. """ text = " - " text += self.__line(msg) return text def __newline(self): """ Private method to return a new empty line. """ if wx.Platform == '__WXMAC__' or wx.Platform == '__WXGTK__': return "\n" return "\r\n" def __separator(self): """ Private method to return a separator line. """ text = "-----------------------------------------------------------------" text += self.__newline() return text